/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
#include "StdAfx.h"
#include "mg/Straight.h"
#include "mg/LBRep.h"
#include "mg/SurfCurve.h"
#include "mg/Tolerance.h"
#include "topo/PVertex.h"
#include "topo/Edge.h"
#include "topo/Face.h"

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

//Test if this edge's start point(when start=true) and edge2 is connected
//and their directions are the same.
//When start=false, this edge's end point is tested.
bool MGEdge::is_connected_and_same_direction(
	bool start,
	const MGEdge& edge2
)const{
	const MGPVertex* pv1;
	const MGPVertex* pv2;
	if(start){
		pv1=vertex_start().get();
		pv2=edge2.vertex_end().get();
	}else{
		pv1=vertex_end().get();
		pv2=edge2.vertex_start().get();
	}
	return pv1->binder()==pv2->binder();
}

//Make a binder cell of this parameter cell.
//Returned is the binder pointer generated by new.
//The binder has no geometry, only has binder and parameter cell relationship.
std::shared_ptr<MGBCell>& MGEdge::make_binder()const{
	assert(is_pcell());
	if(m_binder)
		return m_binder;

	resetBinder(new MGEdge());
	return m_binder;
}

//Make this cell's binder cell's extent expression.
//Returned is a MGGeometry pointer generated by new.
//When this cell does not have star cell, null pointer will be returned.
//make_binder_extent() only makes the expression, and does nothing to
//the topology structure.
UniqueGeometry MGEdge::make_binder_extent() const{
	assert(is_pcell());
	assert(face());
	assert(face()->surface());

	const MGSurface* srf=face()->surface();
	MGSurfCurve srfcrv(*srf, trimmed_curve());//std::cout<<srfcrv<<std::endl;//////////////
	return std::unique_ptr<MGCurve>(srfcrv.copy_as_nurbs());;
}

//Make a binder associated with the world curve rep.
//Returned is the binder edge pointer.
//If the parameter edge already had the binder,
//make_binder_with_curve() only returns the pointer.
//*** This edge must be a parameter edge of a loop that is a boundary of a face.
MGEdge* MGEdge::make_binder_with_curve()const{
	assert(is_pcell());
	MGEdge* bedge = binder_edge();
	if(bedge)
		if(bedge->base_curve())
			return bedge;

	if(!bedge)//Generate binder edge.
		bedge = dynamic_cast<MGEdge*>(make_binder().get());
	bedge->make_extent();
	return bedge;
}

//Make sure that this has an extent expression.
//When this did not have an extent, make the extent from the partner
//member's parameter expression and the star cell.
//This must be a binder cell that has partner members that are
//boundaries. When this is not the case or this had an extent already,
//it does nothing.
void MGEdge::make_extent() const{
	assert(is_bcell());
	if(!is_bcell()) return;
	if(extent()) return;

	const MGEdge* pedge=partner_member_edge(0);
	UniqueGeometry bcrv=pedge->make_binder_extent();
	MGEdge* thisE = const_cast<MGEdge*>(this);
	thisE->set_extent(std::move(bcrv));
	/*double t0 = param_s(), t1 = param_e();
	thisE->set_start(t0);
	thisE->set_end(t1);*/
	pedge->m_equal_to_binder = 1;//pedge is equal_direction to this.
}

//Obtain the i-th member partner edge. This must be a binder edge.
const MGEdge* MGEdge::partner_member_edge(int i)const{
	return dynamic_cast<const MGEdge*>(partner_member(i));
}

//Approximate the parameter edge by a polyline and replace this edge
//expression by the polyline. Polyline approximation is so done that
//the correspoinding binder edge can be appximated by the polyline connecting
//each binder edge's point that corresponds to the each this edge's point.
//(1) This must be a parameter cell edge.
//(2) This edge must be a member of a loop which is a boundary of a face.
//(3) If this edge did not have a binder edge, polygonize generates the binder edge.
//(The tolerance used to generate the binder is MGTolerance::line_zero(),
// not input error.)
//Input error is tolerance allowed between the polygon and the original curve.
void MGEdge::polygonize(double error){
	const MGSurface* srf = face()->surface();
	UniqueLBRep Bcrv;
	MGEdge* bedge = binder_edge();
	mgTolSetLineZero lineZeroSet(error);
	if(bedge){
		if(!bedge->base_curve()){
			Bcrv.reset(new MGLBRep(MGSurfCurve(*srf,trimmed_curve()), 2));
		}else{
			Bcrv.reset(new MGLBRep(bedge->trimmed_curve(), 2));
		}
		bedge->set_extent(std::move(Bcrv));
	}else{
		Bcrv.reset(new MGLBRep(MGSurfCurve(*srf,trimmed_curve()), 2));
		bedge=set_binder_edge(Bcrv.release());
	}
	lineZeroSet.restore();
	
	UniqueLBRep nBcrv(dynamic_cast<MGLBRep*>(bedge->curve_limitted()));
	double t0,t1;
	if(equal_direction_to_binder()){
		t0 = param_s(); t1 = param_e();
	}else{
		t1 = param_s(); t0 = param_e();
	}
	nBcrv->change_range(t0, t1);
		//Now nBcrv is the same direction as this parameter edge, and has
		//the same parameter range.

	//Change Bcrv to original parameter edge's polyline expression.
	MGCurve* originalC=base_curve();
	int n = nBcrv->bdim();
	MGBPointSeq coef(n,2);
	const MGKnotVector& t = nBcrv->knot_vector();
	for(int i = 0; i < n; i++) coef.store_at(i, originalC->eval(t[i+1]));
	nBcrv->line_bcoef() = coef;
	set_extent(std::move(nBcrv));
}
