#include "StdAfx.h"
#include "Tl2/TL2LPline.h"

class MGLoop;
class mgTL2Triangles;
class mgTL2Polyline;

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

//Utility class for mgTL2LPlines to indicate a point on a edge of a mgTL2LPlines.
class mgTLEdgePoint{
public:
	int m_edgeID; //Edge id 
	int m_pointID;//Point id in m_EdgeID

public:
mgTLEdgePoint():m_edgeID(-1), m_pointID(0){;};//-1 means undefined.
mgTLEdgePoint(int edgeID, int pointID=0):m_edgeID(edgeID), m_pointID(pointID){;};
bool operator==(const mgTLEdgePoint& epid)const{
	return m_edgeID==epid.m_edgeID && m_pointID==epid.m_pointID;};
bool undefined()const{return m_edgeID==-1;};
};

//Utility class for mgTL2LPlines, to analyze concavity at vertices of mgTL2LPlines.
class mgTLConcavPid{
public:
	double concavity;//concavity at the point epID, see m_concavity of mgTL2LPlines.
	mgTLEdgePoint epID;

mgTLConcavPid(double cnocav=3., int eid=0, int pid=0)
	:concavity(cnocav),epID(eid,pid){;};
bool operator<(const mgTLConcavPid& cpid2)const{return concavity<cpid2.concavity;};

//Compare this with the input 3 mgTLConcavPid's, then if this is min or max, replace.
void getMinMax(
	mgTLConcavPid& maxConcav,//most concave point.
	mgTLConcavPid& max2Concav,//2ndly most concave point.
	mgTLConcavPid& maxConvex//most convex point.
);

};

/// class of a vector of mgTL2LPline.
// To tessellate, construct this, then invoke tessellate.
class mgTL2LPlines{

#define MAX_LINE_NUM 5
enum SHARPorCONCAVE {
	CONCAVE=-1,
	SHARP=1,
	OTHER=0
};
#define NotObtainedConcav 3.

private:
	mgTL2LPline m_plines[MAX_LINE_NUM];
	mgTL2Triangles& m_triangles;//mgTL2Triangles for the tessellated triangles to output.
	mutable short m_nlines;//Initially -1, then turned to side num.
	mutable short m_npoints;//Initially -1, then turned to num of point number.
	mutable short m_minConcav, m_maxConcav;//minimum conavity vertex id , and mzximum id.
		//When =-1, not obtained yet.
	mutable double m_concavity[MAX_LINE_NUM];//concavity at the vertex i in [i].
		//vertex i is the start point of the edge m_plines[i].
		//Concavity is:
		//when concave, 1 minus cangle, and when convex cangle minus 1.
		//cangle is cosine angle around Z-axis of (U,V) 2D coordinates plane
		//from pre-line's vector to aft-line's vector at the vertx i.
		//m_concavity's value is from -2 to 2.
		//2 is most concave(open), and -2 is most convex(closed).
		// -2 means 180 degree convex, -1:90 degree convex, 0:flat
		// 1:90 degree concave, 2:180 degree concave.
mgTL2LPlines();//Not alowed to use.

public:
//////////// constructor ///////////////

//default constructor.
//mgTL2LPlines()=delete;

mgTL2LPlines(
	mgTL2Triangles& triangles//mgTL2Triangles for the tessellated triangles to output.
);

///Construct from an outer loop that has less than or equal to MAX_LINE_NUM edges.
mgTL2LPlines(
	const MGLoop& oloop,
		//Outer boundary loop of the face of mgTL2Plygon
		//to tessellate whose number of edges is MAX_LINE_NUM at most.
	mgTL2Triangles& triangles	//mgTL2Triangles for the tessellated triangles to output.
);

const mgTL2LPline& operator[](int i)const{return m_plines[i];};
mgTL2LPline& operator[](int i){return m_plines[i];};

///Analyze the numbers of this, getting the number of 2 point edge, maximum and
///minimum point number edges.
void analyzePointNumver(
	int eids[5]
		//Following data are output:
		//eids[0]:num of 2 point edges,
		//eids[1], [2];//minimum number of vertices and the edge id
		//eids[3], [4];//Maximum vertex number and the edge id
)const;

///Check the concavities of all the vertices, concavity or sharp angle.
///Function's return value is SHARPorCONCAVE:
//       CONCAVE if concavity found.
///      SHARP if concavity not found, and sharp angle found.
///      OTHER if both concavity ro sharp angle not found.
///concaveVID and convexVid are always returned regardless of functon's return value.
SHARPorCONCAVE analyzeConcavity(
	int& concaveVID,//most concave vertex id returned,
	int& convexVid	//most convex vertex id returned.
)const;

//Analyse concavity of this polygon,
//not only the vertices but the intermediate points also .
//Function's return value is the number of concave points.
int analyzeConcavityAllPoints(
	mgTLConcavPid& pidMostConcave,//most concave concavity and the point.
	mgTLConcavPid& pid2ndConcave,//2ndly most concave concavity and the point
	mgTLConcavPid& pidMostConvex//most convex concavity and the point
)const;

//Nullify this.
void setNull();

//Obtain how many m_plines[] are effective.
int getNumberOfLines()const{
	return m_nlines<0 ? getNumberOfLinesSub():m_nlines;
};

//Obtain how many points in this closed polygon..
int numberOfPoints()const{
	return m_npoints<0 ? getNumberOfPointsSub():m_npoints;
};

//Obtain how many points in m_plines[i].
int numberOfPointsLine(int i)const{return m_plines[i].number_of_points();};

//Convert point id to epid(line id, id in the line).
//Here, point id is the point number from the start point of m_plines[0] to
//the point before the end point of the last of m_plines[]. 
void convertPointIDToLinePointID(int pid, mgTLEdgePoint& epid)const;

//Normalize the point id epid(eid, Pid), i.e. 
//if is the end point id, change it to the start point id of the next side.
void normalizePointID(mgTLEdgePoint& epid)const;

//Compare vid1's and vid2's openess(concavity),
//then if vid1's is more open, return true.
bool isMoreOpen(int vid1, int vid2)const;
bool isMoreClosed(int vid1, int vid2)const{return isMoreOpen(vid2,vid1);};

bool isUndefinedConcav(int vid)const{
	return m_concavity[vid]>=2.5;};//>=NotObtainedConcav?

//Test if the edge eid is concave or not.
bool isConcaveEdge(int eid)const;

//Test if the vertex vid is loosely flat(true), or not.
bool isLooselyFlatOrConcaveVertex(int vid)const;

//Get the most open vetex id.
int getMostOpenVid()const{return getMaximumConcavID();};

//Return true if epid is the next point of epid2 around this closed polygon.
bool isNextPoint(const mgTLEdgePoint& epid, const mgTLEdgePoint& epid2)const;

//Return true if epid is the previous point of epid2 around this closed polygon.
bool isPrePoint(const mgTLEdgePoint& epid, const mgTLEdgePoint& epid2)const;

//Decrement (eid, Pid) for (eid2, Pid2) to point at the previous num-th point around this.
mgTLEdgePoint decrement(
	const mgTLEdgePoint& epid,//input mgTLEdgePoint to decrement.
	int num=1
)const;

//Increment (eid, Pid) for (eid2, Pid2) to point at the num-th point around this.
mgTLEdgePoint increment(
	const mgTLEdgePoint& epid,//input the pivot point.
	int num=1
)const;

///get the (u,v) parameter box.
MGBox getUvBox()const;

//Evaluate all the points into uv[], then
//evaluate all the point uv dirrerence data into dir[].
//Function's return value is the number of points evaluated.
int evalUVVec(
	MGPosition uv[],//(u.v) data
	MGVector dir[]//uv[i+1]-uv[i] vectors are output in [i].
)const;

///Get concavity of an edge eid, which is not at a vertex.
///That is, concavity is obtained from the differece of two vectors,
///at the start and at the end point point tangent of the same edge eid.
//Concavity's value is from -2 to 2. 2 is most concave, and
// -2 means 180 degree convex(most convex), -1:90 degree convex, 0:flat
// 1:90 degree concave, 2:180 degree concave.
double getEdgeConcavity(int eid)const{return m_plines[eid].getConcavity();};

///Get concavity at the vertex vid, which is not an edge's concavity.
///That is, the concavity is obtained from the difference of two vectors
///at the edge id vid's start point and at the previous edge's end point.
//Concavity's value is from -2 to 2. 2 is most concave, and
// -2 means 180 degree convex(most convex), -1:90 degree convex, 0:flat
// 1:90 degree concave, 2:180 degree concave.
double getVertexConcavity(int vid)const;

//Get the vertx id of minimum concavity(most convex, or most closed).
int getMinimumConcavID()const;

//Get the vertx id of maximum concavity(most concave, or most open).
int getMaximumConcavID()const;

//Obtain from point to subdivide.
//When nlines is 2 or 3, there are no 2 point edges.
//When nlinesis 4, 2point edge number is at most 1.
mgTLEdgePoint getFromPointToSubdivide(
	const int eids[5]
		//Following data are input(that are obtained by analyzePointNumber):
		//eids[0]:num of 2 point edges,
		//eids[1], [2];//minimum number of vertices and the edge id
		//eids[3], [4];//Maximum vertex number and the edge id
)const; 

//Get subdivide to-point, providing from point.
mgTLEdgePoint getToPointToSubdivide(
	const mgTLEdgePoint& from
	//indicates the edge and the point id where to subdivide,
)const;

//Print out this.
std::ostream& toString(std::ostream& ostrm)const;

//Subdivide at a from Vid.
//Function's return value is mgTL2Polyline that subdivides this,
//which must exist until the tessellation is done.
std::unique_ptr<mgTL2Polyline> subdivideAtPoint(
	const mgTLEdgePoint& from,//Indicates the edge and the point id where to subdivide
	mgTL2LPlines& LPlines1,	//1st subdivided rectangle, that is the after part
		//of from. The 1st edge is from edge(or a part of it).
	mgTL2LPlines& LPlines2	//2nd subdivided rectangle, that is the previous part of from.
		//The 1st edge is from(if from.pid>0), or subdividing bridge(if from.pid==0).
)const;

///Let lpFrom=m_pline[from.eid], lpTo=m_pline[to.eid], then
///Subdivide this rectangle from the point from.pid of lpFrom
///to the point to.pid of lpTo.
///Then generate the two subdivided rectangles LPlines1 and LPlines2.
///When nlines =1, PidFrom or PidTo must be 0.
void subdivideFromTo(
	const mgTLEdgePoint& from,//id of edge and point that indicates which edge be subdivided.
	const mgTLEdgePoint& to,//id of edge and point that indicates which edge be subdivided.
	mgTL2LPlines& LPlines1,//1st subdivided rectangle, that is the after part
		//of PidFrom. The 1st edge is eidFrom(or a part of it).
	mgTL2LPlines& LPlines2,//2nd subdivided rectangle, that is the previous part of PidFrom.
		//The 1st edge is eidFrom(if Pidfrom>0), or subdividing bridge(if PidFrom==0).
	std::unique_ptr<mgTL2Polyline>& bridge //input or output bridge. When bridge is null,
		//subdivideFromTo makes it to output.
)const;

///Make strip data from pline0(=m_plines[eidMax]) and pline2(=m_plines[eidOpo]).
///The number of vertices of pline0 is greater or equal to the one of pline2,
///and the differecne must be at most 1.
///Neighbor edges of pline0 must be 2 points edges.
void makeStrip(
	int eidMax//edge of the maximum number of points.
)const;

///Make a fan of 1 triangle data from the pivot and the start point from.
///The number of points except pivot is numPoints.
void makeFan(
	const mgTLEdgePoint& pivot,//input pivot point
	const mgTLEdgePoint& from,//input the start point of the fan
	int numpoints//The number of points except pivot.
)const;

//Evaluate all the points of this polygon from the pivot(lineID,pivot)
//to build a fan, which is pushed back to m_triangles.
void makeFanAllPoints(
	const mgTLEdgePoint& pivot//input the pivot point.
)const;

///Make a fan of 1 triangle data. This point number is 3.
void makeFan3Points()const;

///Make fans from 4 point rectangle.
///This must have just 4 points.
///When nlines=4, each edge has (2, 2, 2, 2) points.
///When nlines=3, each edge has (2, 2, 3) points.
///When nlines=2, each edge has (3, 3), (2, 4) points.
void makeFan4Points(
	const int eids[5] //input the output of analyzePointNumver
				//(e.g. edge number whose point number is maximum).
)const;

//Make triangles. This must have 5 edges. That is
//When nlines=2, each edge has (3, 4), (2, 5) points.
//When nlines=3, each edge has (2, 3, 3), (2,2,4) points.
//When nlines=4, each edge has (2, 2, 2, 3) points.
void makeFan5Points(
	const int eids[5] //input the output of analyzePointNumver
				//(e.g. edge number whose point number is maximum).
)const;

///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 tessellate2mn(
	int eidTwo	//Edge id whose point number is 2.
)const;

///Tessellate a rectangle that has 2 continuous edges of only 2 points.
void 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;

///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 tessellate2m2n(
	int eidMax	//Edge id whose points numuber is maximum.
)const;

///Make fan(s) 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 tessellate222n(
	int eidMax//edge id whose point number is more than 2.
)const;

///Do perform the tessellation for a concave rectangle.
void tessellateConcave(
	int concaveID	//vertiex id at which vertex concavity is detected.
)const;

///Do perform the tessellation for a sharp vertex rectangle.
void tessellateSharp(
	int sharpID	//vertiex id at which sharpness is detected.
)const;

///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 tessellate4()const;

///Do perform the tessellation of this mgTL2LPline m_pline[MAX_LINE_NUM];
///The result will be appended onto tris.
///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).
///Tessellated triangles will be appended.
void tessellate();

private:

//Obtain minimum and maximum concavity vertex id.
void computeMinMaxConcav()const;

//This is a proprietry func of getToPointToSubdivide,
//gets to-point to subdivide by isectSlTl.
//Functin's return value is true if obtained.
bool getToPointByIsect(
	const mgTLEdgePoint& from,//id of edge and point that indicates which edge be subdivided.
	mgTLEdgePoint& to//to point is output.
)const;

//Get the process code to update (eidTo, PidTo) data,
//since we avoid an end point and the same edge of from edge as much as possible,
//we use the neighbor inner point instead.
//Returned code is
// 0: no need to update (This is the normal case)
// 1:eidTo=eidNext; PidTo=1;
// 2:eidTo=eidNext; PidTo=nNext-2
// 3:eidTo=eidPre;  PidTo=nPre-2;
// 4:eidTo=eidOpo;  PidTo=nOpo>=3 ? 1:0;
// 5:if(nOpo>=3){eidTo=eidOpo;PidTo=nOpo-2;}else{eidTo=eidPre;PidTo=0;}
int getProcessCode(
	const mgTLEdgePoint& from,//id of edge and point that indicates which edge be subdivided.
	const mgTLEdgePoint& to,//id of edge and point that indicates which edge be subdivided.
	int& toKind //Point kind is returned.
)const;

//Obtain how many lines(m_plines[]) are effective.
int getNumberOfLinesSub()const;

//Obtain how many points are in this.
int getNumberOfPointsSub()const;

///Check flatness of the edge eid and at the both ends of eid.
///When flat, make a fan putting the next or previous point as the pivot.
///Functions's return value is true if made.
bool makeFanIfFlatEdge(
	int eid//edge to test the flatness.
)const;

};

///Debug Function
inline
std::ostream& operator<< (std::ostream& ostrm, const mgTL2LPlines& lines){
	lines.toString(ostrm);
	return ostrm;
};
