/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
/**
 * @file surface.cpp
 * @brief surface.h の実装。
 */
#include "stdafx.h"

#include "mg/RLBRep.h"
#include "mg/Straight.h"
#include "mg/Plane.h"
#include "mg/SBRep.h"
#include "mg/RSBRep.h"
#include "mg/SBRepTP.h"
#include "topo/Shell.h"
#include "Calc/surface.h"
#include "Calc/curve.h"
#include "Calc/line.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

namespace mgcalc{

// Creates a swept surface with two curves.
// pathのコントロールポイントを平行移動したものを使おうと思ったが
// rational/nonrationalでやっかいなことがおこりそうなので
// 四辺面関数を使いまわす
std::unique_ptr<MGSurface> create_sweep_surface(
	const MGCurve& generatrix,   // generatrix curve
	const MGCurve& path     // degenerate point
){		
	MGPosition pivot = generatrix.start_point();
	MGVector trans = pivot - path.start_point();
	std::unique_ptr<MGCurve> path1(path.clone());
	*path1 += trans;
	
	trans = generatrix.end_point() - pivot;
	std::unique_ptr<MGCurve> path2(path1->clone());
	*path2 += trans;
	
	trans = path1->end_point() - pivot;
	std::unique_ptr<MGCurve> shape2(generatrix.clone());
	*shape2 += trans;

	const MGCurve* tmp[4] = { &generatrix ,path2.get() ,shape2.get() ,path1.get() };
	std::unique_ptr<MGLBRep> perims[4];
	if(!rebuildCurveTrimDirectionUpdate(tmp,perims)){
		return std::unique_ptr<MGSurface>(nullptr);
	}

	MGSBRepTP tp; int err = 0;
	const MGCurve* tmp2[4];
	extractConstPointerVec(perims, perims + 4, tmp2);
	std::unique_ptr<MGSBRep> surf(new MGSBRep);
	err=surf->buildByBlendWithTP(tmp2, tp);
	if(err)
		return std::unique_ptr<MGSurface>(nullptr);
	return surf;
}

// Creates a swept surface with a curve and a position.
// Extrude to a point.
std::unique_ptr<MGSurface> create_sweep_surface(
	const MGCurve&    curve,  // a curve
	const MGPosition& pos     // path curve
	){
	assert(!pos.is_null());
	assert(pos.sdim() == 3);
	assert(curve.sdim() == 3);
	
	if(curve.identify_type() == MGRLBREP_TID){
		const MGRLBRep& c = static_cast<const MGRLBRep&>(curve);
		MGBPointSeq bp = c.line_bcoef();
		MGSPointSeq sp(c.bdim(), 2, 4);
		
		MGVector v(4,pos);
		v(3) = 1.0;
		
		for(int i = 0; i < c.bdim(); i++){
			sp.store_at(i, 0, c.line_bcoef()(i));
			sp.store_at(i, 1, v);
		}
		double len = pos.distance(curve.start_point());
		if(len == 0.){
			return std::unique_ptr<MGSurface>(nullptr);
		}

		// OK. 面が生成できる
		MGKnotVector t(2, 2, 0., len);
		auto rsb(std::make_unique<MGRSBRep>());
		rsb->buildRSBRepFromMemberData(std::move(sp), c.knot_vector(), std::move(t));
		return rsb;
	}else{
		MGLBRep bottom(curve);
		MGSPointSeq sp(bottom.bdim(), 2, curve.sdim());
		for(int i = 0; i < bottom.bdim(); i++){
			sp.store_at(i, 0, bottom.line_bcoef()(i));
			sp.store_at(i, 1, pos);
		}
		double len = pos.distance(curve.start_point());
		if(len == 0.){
			return std::unique_ptr<MGSurface>(nullptr);
		}

		// OK. 面が生成できる
		MGKnotVector t(2, 2, 0., len);
		auto srf(std::make_unique<MGSBRep>());
		srf->buildSBRepFromMemberData(std::move(sp), std::move(bottom.knot_vector()), std::move(t));
		return srf;
	}
}

// Create a planar face sorrounded by boundary.
// If boundary does not make a loop, return 0.
std::unique_ptr<MGFace> planar_face(const MGCurve& boundary){
	// この実装は古いので planar2.cpp のそれと統合する予定
	
	if(!is_jordan(boundary)) return std::unique_ptr<MGFace>(nullptr);

	double t =(boundary.param_s()+boundary.param_e())*0.5, curva, tor;
	MGVector T, N, B;
	boundary.Frenet_frame(t, T, N, B, curva,tor);
	MGPlane plane(B, boundary.eval(t));

	std::unique_ptr<MGFace> face(new MGFace(plane));
	face->trim_projection(boundary);
	return face;
}

// Creates a planar face that has four vertices A, B, C, and D.
//MGVector(B,A) makes u-direction and MGVector(D,A) makes v-direction of the plane.
std::unique_ptr<MGFace> rectangular_face(
	const MGPosition& A,
	const MGPosition& B,
	const MGPosition& C,
	const MGPosition& D
){
	MGUnit_vector U(B-A),V,W;
	U.orthonormal(D-A,V,W);
	std::unique_ptr<MGFace> face(new MGFace(new MGPlane(U,V,(A+C)*.5)));

	face->trim_projection(MGStraight(B, A)); // vmin
	face->trim_projection(MGStraight(C, B)); // umax
	face->trim_projection(MGStraight(D, C)); // vmax
	face->trim_projection(MGStraight(A, D)); // umin
	//std::cout<<*face;
	
	return face;
}
	
// Creates a planar face that has four vertices corners[4].
//Let A=corners[0], B=corners[1],..., then this is the same as the above.
//MGVector(corners[1],corA) makes u-direction and MGVector(D,A) makes v-direction of the plane.
std::unique_ptr<MGFace> rectangular_face(
	const MGPosition corners[4]
){
	const MGPosition& A=corners[0];
	const MGPosition& B=corners[1];
	const MGPosition& C=corners[2];
	const MGPosition& D=corners[3];
	return rectangular_face(A,B,C,D);
}

// Creates a shell shapes to box.
std::unique_ptr<MGObject> shell_box(const MGBox& box){
	if(box.sdim() == 3){
		const MGInterval& x = box[0];
		const MGInterval& y = box[1];
		const MGInterval& z = box[2];

		// 矩形編集しやすいように書いておくこと
		MGPosition A(x. low_point(), y. low_point(), z. low_point());
		MGPosition B(x.high_point(), y. low_point(), z. low_point());
		MGPosition C(x.high_point(), y.high_point(), z. low_point());
		MGPosition D(x. low_point(), y.high_point(), z. low_point());
		MGPosition E(x. low_point(), y. low_point(), z.high_point());
		MGPosition F(x.high_point(), y. low_point(), z.high_point());
		MGPosition G(x.high_point(), y.high_point(), z.high_point());
		MGPosition H(x. low_point(), y.high_point(), z.high_point());

		// 向きに注意...

		if(A == B){
			if(A == D || A == E){
				return std::unique_ptr<MGObject>(nullptr);
			}
			// ADHE
			return std::unique_ptr<MGObject>(rectangular_line(A, D, H, E).release());
		}else if(A == D){
			if(A == E){
				return std::unique_ptr<MGObject>(nullptr);
			}else{
				// ABFH
				return std::unique_ptr<MGObject>(rectangular_line(A, B, F, H).release());
			}
		}else if(A == E){
			// ABCD
			return std::unique_ptr<MGObject>(rectangular_line(A, B, C, D).release());
		}

		// 各 face の向きに注意する。
		// ボックスの外側が表面になるようにする。

		std::unique_ptr<MGShell> she(new MGShell(rectangular_face(A, D, C, B).release()));

		std::unique_ptr<MGFace> f(rectangular_face(D, H, G, C));
		MGFace* fp=f.release();
		if(!she->merge_at_common_edge(fp)){
			delete(fp);
			return std::unique_ptr<MGObject>(nullptr);
		}
		f = rectangular_face(H, E, F, G);
		fp=f.release();
		if(!she->merge_at_common_edge(fp)){
			delete(fp);
			return std::unique_ptr<MGObject>(nullptr);
		}
		f = rectangular_face(E, A, B, F);
		fp=f.release();
		if(!she->merge_at_common_edge(fp)){
			delete(fp);
			return std::unique_ptr<MGObject>(nullptr);
		}
		f = rectangular_face(A, E, H, D);
		fp=f.release();
		if(!she->merge_at_common_edge(fp)){
			delete(fp);
			return std::unique_ptr<MGObject>(nullptr);
		}
		f = rectangular_face(B, C, G, F);
		fp=f.release();
		if(!she->merge_at_common_edge(fp)){
			delete(fp);
			return std::unique_ptr<MGObject>(nullptr);
		}
		return std::unique_ptr<MGObject>(she.release());
	}
	return std::unique_ptr<MGObject>(nullptr);
}

// トリム面のベース面を可能な限り小さくする
std::unique_ptr<MGFace> shrink(const MGFace& face)
{
	// MGCL に専用メソッドを提供してもらったので、
	// それで実装する。
	std::unique_ptr<MGFace> work(face.clone());
	// 引数で端部でのノット多重をするかどうかを指示できる。
	// ここでは自由にさせるべく 0 を与えて様子をみる。
	work->shrink_base_surface_to_knot(0);
	return work;
}
	
} // namespace mgcalc
