#include "MGCLStdAfx.h"
#include "mg/Position.h"
#include "Tl2/TL2LPline.h"
#include "Tl2/TL2Triangles.h"
#include "Tl2/TL2Fans.h"

#if defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//Constructor.
mgTL2Triangles::mgTL2Triangles(
	MGCL::TL_DATA_KIND dkind,
	const MGSurface* surf
):m_kind(dkind),m_surface(surf),m_triangles(){
}

///Copy constructor.
mgTL2Triangles::mgTL2Triangles(
	const mgTL2Triangles& tris
):m_kind(tris.m_kind),m_surface(tris.m_surface){
	mgTL2Triangles& tris2=const_cast<mgTL2Triangles&>(tris);
	m_triangles.push_back(tris2.m_triangles);
}

//Return the i-th mgTL2Triangle.
const mgTL2Triangle& mgTL2Triangles::operator[](int i)const{
	return *(m_triangles[i]);
}
mgTL2Triangle& mgTL2Triangles::operator[](int i){
	return *(m_triangles[i]);
}

static const std::string kind_string[3]={"(u,v)", "(x,y,z)", "(x,y,z) with normal"};
std::ostream& operator<< (std::ostream& out, const mgTL2Triangles& tris){
	out<<"TLTriangles="<<(&tris)<<", surface="<<"tris.m_surface"<<",num of triangles="
		<<tris.m_triangles.size()<<", kind="<<kind_string[tris.get_kind()]<<std::endl;
	mgTL2Triangles::const_iterator iter = tris.begin();
	for(; iter != tris.end(); iter++)
		out<<**iter;
	out<<std::endl;
	return out;
}

///Push back a triangel of type mgTESTRIANG_FAN.
///The veritces of the triangle is verticesIDs which are vertex id of fans.
///The vertex ids are converted to (x,y,x) or (u,v) according to triangles.is_uv().
void mgTL2Triangles::push_back(
	const mgTL2Fans& fans,
	int nvTri,//Number of vertices in verticesIDs. generally nvTri!=verticesIDs.size().
	const std::vector<int>& verticesIDs
){
	if(nvTri<=2)
		return;

	bool uv_is_required=is_uv();
	bool normal_is_required=need_normal();
	mgTL2Triangle* cfan=new mgTL2Triangle(nvTri,mgTESTRIANG_FAN);
	for(int i=0; i<nvTri; i++){
		int id=verticesIDs[i];
		if(uv_is_required)
			(*cfan)[i]=fans.uv(id);
		else
			(*cfan)[i]=fans.xyz(id,normal_is_required);
	}
	push_back(cfan);
}


///Make strip data from pline0 and pline2.
///The number of vertices of pline0 is greater or equal to the one of pline2.
///And the differecne must be at most 1.
void mgTL2Triangles::makeStrip(
	const mgTL2LPline& pline0,
	const mgTL2LPline& pline2
){
	bool normal_is_required=need_normal();
	int n0=pline0.number_of_points(), n2=pline2.number_of_points();
	assert(n0>=n2 && (n0-n2)<=1);
	int id2=0, n0m1=n0-1;
	if(n0==n2){
		if(n0==2){
			tessellate2by2(pline0,pline2);
			return;
		}
		MGVector dire1=pline0.eval(1.,1);
		MGPosition uvn0m1=pline0.uv(n0m1);
		MGPosition uv0=pline2.uv(0);
		MGVector dire2=uv0-uvn0m1;
		if(dire1.cangle(dire2)>RIGHT_ANGLE){
			id2=1;n2--;
			mgTL2Triangle* stripP0=new mgTL2Triangle(3,mgTESTRIANG_STRIP);
			mgTL2Triangle& strip0=*stripP0;
			if(is_uv()){
				strip0[0]=uvn0m1;
				strip0[1]=uv0;
				strip0[2]=pline2.uv(1);
			}else{
				strip0[0]=pline0.xyz(n0m1,normal_is_required);
				strip0[1]=pline2.xyz(0,normal_is_required);
				strip0[2]=pline2.xyz(1,normal_is_required);
			}
			push_back(stripP0);//std::cout<<strip0<<std::endl;
		}
	}

	mgTL2Triangle* stripP=new mgTL2Triangle(n0+n2,mgTESTRIANG_STRIP);
	mgTL2Triangle& strip=*stripP;
	int j=0;
	if(is_uv()){
		for(int i=0; i<n2; i++){
			strip[j++]=pline0.uv(n0m1-i);
			strip[j++]=pline2.uv(id2+i);
		}
		if(n0>n2)
			strip[j]=pline0.uv(0);
	}else{
		for(int i=0; i<n2; i++){
			strip[j++]=pline0.xyz(n0m1-i,normal_is_required);
			strip[j++]=pline2.xyz(id2+i,normal_is_required);
		}
		if(n0>n2)
			strip[j]=pline0.xyz(0,normal_is_required);
	}
	push_back(stripP);//std::cout<<strip<<std::endl;
}

///Make fan data from plinen and pline0.
///The number of vertices of plinen is 2 or greater than 2.
///The fan is made from the pline0's start point to n vertices of plinen.
void mgTL2Triangles::makeFan(
	const mgTL2LPline& plinen,
	const mgTL2LPline& pline0
){
	int nlpn=plinen.number_of_points();
	mgTL2Triangle* fanP=new mgTL2Triangle(nlpn+1,mgTESTRIANG_FAN);
	mgTL2Triangle& fan=*fanP;
	if(is_uv()){
		fan[0]=pline0.uv(0);
		for(int i=0; i<nlpn; i++)
			fan[i+1]=plinen.uv(i);
	}else{
		bool normal_is_required=need_normal();
		fan[0]=pline0.xyz(0,normal_is_required);
		for(int i=0; i<nlpn; i++)
			fan[i+1]=plinen.xyz(i,normal_is_required);
	}
	push_back(fanP);
}

///Tessellate a rectangle that has only 4 vertices, and puch back the tris to this.
void mgTL2Triangles::tessellate2by2(
	const mgTL2LPline&  pl,
	const mgTL2LPline&  pl_opposite
){
	MGVector dir1=pl_opposite.xyz(0)-pl.xyz(0);
	MGVector dir2=pl_opposite.xyz(1)-pl.xyz(1);
	int idmax;
	if(dir1%dir1<=dir2%dir2)
		idmax=0;//pl.xyz(0) is pivot of the fan.
	else
		idmax=1;//pl.xyz(1) is pivot of the fan.

	mgTL2Triangle* fanP=new mgTL2Triangle(4,mgTESTRIANG_FAN);
	mgTL2Triangle& fan=*fanP;
	if(is_uv()){
		if(idmax){
			fan[0]=pl.uv(1);
			fan[1]=pl_opposite.uv(0);
			fan[2]=pl_opposite.uv(1);
			fan[3]=pl.uv(0);
		}else{
			fan[0]=pl.uv(0);
			fan[1]=pl.uv(1);
			fan[2]=pl_opposite.uv(0);
			fan[3]=pl_opposite.uv(1);
		}
	}else{
		bool normal_is_required=need_normal();
		if(idmax){
			fan[0]=pl.xyz(1,normal_is_required);
			fan[1]=pl_opposite.xyz(0,normal_is_required);
			fan[2]=pl_opposite.xyz(1,normal_is_required);
			fan[3]=pl.xyz(0,normal_is_required);
		}else{
			fan[0]=pl.xyz(0,normal_is_required);
			fan[1]=pl.xyz(1,normal_is_required);
			fan[2]=pl_opposite.xyz(0,normal_is_required);
			fan[3]=pl_opposite.xyz(1,normal_is_required);
		}
	}
	push_back(fanP);
}

///Tessellate a rectangle that has only 4 edges, whose number of vertices are (2,2,2,n).
///Here n>=3. Tessellated tris are pushed back to this.
void mgTL2Triangles::tessellate222n(
	const mgTL2LPline& pl3,//edge that has 3 or more than 3 vetices.
	const mgTL2LPline& pl2//opposite edge of pl3 whose vertices number is 2.
){
	//std::cout<<pl3<<pl2<<std::endl;

	bool uv_is_required=is_uv(), normal_is_required=need_normal();
	int n3=pl3.number_of_points();

	std::unique_ptr<mgTL2Triangle> fan1P(new mgTL2Triangle(mgTESTRIANG_FAN));
	mgTL2Triangle& fan1=*fan1P;

	std::unique_ptr<mgTL2Triangle> fan2P(new mgTL2Triangle(mgTESTRIANG_FAN));
	mgTL2Triangle& fan2=*fan2P;

	MGPosition pivot1=pl2.xyz(1), pivot2=pl2.xyz(0);
	fan1.push_back(uv_is_required ? pl2.uv(1):pl2.xyz(1,normal_is_required));//Pivot1
	fan1.push_back(uv_is_required ? pl3.uv(0):pl3.xyz(0,normal_is_required));//Edge Pivot1 to pl3 start.
	fan2.push_back(uv_is_required ? pl2.uv(0):pl2.xyz(0,normal_is_required));//Pivot2
	fan2.push_back(fan1[0]);//Edge Pivot2 to Pivot1.

	int i=1;
	MGPosition xyzim1=pl3.xyz(0),xyzi=pl3.xyz(1);
	MGVector pv1Toxyzi=xyzi-pivot1, pv2Toxyzim1=xyzim1-pivot2;
	double l1i=pv1Toxyzi%pv1Toxyzi, l2im1=pv2Toxyzim1%pv2Toxyzim1;
	while(i<n3 && l1i<=l2im1){
		fan1.push_back(uv_is_required ? pl3.uv(i):pl3.xyz(i,normal_is_required));
		i++;
		if(i<n3){
			xyzim1=xyzi;
			xyzi=pl3.xyz(i);
			pv1Toxyzi=xyzi-pivot1; pv2Toxyzim1=xyzim1-pivot2;
			l1i=pv1Toxyzi%pv1Toxyzi; l2im1=pv2Toxyzim1%pv2Toxyzim1;
		}
	}
	if(i==n3)
		fan1.push_back(fan2[0]);
	else
		fan2.push_back(uv_is_required ? pl3.uv(i-1):pl3.xyz(i-1,normal_is_required)); 
	
	for(;i<n3; i++){
		fan2.push_back(uv_is_required ? pl3.uv(i):pl3.xyz(i,normal_is_required)); 
	}

	if(fan1.size()>=3){
		//std::cout<<fan1<<std::endl;
		push_back(fan1P.release());
	}
	if(fan2.size()>=3){
		//std::cout<<fan2<<std::endl;
		push_back(fan2P.release());
	}
}

///Tessellate a rectangle that has 2 continuous edges of only 2 vertices.
///Tessellated triangles will be appended.
void mgTL2Triangles::tessellate22nn(
	int id2,	//id of edge that has 2 vertices. Next edge of id2 also have only 2 vertices.
	const mgTL2LPline pline[4]
){
	int id2next=(id2+1)%4;
	int id2next2=(id2+2)%4;
	int id2pre=(id2+3)%4;
	const mgTL2LPline& lp0=pline[id2];//lp0.number_of_points()==2
	const mgTL2LPline& lp1=pline[id2next];//lp1.number_of_points()==2
	const mgTL2LPline& lp2=pline[id2next2];
	const mgTL2LPline& lp3=pline[id2pre];

	int n2=lp2.number_of_points();
	int n3=lp3.number_of_points();

	MGVector xyz2S=lp2.xyz(0)-lp2.xyz(1);
	MGVector xyz3E=lp3.xyz(n3-1)-lp3.xyz(n3-2);
	if(xyz3E%xyz3E > xyz2S%xyz2S){
		std::unique_ptr<mgTL2Polyline> poly=lp1.polygonizeSL(lp3,0,n3-2);
		mgTL2LPline lpn(poly.get());
		makeFan(lpn,lp0);
		mgTL2LPline lp3Sub(lp3,0,n3-1);
		lpn.reverse();
		mgTL2LPline plines2[4]={lpn,lp1,lp2,lp3Sub};
		tessellate4(plines2);
	}else{
		std::unique_ptr<mgTL2Polyline> poly=lp2.polygonizeSL(lp1,1,0);
		mgTL2LPline lpn(poly.get());
		makeFan(lpn,lp2);
		mgTL2LPline lp2Sub(lp2,1,n2-1);
		lpn.reverse();
		mgTL2LPline plines2[4]={lpn,lp2Sub,lp3,lp0};
		tessellate4(plines2);
	}
}