/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/

/**
 * @file SurfBlendAcross.cpp
 * @brief MGSurfBlendAcrossTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include "mg/LBRepEndC.h"
#include "mg/SBRepTP.h"
#include "Calc/mgcalc.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "GLBlendDlg.h"
#include "SurfCmd/SurfBlendAcross.h"

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

// MGSurfBlendAcrossTool

MGSurfBlendAcrossTool::MGSurfBlendAcrossTool(fugenDoc* pDoc)
: MGSelectState(pDoc, ID_SURFACE_FUNC_ACROSS,
	SINGLE_SELECT, // single-pick
	mgAll_SBRep, // SBRepだけが対象
	BOUNDARY_SELECT, // edge-pick
	PROHIBIT_UNSELECT//add mode and prohibit unselect
	),m_first(true)
{
	attachModelessDialogue<CGLBlendDlg>(m_derivMagnitude, m_continuousToNeiborEdge,this);
	set_add_mode();
	for (int i = 0; i < 4; i++) 
		m_perimeter[i] = UniqueLBRep(new MGLBRep);
	m_derivMagnitude[0] = m_derivMagnitude[1] = 1.;
	m_tp.set_TP(0, UniqueLBRep(new MGLBRep));
	m_tp.set_TP(2, UniqueLBRep(new MGLBRep));
}

MGCommandBase* MGSurfBlendAcrossTool::initial_clone(fugenDoc* pDoc) const{
	return new MGSurfBlendAcrossTool(pDoc);
}

bool MGSurfBlendAcrossTool::initiate_tool(){
	MGSelectState::initiate_tool();
	clear_pick_object();
	prompt_message();
	return false;
}

void MGSurfBlendAcrossTool::buildSurface(){
	if(m_pair.second.is_null())
		return;

	m_surf.reset(new MGSBRep);
	if(m_continuousToNeiborEdge){
		double magS = m_derivMagnitude[0], magE = m_derivMagnitude[1];
		MGLBRep& peri0 = *m_perimeter[0];
		MGLBRep& peri2 = *m_perimeter[2];
		perim_curve(peri0.end_point(), peri2.end_point(), magS*m_s1, magE*m_e1, *m_perimeter[1]);
		perim_curve(peri0.start_point(), peri2.start_point(), magS*m_s3, magE*m_e3, *m_perimeter[3]);

		const MGCurve* crvs[4];
		extractConstPointerVec(m_perimeter, m_perimeter+4, crvs);
		if(m_Coons)
			m_surf->buildFromSidesCoonsWithTP(crvs, m_tp);//Tangent by Coon Patch method.
		else
			m_surf->buildFromSidesBoolSumWithTP(crvs, m_tp);//Tangent by Boolean sum Patch method.
	}else{
		const MGCurve* curves[2]{m_perimeter[0].get(), m_perimeter[2].get()};
		const MGLBRep* tp[2]{&m_tp.TP(0), &m_tp.TP(2)};
		m_surf->buildFrom2Sides(curves, tp, m_derivMagnitude);
	}
 }

void MGSurfBlendAcrossTool::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	if(m_pair.second.is_null())
		return;

	buildSurface();
	MGColor::get_instance(MGColor::SpringGreen).exec(sgl);
	mgcalc::CreateDrawFunctor(sgl)(m_surf.get());
}

//Invoked when command is to terminate as a nomal end.
bool MGSurfBlendAcrossTool::OnCommandEnd(
	UINT nIDS,	//=0: erase the current message, and display no messages.
				//=1: display "xxxx" normally end.
				//otherwise: nIDS is a string id, and load the message from string table to display.
	bool erase_temporary_display
){
	if(m_pair.second.is_null())
		return 	OnCommandCanceled(1);

	add_object_to_current_group(m_surf.release());
	return MGSelectState::OnCommandEnd(1);
}

bool MGSurfBlendAcrossTool::OnKeyDown(fugenView * pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'c':
	case 'C':
		m_continuousToNeiborEdge = !m_continuousToNeiborEdge;
		prompt_message();
		return false;
	default:
		return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
	}
}

// SBRepのエッジがピックされたら呼ばれることになっている
bool MGSurfBlendAcrossTool::OnSelected(
	fugenView* window,//The fugenView pointer where point input event took place.
	MGPickObjects&	objs,	//selected objects at this selection operation.
	MGPickObjects&	unselected_objects	//unselected objects at this selection operation.
		//unselected_objects.size()>=1 only when the already selected objects are selected
		//when add mode is set(or when operation is done with a crtl key pressed).
){
	ASSERT(objs.size() == 1); // single-pick
	MGPickObjectSB* boundary = dynamic_cast<MGPickObjectSB*>(&objs.front());
	ASSERT(boundary);

	if(m_pair.first.is_null()){		
		m_pair.first = *boundary;// ひとつめのエッジをセット
		prompt_message();
	}else{
		m_pair.second = *boundary;// ふたつめのエッジをセット
		make_perimeter();// 辺と面を作成

		// メッセージを変更
		SetStatusMessage(IDS_PROMPT_PARAMETER);

		// ダイアログボックスを表示する
		if(m_first){
			append_current_object(m_pair.first);
			m_first = false;

			CPoint point;
			::GetCursorPos(&point);
			point.Offset(-4, -4);
			CGLBlendDlg* dialog = getBlendDialog();
			dialog->OnUpdateEdit();
			dialog->SetWindowPos(&CWnd::wndTop,point.x,point.y,0,0,SWP_NOSIZE|SWP_SHOWWINDOW);
			dialog->UpdateView();
		}
	}

	return false; // この段階ではコマンドは終了しない
}

void MGSurfBlendAcrossTool::make_perimeter(){
	
	const MGSBRep* srf1 = dynamic_cast<const MGSBRep*>(m_pair.first.surface());ASSERT(srf1);
	const MGSBRep* srf2 = dynamic_cast<const MGSBRep*>(m_pair.second.surface()); ASSERT(srf2);

	// s1 s3:
	MGPosition s1uv(2), s3uv(2);
	int periNum1 = m_pair.first.perimeter();
	switch(periNum1){
	case 0:
		s1uv = srf1->perimeter_uv(0, srf1->param_e_u());
		s3uv = srf1->perimeter_uv(0, srf1->param_s_u());
		m_s1 = -srf1->eval(s1uv, 0, 1);
		m_s3 = -srf1->eval(s3uv, 0, 1);
		break;
	case 1:
		s1uv = srf1->perimeter_uv(1, srf1->param_e_v());
		s3uv = srf1->perimeter_uv(1, srf1->param_s_v());
		m_s1 = srf1->eval(s1uv, 1, 0);
		m_s3 = srf1->eval(s3uv, 1, 0);
		break;
	case 2:
		s1uv = srf1->perimeter_uv(2, srf1->param_e_u());
		s3uv = srf1->perimeter_uv(2, srf1->param_s_u());
		m_s1 = srf1->eval(s1uv, 0, 1);
		m_s3 = srf1->eval(s3uv, 0, 1);
		break;
	case 3:
		s1uv = srf1->perimeter_uv(3, srf1->param_e_v());
		s3uv = srf1->perimeter_uv(3, srf1->param_s_v());
		m_s1 = -srf1->eval(s1uv, 1, 0);
		m_s3 = -srf1->eval(s3uv, 1, 0);
		break;
	}

	// e1 e3:
	MGPosition e1uv(2), e3uv(2);
	int periNum2 = m_pair.second.perimeter();
	switch(periNum2){
	case 0:
		e1uv = srf2->perimeter_uv(0, srf2->param_e_u());
		e3uv = srf2->perimeter_uv(0, srf2->param_s_u());
		m_e1 = srf2->eval(e1uv, 0, 1);
		m_e3 = srf2->eval(e3uv, 0, 1);
		break;
	case 1:
		e1uv = srf2->perimeter_uv(1, srf2->param_e_v());
		e3uv = srf2->perimeter_uv(1, srf2->param_s_v());
		m_e1 = -srf2->eval(e1uv, 1, 0);
		m_e3 = -srf2->eval(e3uv, 1, 0);
		break;
	case 2:
		e1uv = srf2->perimeter_uv(2, srf2->param_e_u());
		e3uv = srf2->perimeter_uv(2, srf2->param_s_u());
		m_e1 = -srf2->eval(e1uv, 0, 1);
		m_e3 = -srf2->eval(e3uv, 0, 1);
		break;
	case 3:
		e1uv = srf2->perimeter_uv(3, srf2->param_e_v());
		e3uv = srf2->perimeter_uv(3, srf2->param_s_v());
		m_e1 = srf2->eval(e1uv, 1, 0);
		m_e3 = srf2->eval(e3uv, 1, 0);
		break;
	}

	//Build perimeters and the TP.
	*m_perimeter[0]=srf1->perimeter(periNum1);
	srf1->TPatPerimeter(periNum1, m_tp.TP(0));
	*m_perimeter[2]=srf2->perimeter(periNum2);
	srf2->TPatPerimeter(periNum2, m_tp.TP(2));
	//std::cout << "TP::" << std::endl << m_tp;
	buildSurface();
}

void MGSurfBlendAcrossTool::perim_curve(
	const MGPosition& s, const MGPosition& e,
	const MGVector& ds, const MGVector& de,
	MGLBRep& b
){
	MGBPointSeq bp(2, 3);
	bp.store_at(0, s);
	bp.store_at(1, e);

	MGLBRepEndC start(MGENDC_1D, ds);
	MGLBRepEndC end(MGENDC_1D, de);
	MGNDDArray tau(bp);
	b.buildByInterpolationEC(start, end, tau, bp);
}

void MGSurfBlendAcrossTool::prompt_message() const{
	UINT msdID= m_pair.first.is_null() ?
		IDS_PROMPT_BLENDACROS1:IDS_PROMPT_EDGE_ANOTHER;

	CString Non; Non.LoadString(IDS_STRING_NON);
	CString first, second;
	if(m_continuousToNeiborEdge)
		second = Non;
	else
		first = Non;
	SetStatusMessage(msdID, first, second);// メッセージを変更
}

CGLBlendDlg* MGSurfBlendAcrossTool::getBlendDialog(){
	return static_cast<CGLBlendDlg*>(getModelessDialogPointer());
}
