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

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

// MGTrimTool

MGTrimTool::MGTrimTool(fugenDoc* pDoc)
	 : MGCommandStateOwner(pDoc, ID_EDIT_TRIM),
	   m_bExtend(false),
	   m_bApparent(false),
	   m_bExtract(false)
{}

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

bool MGTrimTool::initiate_tool(){
	MGCommandStateOwner::initiate_tool();
	if(FilterForTrimmer()){
		// Skip to [S2]
		set_child_current_command(new MGTrimSelTarget(this));
	}
	else{
		// [S1]; トリムされるオブジェクトを選択する state からスタート
		set_child_current_command(new MGTrimSelTrimmer(this));
	}
	return false;
}

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

bool MGTrimTool::IsEnabled(MGTrimTool::OptionName opt) const
{
	switch(opt){
	case EXTEND:
		return m_bExtend;
	case APPARENT:
		return m_bApparent;
	case EXTRACT:
		return m_bExtract;
	default:
		ASSERT(false);
		throw std::invalid_argument("implementation missed");
	}
}

void MGTrimTool::ToggleExtendOption(){
	m_bExtend = !m_bExtend;
}

void MGTrimTool::ToggleApparentOption(){
	m_bApparent = !m_bApparent;
}

void MGTrimTool::ToggleExtractOption(){
	m_bExtract = !m_bExtract;
}

void MGTrimTool::prompt_message(UINT nIDS)const{
	CString strExtendYN;
	strExtendYN.LoadString(IsEnabled(MGTrimTool::EXTEND) ? IDS_YES : IDS_NO);
	CString strApparentYN;
	strApparentYN.LoadString(IsEnabled(MGTrimTool::APPARENT) ? IDS_YES : IDS_NO);
	CString strExtractYN;
	strExtractYN.LoadString(IsEnabled(MGTrimTool::EXTRACT) ? IDS_YES : IDS_NO);

	SetStatusMessage(nIDS, strExtendYN, strApparentYN, strExtractYN);
}

/// 選択オブジェクトの仕分けを行う。
/// @return 切断オブジェクトがひとつ以上定義できたら true を返す。
///
/// 1. 切断オブジェクトとして不適当なタイプのものを選択オブジェクトから除外する。
/// 2. このクラスのメンバーデータを適宜セットする。
bool MGTrimTool::FilterForTrimmer()
{
	if(current_objects().empty()) return false;

	m_Ctrimmers=select_from_current_objects(mgAll_Curve);
	m_Strimmers=select_from_current_objects(mgAll_FSurface);
	set_current_object(m_Ctrimmers);
	append_current_object(m_Strimmers);
	return (!m_Ctrimmers.empty() || !m_Strimmers.empty());
}

/// ターゲットに切断オブジェクトが含まれていればそれを除外する。
/// @param [in,out] target トリム対象オブジェクト候補。
void MGTrimTool::ExcludeTrimmer(MGPickObjects& target)
{
	target.remove(m_Ctrimmers);
	target.remove(m_Strimmers);
}

/// トリムを実行する。
/// @return トリム操作が実際に発生したら true を返す。
bool MGTrimTool::Execute(
	MGPickObject& pobj,
	fugenView* pView)
{
	// 延長オプション
	const bool bExtend = IsEnabled(EXTEND);

	// 仮想交差オプション
	const bool bApparent = IsEnabled(APPARENT);

	MGObject* target = pobj.top_object();
	ASSERT(target);

	CWaitCursor suna;

	if(MGFSurface* surf = target->fsurface()){
		const MGVector& dir = GetObjProjDir(*target, pView, bApparent);
		if(!ExecTrimFSurf(pobj, m_Ctrimmers, m_Strimmers, dir, bExtend, *this, m_bExtract)){
			// failed
			return false;
		}
	}else{
		if(!ExecTrimCurve(pobj, m_Ctrimmers, m_Strimmers, pView, bExtend, bApparent, *this, m_bExtract)){
			// failed
			return false;
		}
	}

	return true;
}

// MGTrimSelTrimmer

MGTrimSelTrimmer::MGTrimSelTrimmer(MGTrimTool* owner)
	 : MGSelectState(owner, MGSelectState::MULTIPLE_SELECT)
{
	set_add_mode();
}

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

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

void MGTrimSelTrimmer::prompt_message()const{
	state_owner()->prompt_message(IDS_PROMPT_TRIM_TRIMMER);
}

bool MGTrimSelTrimmer::OnKeyDown(fugenView* pView, UINT nChar,UINT nRepCnt, UINT nFlags)
{
	switch(nChar){
	case 'e':
	case 'E':
		state_owner()->ToggleExtendOption();
		break;
	case 'a':
	case 'A':
		state_owner()->ToggleApparentOption();
		break;
	case 'r':
	case 'R':
		state_owner()->ToggleExtractOption();
		break;
	case VK_RETURN:{
			// ここでトリムするオブジェクトが確定
			if(!state_owner()->FilterForTrimmer()){
				return false;
			}

			// 次のstateへ進む
			set_sibling_next_command(new MGTrimSelTarget(state_owner()));
		}
		return false;
	default:;
	}
	prompt_message();
	return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGTrimSelTrimmer::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).
){
	// シェルでは切れない
	MGPickObjects pobjs(current_objects());
	pobjs.remove(mgAll_Shell);
	set_current_object(pobjs,true);
	return false;
}

// MGTrimSelTarget

MGTrimSelTarget::MGTrimSelTarget(MGTrimTool* owner)
: MGSelectState(owner, MGSelectState::SINGLE_SELECT)
{}

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

	// 強制クリアしない
	//clear_pick_object();
	// 代わりに切断オブジェクトを選択ロックする。
	lock_so_far();

	// 最初のメッセージ
	prompt_message();
	return false;
}

bool MGTrimSelTarget::OnKeyDown(fugenView* pView, UINT nChar,UINT nRepCnt, UINT nFlags)
{
	switch(nChar){
	case 'e':
	case 'E':
		state_owner()->ToggleExtendOption();
		prompt_message();
		return false;
	case 'a':
	case 'A':
		state_owner()->ToggleApparentOption();
		prompt_message();
		return false;
	case 'r':
	case 'R':
		state_owner()->ToggleExtractOption();
		prompt_message();
		return false;
	default:
		return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
	}
}

bool MGTrimSelTarget::OnSelected(
	fugenView* pView,//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).
){
	// point と shell はいらない
	objs.remove(mgAll_Shell);
	objs.remove(mgAll_Point);
	if(objs.empty()){
		return false;
	}

	MGTrimTool* owner=state_owner();
	// すでにトリムされるオブジェクトとして
	// 指定されたものとの重複は認めない
	owner->ExcludeTrimmer(objs);
	if(objs.empty()){
		return false;
	}

	// トリム開始。
	// トリムターゲットは一つだけなので、それを取得する。
	MGPickObject& pobj = static_cast<MGPickObject&>(objs.front());
	MGFSurface* f = pobj.top_object()->fsurface();
	MGPlane* pl=dynamic_cast<MGPlane*>(f);
	if(pl)
		return owner->OnCommandCanceled(IDS_PLAIN_PROHIBITTED);////****無限平面は不可のエラーメッセージを出す


	// トリムを実行する。
	if(!owner->Execute(pobj, pView)){
		return owner->OnCommandCanceled(1);
	}

	// 終了
	return OnCommandEnd(1);
}

void MGTrimSelTarget::prompt_message()const{
	state_owner()->prompt_message(IDS_PROMPT_TRIM_TRIMMED);
}
