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

#include "stdafx.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "EditCmd/EditJoinTool.h"
#include "Calc/mgjoin.h"
#include "Calc/mgcalc.h"

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

// MGJoinTool

MGJoinTool::MGJoinTool(fugenDoc* pDoc)
:MGCommandStateOwner(pDoc, ID_EDIT_JOIN){}

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

bool MGJoinTool::initiate_tool(){
	MGCommandStateOwner::initiate_tool();

	// join 操作の最初のオブジェクトが
	// 選択されている場合はスキップ
	const MGPickObjects& objs = current_objects();
	if(objs.size() == 1){
		if(MGSelectState* state = check_current(objs.front())){
			set_child_current_command(state);
			return false;
		}
	}
	// 最初のオブジェクトを選択するところからスタート
	set_child_current_command(new MGJoinSSeed(this));
	return false;
}

bool MGJoinTool::OnCommandCanceled(UINT nIDS){
	clear_pick_object();
	return MGCommandStateOwner::OnCommandCanceled(nIDS);
}

bool MGJoinTool::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
){
	UINT nids=1;
	if(!calculate()){
		nids=2;
		// failed
	}
	return MGCommandStateOwner::OnCommandEnd(nids);
}

// type check.
// curve or (surface or face or shell)
MGSelectState* MGJoinTool::check_current(const MGPickObject& po){
	// 核はつくれるか
	int nmani=MakeSeed(po);
	if(nmani==1){
		// Composite Curve コースへ進む
		return new MGJoinSCurve(this);
	}else if(nmani==2){
		// Shell コースへ進む
		return new MGJoinSSurf(this);
	}
	clear_pick_object();
	return 0;
}

bool MGJoinTool::calculate(){
	MGObject* obj=0;
	if(curve()){
		obj = m_curve.release();
	}else if(shell()){
		obj = m_shell.release();
	}else{
		return false;
	}

	// 入れ替えて終了
	replace(m_target, MGGelPositions(MGGelPosition(current_group(),obj)));
	return true;
}

// 複合オブジェクトの核を作る
//Function's return value is the manifold dimension of the kernel.
int MGJoinTool::MakeSeed(const MGPickObject& po){
	int manifold=0;
	const MGObject* obj = po.top_object();
	const MGCurve* c = dynamic_cast<const MGCurve*>(obj);
	if(c){
		// clone とコピーコンストラクターのコピー二段重ね。
		m_curve.reset(new MGCompositeCurve(c->clone()));
		manifold=1;
	}else{
		if(obj->manifold_dimension()==2){
			if(dynamic_cast<const MGPlane*>(obj))
				return 0;//MGPlane is excluded.

			manifold = 2;
			// MGShellのタネをつくる
			const MGSurface* surf = dynamic_cast<const MGSurface*>(obj);
			if(surf){
				MGFace* tmp = new MGFace(surf->copy_surface());
				m_shell.reset(new MGShell(tmp));
			}else{
				const MGFace* f = dynamic_cast<const MGFace*>(obj);
				if(f){
					MGFace* tmp = new MGFace(*f);
					m_shell.reset(new MGShell(tmp));
				}else{
					const MGShell* sh = dynamic_cast<const MGShell*>(obj);
					if(sh){
						m_shell.reset(new MGShell(*sh));
					}
				}
			}
		}
	}
	if(manifold)
		m_target.push_back(po);

	return manifold;
}

// MGJoinSSeed

MGJoinSSeed::MGJoinSSeed(MGJoinTool* owner)
: MGSelectState(owner, MGSelectState::SINGLE_SELECT){}

bool MGJoinSSeed::initiate_tool(){
	MGSelectState::initiate_tool();
	// 強制クリア
	clear_pick_object();
	// 最初のメッセージ
	SetStatusMessage(IDS_PROMPT_JOIN_OBJECT);
	return false;
}

bool MGJoinSSeed::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);
	if(MGSelectState* state = state_owner()->check_current(objs.front())){
		set_sibling_next_command(state);
	}
	return false;
}

// MGJoinSCurve

MGJoinSCurve::MGJoinSCurve(MGJoinTool* owner)
: MGSelectState(owner, MGSelectState::SINGLE_SELECT, mgAll_Curve){}

bool MGJoinSCurve::initiate_tool(){
	MGSelectState::initiate_tool();
	set_add_mode();
	lock_so_far();
	// 最初のメッセージ
	SetStatusMessage(IDS_PROMPT_JOIN_CURVE);
	return false;
}

bool MGJoinSCurve::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).
){
	MGCompositeCurve* c = state_owner()->curve();
	ASSERT(c);

	MGPickObject& po = objs.back();
	if(!mgcalc::join(c, po.top_object())){ // c に結合する
		SetStatusMessage(IDS_FAIL_JOIN_CURVE);//May not have common vertices.
		exclude_pick_object(objs);
	}else{
		state_owner()->m_target.push_back(po);
		lock_so_far();
	}
	return false;
}

// MGJoinSSurf
MGJoinSSurf::MGJoinSSurf(MGJoinTool* owner)
: MGSelectState(owner, MGSelectState::SINGLE_SELECT, mgAll_2Manifold){}

bool MGJoinSSurf::initiate_tool(){
	MGSelectState::initiate_tool();
	set_add_mode();
	lock_so_far();
	// 最初のメッセージ
	SetStatusMessage(IDS_PROMPT_JOIN_SURFACE);
	return false;
}

bool MGJoinSSurf::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).
){
	if(objs.empty()){
		return false;
	}

	MGShell* sh = state_owner()->shell();
	ASSERT(sh);

	MGPickObject& po = objs.back();
	MGObject* obj=po.top_object();
	if(dynamic_cast<MGPlane*>(obj)){
		// 何もしない。
	}else if(dynamic_cast<MGShell*>(obj)){
		SetStatusMessage(IDS_FAIL_JOIN_SHELL);//Shell not allowed.
	}else if(!mgcalc::join(sh,obj)){
		SetStatusMessage(IDS_FAIL_JOIN_SURFACE);//May not have common edges.
	}else{
		state_owner()->m_target.push_back(po);
		lock_so_far();
		SetStatusMessage(IDS_PROMPT_JOIN_SURFACE);
		return false;
	}

	exclude_pick_object(objs);
	return false;
}
