#include "MGCLStdAfx.h"
#include "mg/Tolerance.h"
#include "mg/Straight.h"
#include "mg/Surface.h"
#include "topo/Face.h"
#include "Tl2/TL2parameter.h"
#include "TL2/TL2LPline.h"
#include "TL2/TL2Polyline.h"

/****************************************************************/
/*   Copyright (c) 2017 by System fugen G.K.                */
/*                       All rights reserved.                   */
/****************************************************************/


///mgTL2LPline is a proprietry class for Face tessellation.
///mgTL2LPline is limitted subinterval of mgTL2Polyline,
///holds the starting id of mgTL2Polyline, and the number of vertices.

//	enum polyline_type{
//		WHOLE_INNER=0,	//whole points are free inner points.
//		START_BOUNDARY,	//only start point is connected to a boundary line.
//		END_BOUNDARY,	//only end point is connected to a boundary line.
//		START_END_BOUNDARY,//Only start and end points are connected to a boundary.
//						//Inner points except start and end are not on a boundary.
//		WHOLE_BOUNDARY	//All of the points are on a boundary.
//	};


//////////// constructor ///////////////

///Construct from subinterval of input lpline.
mgTL2LPline::mgTL2LPline(
	const mgTL2LPline& lpline,
	int idS, //starting id of lpline
	int nV
):m_polyline(lpline.m_polyline),m_idS(lpline.m_idS),m_nV(lpline.m_nV){
	limit(idS,nV);
	assert(m_idS<m_polyline->bdim());
}

void mgTL2LPline::setNull(){
	m_polyline=0;
	m_idS=0;
	m_nV=0;
}


//Change m_polyline's id to this mgTL2LPline's id.
//Function's return value is true if id_in is valid id for this mgTL2LPline.
//False if not.
bool mgTL2LPline::change_id(
	int id_in,	//id of m_polyline.
	int& id_out	//id of this will be output.
)const{
	if(m_nV>0){
		if(id_in<m_idS)
			return false;
		id_out=id_in-m_idS;
		if(int(id_out)>=m_nV)
			return false;
	}else{
		if(id_in>m_idS)
			return false;
		id_out=m_idS-id_in;
		if(int(id_out)>=(-m_nV))
			return false;
	}
	return true;
}

///Construct new curve object by copying to newed area.
///User must delete this copied object by "delete".
mgTL2LPline* mgTL2LPline::clone()const{
	return new mgTL2LPline(*this);
}

//Evaluation of this with the normalized parameter value t from 0. to 1.
//is provided. t=0. for the start point, t=1. for the end point.
MGVector mgTL2LPline::eval(double t, int nderi)const{
	assert(0.<=t && t<=1.);
	const mgTL2Polyline& pl=*(TL2Polyline());
	const MGKnotVector& kv=pl.knot_vector();
	double ts=kv(m_idS+1), te;
	if(m_nV>0){
		te=kv(m_idS+m_nV);
	}else{
		te=kv(m_idS+2+m_nV);
	}
	MGVector result=pl.eval(ts+(te-ts)*t,nderi);
	if(m_nV<0 && nderi%2)
		result.negate();
	return result;
}

//Get id of m_Bpolylines of m_param of m_polyline from vertex id i.
//Function's return value is
//true if the point of t is a boundary point, false if not.
bool mgTL2LPline::get_id_from_VertexID(int i, short id[3])const{
	assert(i<number_of_points());
	if(m_nV>0)
		return m_polyline->get_id_from_VertexID(i+m_idS, id);
	return m_polyline->get_id_from_VertexID(m_idS-i, id);
}

//Get i-th point surface parameter (u,v) of this polyline.
//i is relative one that start from 0 even for opposite direction.
MGPosition mgTL2LPline::uv(int i)const{
	assert(i<number_of_points());
	if(m_nV>0)
		return m_polyline->uv(i+m_idS);
	return m_polyline->uv(m_idS-i);
}

//Get i-th point(x,y,z,xn,yn,zn) of this polyline.
//Here (x,y,z) is the position data, and (xn,yn,zn) is the unit normal at (x,y,z).
//i is relative one that start from 0 even for opposite direction.
MGPosition mgTL2LPline::xyz(int i, bool need_normal)const{
	assert(i<number_of_points());
	if(m_nV>0)
		return m_polyline->xyz(i+m_idS,need_normal);
	return m_polyline->xyz(m_idS-i,need_normal);
}

//Update this by limiting the parameter range of the curve.
///Limitting is done at the knot parameter for both start and end.
mgTL2LPline& mgTL2LPline::limit(
	int idS,	//start point id of this mgTL2LPline.
				//idS is relative one that starts from 0 even for opposite direction.
	int nV	//Number of vertices.
){
	assert(nV<=number_of_points());
	if(m_nV>0){
		assert(idS<int(m_nV) && idS+nV<=int(m_nV));
		m_idS+=idS;
		m_nV=nV;
	}else{
		assert(int(idS)<-m_nV && int(idS+nV)<=-m_nV);
		m_idS-=idS;
		m_nV=-int(nV);
	}
	assert(m_idS<m_polyline->bdim());
	return *this;
}

//Obtain the mid point of this line.
void mgTL2LPline::mid(MGPosition& uvmid){
	int nmid=number_of_points()/2;
	uvmid=uv(nmid);
}

//Get the number of points of this polyline.
int mgTL2LPline::number_of_points()const{
	if(m_nV>0)
		return m_nV;
	return -m_nV;
}

//Reverse the direction.
void mgTL2LPline::reverse(){
	if(m_nV>0){
		m_idS+=m_nV-1;
		m_nV=-m_nV;
	}else{
		m_idS=int(m_idS+1+m_nV);
		m_nV=-m_nV;
	}
	assert(m_idS<m_polyline->bdim());
}

//Subdivide at the id.
void mgTL2LPline::subdivide(
	int id, //Relative one that start from 0 even for opposite direction.
	mgTL2LPline& lp1,	//devided 1st mgTL2LPline.
	mgTL2LPline& lp2	//devided 2nd mgTL2LPline.
)const{
	assert(m_nV>2 || m_nV<-2);
	lp1.m_polyline=lp2.m_polyline=m_polyline;
	lp1.m_idS=m_idS;
	if(m_nV>0){
		assert(int(id)<m_nV);
		lp1.m_nV=int(id+1);
		lp2.m_idS=m_idS+id;
		lp2.m_nV=m_nV-int(id);
	}else{
		assert(int(id)<(-m_nV));
		lp1.m_nV=-int(id+1);
		lp2.m_idS=m_idS-id;
		lp2.m_nV=m_nV+int(id);
	}
	assert(m_idS<m_polyline->bdim());
}

//Compute the intersections of sl and this mgTL2LPline.
//Function's return value is the (nearest) intersection vertex id of lp if >=0.
//If return value is minus, intersection not found.
int mgTL2LPline::isectSlTl(
	const MGStraight& sl
)const{
	const mgTL2parameter& tlparam=TL2param();
	const MGSurface& sufaceOrg=tlparam.get_surface();
	double serror=sufaceOrg.parameter_error();
	double errorSave=MGTolerance::set_wc_zero(serror);
	int id_lp=-1;

	const mgTL2Polyline* ipoly=TL2Polyline();
	MGCCisect_list tlist=ipoly->isect(sl);
	if(!tlist.isEmpty()){
		MGCCisect ti=tlist.front();	
		//change the parameter value to indicate a vetex point of the polygon.
		int id_poly=int(ti.param1()-ipoly->param_s()+.5);
		int id_lp2;
		if(change_id(id_poly,id_lp2))
			id_lp=id_lp2;
	}
	MGTolerance::set_wc_zero(errorSave);
	return id_lp;
}

//Polygonize of the (u,v) space straight line from this->uv(id1V) to pline2.uv(id2V).
//The direction of the output is from id1V to id2V.
//polygonizeSL does ensure the deviation from the surface to be within the surface
//tolerance.
std::unique_ptr<mgTL2Polyline> mgTL2LPline::polygonizeSL(
	const mgTL2LPline& pline2,
	int id1V,	//id of this vertex.
	int id2V	//id of pline2's vertex.
)const{
	MGPosition uvS=uv(id1V);
	MGPosition uvE=pline2.uv(id2V);
	MGStraight sl(uvE,uvS);
	std::unique_ptr<mgTL2Polyline> uvpolyline(new mgTL2Polyline(TL2param(),sl));

	short idS[3];
	if(get_id_from_VertexID(id1V,idS))
		uvpolyline->set_startID(idS);
	short idE[3];
	if(pline2.get_id_from_VertexID(id2V,idE))
		uvpolyline->set_endID(idE);
	return uvpolyline;
}

std::unique_ptr<mgTL2Polyline> mgTL2LPline::getPolygonizedMidLine(
	const mgTL2LPline& lp2,
	int& id0, int& id2
)const{
	id0=number_of_points()/2;
	MGPosition uvM=uv(id0);
	MGStraight slMid(uvM-uv(id0-1),uv(id0+1)-uvM,uvM);
	id2=lp2.isectSlTl(slMid);
	std::unique_ptr<mgTL2Polyline> mid;
	if(id2>0)
		mid=polygonizeSL(lp2,id0,id2);
	return mid;
}

//Interpolate pline1.uv(id1V) and pline2.uv(id2V)
//by a straight line for the tessellation.
//The direction of the output is from id1V to id2V.
//interpolateSL does not ensure the deviation from the surface.
std::unique_ptr<mgTL2Polyline> interpolateSL(
	const mgTL2LPline& pline1,
	const mgTL2LPline& pline2,
	int id1V,	//id of pline[id1]'s vertex.
	int id2V	//id of pline[id3]'s vertex.
){
	std::unique_ptr<mgTL2Polyline> uvpolyline
		=std::unique_ptr<mgTL2Polyline>(new mgTL2Polyline(pline1.TL2param()));

	int nbd=2;
	MGBPointSeq& uvbp=uvpolyline->line_bcoef();
	uvbp.resize(nbd,2);
	MGPosition uvS=pline1.uv(id1V),uvE=pline2.uv(id2V);
	uvbp.store_at(0,uvS);

	MGKnotVector& uvKnotv=uvpolyline->knot_vector();
	uvKnotv.size_change(2,nbd);
	uvKnotv(0)=uvKnotv(1)=0.;

	double tnbdm1=1.;
	uvbp.store_at(1,uvE);
	uvKnotv(nbd+1)=uvKnotv(nbd)=tnbdm1;

	short idBP[3];
	if(pline1.get_id_from_VertexID(id1V,idBP))
		uvpolyline->set_startID(idBP);
	if(pline2.get_id_from_VertexID(id2V,idBP))
		uvpolyline->set_endID(idBP);
	return uvpolyline;
}

///Debug Function
std::ostream& mgTL2LPline::out(std::ostream& ostrm)const{
	ostrm<<"mgTL2LPline::"<<this;
	ostrm<<", m_polyline="<<m_polyline<<", m_idS="<<m_idS<<", m_nV="<<m_nV<<std::endl;
	if(m_polyline)
		ostrm<<(*m_polyline);
	ostrm<<std::endl;
	return ostrm;
}
