#include "StdAfx.h"
#include "mg/Tolerance.h"
#include "mg/Straight.h"
#include "topo/Loop.h"
#include "Tl2/TL2parameter.h"
#include "Tl2/TL2Triangles.h"
#include "Tl2/TL2LPlines.h"

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

/****************************************************************/
/*   Copyright (c) 2018 by System fugen G.K.                    */
/*                       All rights reserved.                   */
/****************************************************************/

mgTL2LPlines::mgTL2LPlines(
	mgTL2Triangles& triangles	//mgTL2Triangles for the tessellated triangles to output.
):m_triangles(triangles),m_nlines(-1),m_npoints(-1),
m_minConcav(-1), m_maxConcav(-1){	
	for(int i=0; i<MAX_LINE_NUM; i++)
		m_concavity[i]=NotObtainedConcav;//the value that indicates not obtained yet.
}

///Construct from outer loop that has less than or equal to 5 edges.
///The outer boundary loop of the face must have edges whose number is up to 5.
mgTL2LPlines::mgTL2LPlines(
	const MGLoop& oloop,
	mgTL2Triangles& triangles	//mgTL2Triangles for the tessellated triangles to output.
):m_triangles(triangles),m_nlines(-1), m_npoints(-1),
m_minConcav(-1), m_maxConcav(-1){	
	MGComplex::const_iterator ei=oloop.pcell_begin();
	int nedges=oloop.number_of_edges();	assert(nedges<=5);

	for(int i=0; i<nedges; ei++, i++){
		const mgTL2Polyline* pli=TL2Polyline(ei);
		int nVi=pli->number_of_points();
		m_plines[i]=mgTL2LPline(pli,0,nVi);
		m_concavity[i]=NotObtainedConcav;//the value that indicates not obtained yet.
	}
}

///Do perform the tessellation for a concave rectangle.
void mgTL2LPlines::tessellateConcave(
	int concaveID	//vertiex id at which vertex concavity is detected.
)const{
	mgTLEdgePoint from(concaveID);
	mgTL2LPlines LPlines1(m_triangles), LPlines2(m_triangles);
	std::unique_ptr<mgTL2Polyline> bridge;
	int nlines=getNumberOfLines();
	if(numberOfPoints()==6){
		mgTLEdgePoint to=increment(from,3);
		int& PidTo=to.m_pointID;
		int& eidTo=to.m_edgeID;
		if(PidTo==0){
			if(eidTo==(concaveID+1)%nlines)
				PidTo++;
			else{
				int eidPre=(concaveID+nlines-1)%nlines;
				if(eidTo==eidPre){
					eidTo=(eidPre+nlines-1)%nlines;;
					PidTo=numberOfPointsLine(eidTo)-2;
				}
			}
		}
		subdivideFromTo(from,to,LPlines1,LPlines2,bridge);
	}else
		bridge=	subdivideAtPoint(from,LPlines1,LPlines2);
	LPlines1.tessellate4();
	LPlines2.tessellate4();
}

///Do perform the tessellation for a sharp vertex rectangle.
void mgTL2LPlines::tessellateSharp(
	int sharpID	//vertiex id at which sharpness is detected.
)const{
	mgTL2LPlines LPlines1(m_triangles),LPlines2(m_triangles);
	std::unique_ptr<mgTL2Polyline> bridge;

	int nlines=getNumberOfLines();//assert(nlines>=3);
	int eidOpo=(sharpID+2)%nlines, eidPre=(sharpID+nlines-1)%nlines,
		eidAft=(sharpID+1)%nlines;
	int nPre=numberOfPointsLine(eidPre);
	int nOpo=numberOfPointsLine(eidOpo);
	int nAft=numberOfPointsLine(eidAft);
	int n=m_plines[sharpID].number_of_points();

	mgTLEdgePoint from, to;//tested if undefined.
	if(nlines==4){
		if(isConcaveEdge(sharpID)){
			if(nOpo>=3)
				from=mgTLEdgePoint(eidOpo,nOpo-2);
		}else if(isConcaveEdge(eidPre)){
			if(nAft>=3)
				from=mgTLEdgePoint(eidAft,1);
		}
	}
	if(from.undefined() && nlines>=4){
		if(isLooselyFlatOrConcaveVertex(eidAft))
			from=mgTLEdgePoint(eidAft,0);
		else if(isLooselyFlatOrConcaveVertex(eidPre))
			from=mgTLEdgePoint(eidPre,0);
	}
	if(from.undefined() && (nlines<=4 || nPre==2 || n==2)){
		//|| nPre==2 || n==2 is the condition that does
		//not increment the edge number of this when nlines=5.
		from=mgTLEdgePoint(sharpID,1);
		int& eidFrom=from.m_edgeID;
		int& pidFrom=from.m_pointID;
		if(n==2){
			eidFrom=eidAft; pidFrom=0;
		}
		to=mgTLEdgePoint(eidPre,nPre-2);
	}
	if(from.undefined()){
		from=mgTLEdgePoint(eidPre,0);
		if(isMoreOpen(eidAft,eidPre))//If eidAft is opener.
			from.m_edgeID=eidAft;
	}
	if(to.undefined())
		bridge=subdivideAtPoint(from,LPlines1,LPlines2);
	else
		subdivideFromTo(from,to,LPlines1,LPlines2,bridge);
	LPlines1.tessellate4();
	LPlines2.tessellate4();
}

///Tessellate a rectangle that has 2 continuous edges of only 2 points.
void mgTL2LPlines::tessellate22mn(
	int eidTwo,
		//id of edge that has 2 vertices. Next edge of eidTwo also have only 2 vertices.
	int concaveVid//The most concave vid which is obtained by analyzeConcavity.
)const{
	int eidAft=(eidTwo+1)%4;
	int eidPre=(eidTwo+3)%4;
	if(isLooselyFlatOrConcaveVertex(eidPre)){
		makeFanAllPoints(mgTLEdgePoint(eidAft,0));
		return;
	}

	mgTL2LPlines LPlines1(m_triangles), LPlines2(m_triangles);
	mgTLEdgePoint from(concaveVid,0);
	std::unique_ptr<mgTL2Polyline> bridge//mgTL2Polyline that subdivide this.
		=subdivideAtPoint(from,LPlines1,LPlines2);
	LPlines1.tessellate4();
	LPlines2.tessellate4();
}

///Do perform the tessellation for a rectangle whose points num is(2,m,2,n).
///Here m, n are greater or equal to 3.
///Tessellated triangles are appended onto m_triangles
void mgTL2LPlines::tessellate2m2n(
	int eidMax//edge id whose point number is maximum.
)const{
	const mgTL2LPline& pl=m_plines[eidMax];
	int np=pl.number_of_points();assert(np>=3);
	
	int eidOpo=(eidMax+2)%4;;
	const mgTL2LPline& plOpo=m_plines[eidOpo];
	int nOpo=plOpo.number_of_points();assert(nOpo<=np);
	int nhalf=nOpo/2;
	mgTLEdgePoint from(eidOpo,nhalf);
	mgTLEdgePoint to=getToPointToSubdivide(from);

	int& eidTo=to.m_edgeID;
	int& PidTo=to.m_pointID;
	const mgTL2LPline& plTo=m_plines[eidTo];
	auto bridge=plOpo.polygonizeSL(plTo,nhalf,PidTo);
	if(bridge->number_of_points()==2 && np-nOpo<=1)
		makeStrip(eidMax);
	else{
		mgTL2LPlines LPlines1(m_triangles), LPlines2(m_triangles);
		subdivideFromTo(from,to,LPlines1, LPlines2,bridge);
		LPlines1.tessellate4();
		LPlines2.tessellate4();
	}
}

///Make a fan from a rectangle that has 4 edges, whose number of vertices are (2,2,2,n).
///Here n>=3. The fan is pushed back to m_triangles.
void mgTL2LPlines::tessellate222n(
	int eidMax//edge id whose point number is more than 2.
)const{
	if(!makeFanIfFlatEdge(eidMax)){
		const mgTL2LPline& pl=m_plines[eidMax];
		int n=pl.number_of_points();
		int eidOpo=(eidMax+2)%4;
		const mgTL2LPline& plOpo=m_plines[eidOpo];
		MGPosition Ps=plOpo.uv(0), Pe=plOpo.uv(1);

		MGPosition uviPre=pl.uv(0);
		int i=1, nm1=n-1;
		for(; i<nm1; i++){
			const MGPosition& uvi=pl.uv(i);
			MGVector Vi=uvi-uviPre;
			MGVector Vsi=Ps-uvi;
			MGVector Vei=Pe-uvi;
			double sangS=Vi.sangleSigned2D(Vsi);
			double sangE=Vi.sangleSigned2D(Vei);
			if(sangS>=sangE)//When pe is more away from uvi.
				break;
			uviPre=uvi;
		}
		int nHalf1=i;
		int nHalf2=n-nHalf1+1;
		mgTL2LPline pl1(pl,0,nHalf1);
		mgTL2LPline pl2(pl,nHalf1-1,nHalf2);
		if(nHalf1>=2)
			m_triangles.makeFan(pl1,plOpo,false,true);
		m_triangles.makeFan(pl2,plOpo,true);
	}
}

///Do perform the tessellation for a rectangle whose points num is(2,m,n),
///that is a rectangel of 3 edges.
///Here m, n are greater than or equal to 3.
///Tessellated triangles are appended onto m_triangles
void mgTL2LPlines::tessellate2mn(
	int eidTwo	//Edge id whose point number is 2.
)const{
	int eidAft=(eidTwo+1)%3;
	int eidPre=(eidTwo+2)%3;
	int nAft=numberOfPointsLine(eidAft);
	int nPre=numberOfPointsLine(eidPre);
	mgTLEdgePoint from(eidAft,nAft-2), to(eidPre,1);
	mgTL2LPlines LPlines1(m_triangles), LPlines2(m_triangles);
	std::unique_ptr<mgTL2Polyline> bridge;
	subdivideFromTo(from, to, LPlines1, LPlines2, bridge);
	LPlines1.tessellate4();
	LPlines2.tessellate4();
}

///Do perform the tessellation for the mgTL2Plygon that has 4 edges as the boundary.
///The result will be appended onto triangles.
///When triangles.is_uv()=false, all of the element of the triangle position data
///has normal data as (x,y,z,xn,yn,zn). Here (x,y,z) is the position data and
///(xn,yn,zn) is the normal vector at the position (x,y,z).
///When triangles.is_uv()=true, all of the element of the triange position data are (u,v).
void mgTL2LPlines::tessellate4()const{
	int nlines=getNumberOfLines();
	if(nlines==0)
		return;

	//MGBox uvb=getUvBox();std::cout<<uvb<<std::endl;///////////***************
	//if(-1.46<uvb[0]&&uvb[0]<0.15 && .54<=uvb[1]&&uvb[1]<18.4){
	//	std::cout<<uvb<<std::endl;///////////***************
	//	std::cout<<m_plines[0]<<m_plines[1]<<m_plines[2]<<m_plines[3]<<std::endl;//////****
	//}

	int eids[5];
	analyzePointNumver(eids);
	int &nMin=eids[1], &eidMin=eids[2];//minimum number of vertices and the edge id will be stored.
	int &nMax=eids[3], &eidMax=eids[4];//Maximum vertex number and the id of m_plines[i] will be stored.
	int& numEdgeTwoPoint=eids[0];//number of edges whose points are only 2.
	if(nlines==2 && numEdgeTwoPoint){
		m_triangles.makeFan(m_plines[eidMax],m_plines[eidMin]);
		return;
	}

	int nPoints=numberOfPoints();
	switch(nPoints){
		case 3: makeFan3Points(); return;
		case 4:	makeFan4Points(eids); return;
		case 5:	makeFan5Points(eids); return;
		default:;
	}

	if(nlines==3){
		if(numEdgeTwoPoint==2){
			m_triangles.makeFan(m_plines[eidMax],m_plines[(eidMax+2)%3]);
			return;
		}else if(numEdgeTwoPoint==1){
			tessellate2mn(eidMin);	
			return;
		}
	}

	int concaveVID, convexVID;
	SHARPorCONCAVE sharpOrConcave=analyzeConcavity(concaveVID,convexVID);
	if(sharpOrConcave==CONCAVE){
		tessellateConcave(concaveVID);
		return;
	}
	if(nlines==4){
		if(numEdgeTwoPoint==3){//Case that 3 of the edges have only 2 vertices.
			tessellate222n(eidMax);
			return;
		}else if(numEdgeTwoPoint==2){
			int eid2Next=(eidMin+1)%4;
			if(numberOfPointsLine(eid2Next)==2)
				if(sharpOrConcave==SHARP)
					tessellateSharp(convexVID);
				else
					tessellate22mn(eidMin, concaveVID);
			else
				//the case of (2,m,2,n)
				tessellate2m2n(eidMax);		
			return;
		}
	}

	if(sharpOrConcave==SHARP){
		if(nlines!=4 || numEdgeTwoPoint==0){
			tessellateSharp(convexVID);
			return;
		}
	}

	mgTLEdgePoint from=getFromPointToSubdivide(eids);
	mgTL2LPlines LPlines1(m_triangles), LPlines2(m_triangles);
	std::unique_ptr<mgTL2Polyline> bridge//mgTL2Polyline that subdivide this.
		=subdivideAtPoint(from,LPlines1,LPlines2);
	//std::cout<<LPlines1<<std::endl;
	LPlines1.tessellate4();
	LPlines2.tessellate4();
}

///Do perform the tessellation for the mgTL2Plygon that has 3 or 4 edges outer loop,
///and that has no inner loops.
///The result will be appended onto triangles.
///When triangles.is_uv()=false, all of the element of the triangle position data
///has normal data as (x,y,z,xn,yn,zn). Here (x,y,z) is the position data and
///(xn,yn,zn) is the normal vector at the position (x,y,z).
///When triangles.is_uv()=true, all of the element of the triange position data are (u,v).
void mgTL2LPlines::tessellate(){
	mgTLEdgePoint from(0,0);
	int nedges=getNumberOfLines();assert(nedges>=1 && nedges<=4);
	if(nedges==1){
		mgTL2LPlines LPlines1(m_triangles), LPlines2(m_triangles);
		std::unique_ptr<mgTL2Polyline> midLine=
			subdivideAtPoint(from,LPlines1,LPlines2);
		LPlines1.tessellate4();
		LPlines2.tessellate4();
	}else{
		//Check concavity of all edges.
		//Concavity is tested for all edge's start and end point tangent difference.
		//(Not concavity at a vertex)
		int eid=0;
		for(eid; eid<nedges; eid++){
			if(numberOfPointsLine(eid)==2)
				continue;
			if(isConcaveEdge(eid))
				break;
		}
		if(eid<nedges){//When a concave edge found.
			int eidOpo=(eid+2)%nedges;
			if(numberOfPointsLine(eidOpo)>=3){
				int& eidFrom=from.m_edgeID;
				eidFrom=eidOpo;
				from.m_pointID=numberOfPointsLine(eidFrom)/2;
				mgTL2LPlines LPlines1(m_triangles), LPlines2(m_triangles);
				std::unique_ptr<mgTL2Polyline> bridge//mgTL2Polyline that subdivide this.
					=subdivideAtPoint(from,LPlines1,LPlines2);
				LPlines1.tessellate4();
				LPlines2.tessellate4();
				return;
			}
		}
		tessellate4();
	}
}
