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

/**
 * @file EditSplitTool.cpp
 * @brief EditSplitTool.h の実装
 */
#include "stdafx.h"
#include "Calc/mgcalc.h"
#include "fugenView.h"
#include "EditCmd/editfunc.h"
#include "EditCmd/EditSplitTool.h"

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

// MGSplitTool

MGSplitTool::MGSplitTool(fugenDoc* pDoc)
: MGCommandStateOwner(pDoc, ID_EDIT_SPLIT),m_splitMode(ByIntersection){
}

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

bool MGSplitTool::initiate_tool(){
	MGCommandStateOwner::initiate_tool();
	MGPickObjects curobj = current_objects();
	// Remove points and shells.
	curobj.remove(mgAll_Point);
	curobj.remove(mgAll_Shell);
	set_current_object(curobj);
	if(curobj.empty()){
		// [S1]; 分割されるオブジェクトを選択する state からスタート
		set_child_current_command(new MGSplitSelTarget(this));
	}else{
		// [S3]; 切断オブジェクトを選択する state からスタート
		SetSplitTarget(curobj);
		set_child_current_command(new MGSplitSelCutter(this));
	}

	return false;
}

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

bool MGSplitTool::OnCommandEnd(
	UINT nIDS,
	bool erase_temporary_display
){
	switch(GetSplitType()){
	case ByIntersection:
		if(m_cutter.empty()){
			return OnCommandCanceled(1);
		}else{
			const size_t nRes = ExecSplitByIntersection(
				m_target, m_cutter, *this);
			if(nRes){
				CString str;
				str.Format(IDS_SPLIT_RESULT_BY_INTERSECTION, m_target.size(), nRes);
				COUT << (TCAST)str << std::endl;
			}else{
				return OnCommandCanceled(IDS_FAIL_SPLIT);
			}
		}
		break;
	case ByPoints:
		if(m_param.empty()){
			return MGCommandStateOwner::OnCommandCanceled(1);
		}else{
			const size_t nRes = ExecSplitCurveByPoints(
				m_target.front(), m_param, *this);
			if(nRes){
				CString str;
				str.Format(IDS_SPLIT_RESULT_BY_POINTS, 1, nRes);
				COUT << (TCAST)str << std::endl;
			}else{
				return OnCommandCanceled(IDS_FAIL_SPLIT);
			}
		}
		break;
	case ByIsocurves:
		break;
	}

	return MGCommandStateOwner::OnCommandEnd(1);
}

SplitBy MGSplitTool::GetSplitType()const{
	return m_splitMode;
}

void MGSplitTool::SetSplitType(SplitBy mode){
	m_splitMode = mode;
}

void MGSplitTool::DrawPoints(mgSysGL& sgl,fugenView* pView)const{
	// これまでの確定分
	sgl.drawPoints(
		MGColor::get_instance(MGColor::DeepPink),
		MGColor::get_instance(MGColor::White),
		m_preview_points);

}

// 分割コマンドのターゲットを取得する。
const MGPickObjects& MGSplitTool::GetSplitTarget()const{
	return m_target;
}

// 分割コマンドのターゲットをセットする。
void MGSplitTool::SetSplitTarget(const MGPickObjects& target){
	m_target = target;
}

const MGPickObjects& MGSplitTool::GetCutter()const{
	return m_cutter;
}

void MGSplitTool::SetCutter(const MGPickObjects& added, const MGPickObjects& removed){
	m_cutter.remove(removed);

	// 重複要素はないだろう。
	m_cutter.push_back(added);
}

const MGCurve* MGSplitTool::GetSplitTargetCurve()const{
	ASSERT(m_target.size() == 1);
	return dynamic_cast<const MGCurve*>(m_target.front().top_object());
}

void MGSplitTool::AddSplitParam(double dParam){
	ASSERT(m_splitMode == ByPoints);
	ASSERT(m_target.size() == 1);

	// ソートとかは後で。
	m_param.push_back(dParam);

	// プレビューデータを更新
	const MGCurve* curve = GetSplitTargetCurve();
	m_preview_points.push_back(curve->eval_position(dParam));
}

void MGSplitTool::RemoveSplitParam(){
	if(m_splitMode==ByPoints){
		m_param.pop_back();
		m_preview_points.pop_back();
	}
}

const MGFSurface* MGSplitTool::GetSplitTargetSurf() const{
	ASSERT(m_splitMode == ByIsocurves);
	ASSERT(m_target.size() == 1);

	return m_target.front().top_object()->fsurface();
}

//////////////////////////////////////////////////////////////////////
// [S1] MGSplitSelTarget

MGSplitSelTarget::MGSplitSelTarget(MGSplitTool* owner)
: MGSelectState(owner, MULTIPLE_SELECT){
	set_add_mode();
}

bool MGSplitSelTarget::initiate_tool(){
	MGSelectState::initiate_tool();

	// メッセージを更新
	prompt_message();
	return false;
}

void MGSplitSelTarget::prompt_message()const{
	const bool bCanChangeCurve = CanChangeSelCurveState();
	const bool bCanChangeSurf = CanChangeSelSurfState();

	if(bCanChangeCurve){
		if(bCanChangeSurf){
			// オプションを変更できる場合のプロンプト
			SetStatusMessage(IDS_PROMPT_SPLIT_TARGET);
		}else{
			SetStatusMessage(IDS_PROMPT_SPLIT_TARGET_CURVE);
		}
	}else if(bCanChangeSurf){
		SetStatusMessage(IDS_PROMPT_SPLIT_TARGET_SURF);
	}else{
		// オプションなし
		SetStatusMessage(IDS_PROMPT_SPLIT_TARGET_INTERSECTION);
	}
}

bool MGSplitSelTarget::OnKeyDown(
	fugenView* pView, UINT nChar,UINT nRepCnt, UINT nFlags
){
	MGSplitTool* pOwner = state_owner();
	switch(nChar){
	case 'p':
	case 'P':
		if(CanChangeSelCurveState()){///<test if current objects include curve
			pOwner->SetSplitType(ByPoints);
			if(current_objects().empty()){
				// [S2] へ遷移。
				set_sibling_next_command(new MGSplitSelTargetCurve(pOwner));
				return false;
			}else{
				// [S5] へ遷移。
				pOwner->SetSplitTarget(current_objects());
				set_sibling_next_command(new MGSplitLocatePoints(pOwner));
				return false;
			}
		}
		break;
	case 'i':
	case 'I':
		if(CanChangeSelSurfState()){///<test if current objects include fsurface
			pOwner->SetSplitType(ByIsocurves);
			if(current_objects().empty()){
				// [S4] へ遷移する。
				set_sibling_next_command(new MGSplitSelTargetSurf(pOwner));
				return false;
			}else{
				// [S6] へ遷移する。
				pOwner->SetSplitTarget(current_objects());
				set_sibling_next_command(new MGSplitLocateIsocurves(pOwner,pOwner->m_target.back()));
				return false;
			}
		}
		break;
	case VK_RETURN:
		if(!current_objects().empty()){
			// 分割ターゲット確定
			pOwner->SetSplitTarget(current_objects());
			set_sibling_next_command(new MGSplitSelCutter(pOwner));
			return false;
		}else{
			return OnCommandEnd(3);
		}
	default:
		break;
	}

	return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGSplitSelTarget::OnSelected(
	fugenView* pView,
	MGPickObjects& added,
	MGPickObjects& removed)
{
	// シェルを切ることはできない
	MGPickObjects pobjs(current_objects());
	pobjs.remove(mgAll_Shell);
	set_current_object(pobjs);
	prompt_message();
	return false;
}

bool MGSplitSelTarget::CanChangeSelCurveState() const{
	return CanChangeSelSpecialTargetState(mgAll_Curve);
}

bool MGSplitSelTarget::CanChangeSelSurfState() const{
	return CanChangeSelSpecialTargetState(mgAll_FSurface);
}

bool MGSplitSelTarget::CanChangeSelSpecialTargetState(const MGAbstractGels& type)const{
	const MGPickObjects& selection = current_objects();
	switch(selection.size()){
	case 0:
		return true;
	case 1:
		return current_object_is_valid(type);
	default:
		return false;
	}
}

//////////////////////////////////////////////////////////////////////
// [S2] MGSplitSelTargetCurve

MGSplitSelTargetCurve::MGSplitSelTargetCurve(MGSplitTool* owner)
: MGSelectState(owner, SINGLE_SELECT, mgAll_Curve){
}

bool MGSplitSelTargetCurve::initiate_tool(){
	ASSERT(current_objects().empty());
	MGSelectState::initiate_tool();

	MGSplitTool* pOwner = state_owner();
	pOwner->SetSplitType(ByPoints);

	// メッセージを更新
	prompt_message();

	return false;
}

bool MGSplitSelTargetCurve::OnSelected(
	fugenView* pView,
	MGPickObjects& added,
	MGPickObjects& removed
){
	// オーナーにターゲットを教えて次 [S5] へ
	MGPickObjects work;
	current_object_is_valid(mgAll_Curve, work);
	if(work.size() == 1){
		state_owner()->SetSplitTarget(work);
		// [S5]
		set_sibling_next_command(new MGSplitLocatePoints(state_owner()));
		return false;
	}

	// キャンセルとみなす。
	return OnCommandCanceled(1);
}

void MGSplitSelTargetCurve::prompt_message()const{
	SetStatusMessage(IDS_PROMPT_SPLIT_TARGET_CURVE_ONLY);
}

//////////////////////////////////////////////////////////////////////
// [S3] MGSplitSelCutter

MGSplitSelCutter::MGSplitSelCutter(MGSplitTool* owner)
: MGSelectState(owner, MULTIPLE_SELECT){
	set_add_mode();
}

bool MGSplitSelCutter::initiate_tool(){
	MGSelectState::initiate_tool();

	// 一旦選択をクリアしないで
	// 単に選択をロックする。
	lock_so_far();

	// メッセージを更新
	prompt_message();

	return false;
}

void MGSplitSelCutter::prompt_message()const{
	// [S1] によく似ている。
	const MGSplitTool* pOwner = state_owner();
	const MGPickObjects& target = pOwner->GetSplitTarget();
	const MGPickObjects& current = current_objects();

	// ターゲットのタイプでプロンプトを変更する。
	UINT nIDS = IDS_PROMPT_SPLIT_CUTTER;

	if(CanChangeLocatePointsState()){//Test if selected objects are curves.
		nIDS = IDS_PROMPT_SPLIT_CUTTER_CURVE;
	}else if(CanChangeLocateIsocurvesState()){
		nIDS = IDS_PROMPT_SPLIT_CUTTER_SURF;
	}

	SetStatusMessage(nIDS);
}

bool MGSplitSelCutter::OnKeyDown(
	fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags
){
	MGSplitTool* pOwner = state_owner();
	switch(nChar){
	case 'p':
	case 'P':
		if(CanChangeLocatePointsState()){//Curve selected?
			// [S5] へ遷移する。
			pOwner->SetSplitType(ByPoints);
			set_sibling_next_command(new MGSplitLocatePoints(state_owner()));
			return false;
		}
		break;
	case 'i':
	case 'I':
		if(CanChangeLocateIsocurvesState()){
			// [S6] へ遷移する。
			pOwner->SetSplitType(ByIsocurves);
			set_sibling_next_command(new MGSplitLocateIsocurves(pOwner,pOwner->m_target.back()));
			return false;
		}
		break;
	case VK_RETURN:
		// カッター要素確定
			return pOwner->OnCommandEnd(1);
	default:
		break;
	}

	return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGSplitSelCutter::OnSelected(
	fugenView* pView,
	MGPickObjects& added,
	MGPickObjects& removed
){
	MGSplitTool* pOwner = state_owner();
	pOwner->SetCutter(added, removed);

	prompt_message();
	return false;
}

bool MGSplitSelCutter::CanChangeLocatePointsState()const{
	return CanChangeLocateSpecialState(mgAll_Curve);
}

bool MGSplitSelCutter::CanChangeLocateIsocurvesState()const{
	return CanChangeLocateSpecialState(mgAll_FSurface);
}

bool MGSplitSelCutter::CanChangeLocateSpecialState(
	const MGAbstractGels& type
)const{
	const MGSplitTool* pOwner = state_owner();
	const MGPickObjects& target = pOwner->GetSplitTarget();
	const MGPickObjects& current = current_objects();

	if(pOwner->m_cutter.empty() && target.size() == 1){
		MGPickObjects work(target);
		work.reset_objects(type);
		if(work.size() == 1){
			return true;
		}
	}

	return false;
}

//////////////////////////////////////////////////////////////////////
// [S4] MGSplitSelTargetSurf
//
// [S2] とほぼ同じ。

MGSplitSelTargetSurf::MGSplitSelTargetSurf(MGSplitTool* owner)
: MGSelectState(owner, SINGLE_SELECT, mgAll_FSurface){
}

bool MGSplitSelTargetSurf::initiate_tool(){
	ASSERT(current_objects().empty());
	MGSelectState::initiate_tool();
	MGSplitTool* pOwner = state_owner();
	pOwner->SetSplitType(ByIsocurves);

	// メッセージを更新
	prompt_message();
	return false;
}

bool MGSplitSelTargetSurf::OnSelected(
	fugenView* pView,
	MGPickObjects& added,
	MGPickObjects& removed
){
	// オーナーにターゲットを教えて次 [S6] へ
	MGPickObjects work;
	current_object_is_valid(mgAll_FSurface, work);
	if(work.size() == 1){
		MGSplitTool* pOwner = state_owner();
		pOwner->SetSplitTarget(work);
		pOwner->SetSplitType(ByIsocurves);
		// [S6] へ遷移する。
		set_sibling_next_command(new MGSplitLocateIsocurves(pOwner,pOwner->m_target.back()));
		return false;
	}

	// キャンセルとみなす。
	return OnCommandCanceled(1);
}

void MGSplitSelTargetSurf::prompt_message()const{
	SetStatusMessage(IDS_PROMPT_SPLIT_TARGET_SURF_ONLY);
}

//////////////////////////////////////////////////////////////////////
// [S5] MGSplitLocatePoints

MGSplitLocatePoints::MGSplitLocatePoints(MGSplitTool* owner)
: MGLocateOnObjects(owner,
LOCK_SNAP_ATTRIB//Prohibit to update snap attrib
){
	turn_on_near();
	turn_on_knot();
}

bool MGSplitLocatePoints::initiate_tool(){
	set_locate_objectives(state_owner()->GetSplitTargetCurve());
	MGLocateOnObjects::initiate_tool();
	MGSplitTool* pOwner = state_owner();
	pOwner->SetSplitType(ByPoints);
	return false;
}

bool MGSplitLocatePoints::OnLocated(const MGLocateInfo& info){
	// ロケートされた点で分割するので、点をすべて記憶する。

	MGSplitTool* pOwner = state_owner();
	if(info.is_deleted()){
		pOwner->RemoveSplitParam();
	}else{
		if(info.object() == pOwner->GetSplitTargetCurve()){
			const double dParam = info.curve_parameter();
			pOwner->AddSplitParam(dParam);
		}
	}
	return false;
}

void MGSplitLocatePoints::prompt_message()const{
	SetStatusMessage(IDS_PROMPT_SPLIT_LOCATE_POINTS);
}

void MGSplitLocatePoints::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	MGSplitTool* owner = state_owner();
	owner->DrawPoints(sgl,pView);
}
