/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/* ***************************************************** */
/********************************************************************/
/**
 * @file CurveChamfer.cpp
 * @brief MGCurveChamferTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include "Calc/curve.h"
#include "Calc/chamfer.h"
#include "fugenView.h"
#include "GLCurveChamferDlg.h"
#include "Undo/GelAddAction.h"
#include "Undo/GelRemoveAction.h"
#include "Undo/MultiActions.h"
#include "Misc/UserPreference.h"
#include "CurveCmd/CurveChamfer.h"

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

// MGCurveChamferTool

MGCurveChamferTool::MGCurveChamferTool(fugenDoc* pDoc)
	 : MGSelectState(
		 pDoc,
		 ID_CURVE_CHAMFER,
		 MGSelectState::SINGLE_SELECT,
		 mgAll_Curve),
	   m_dDistance1(1.0),
	   m_dDistance2(1.0),
	   m_nIDS(1),
	   m_bJoin(false),
	   m_bTrim(false)
{
}

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

bool MGCurveChamferTool::initiate_tool()
{
	MGSelectState::initiate_tool();
	UserPreference& pref = UserPreference::getInstance();
	m_dDistance1 = pref.GetDoubleValue(upv_Curve_Chamfer_Distance1);
	m_dDistance2 = pref.GetDoubleValue(upv_Curve_Chamfer_Distance2);
	m_bJoin = pref.GetBoolValue(upv_Curve_Chamfer_Join);
	m_bTrim = pref.GetBoolValue(upv_Curve_Chamfer_Trim);

	// 強制的にクリア
	clear_pick_object();
	set_add_mode();
	prompt_message();
	return false;
}

bool MGCurveChamferTool::terminate_tool(bool cancel)
{
	UserPreference& pref = UserPreference::getInstance();
	pref.SetDoubleValue(upv_Curve_Chamfer_Distance1, m_dDistance1);
	pref.SetDoubleValue(upv_Curve_Chamfer_Distance2, m_dDistance2);
	pref.SetBoolValue(upv_Curve_Chamfer_Join, m_bJoin);
	pref.SetBoolValue(upv_Curve_Chamfer_Trim, m_bTrim);

	return MGSelectState::terminate_tool(cancel);
}

bool MGCurveChamferTool::calculate(){
	// 計算開始
	SetStatusMessage(IDS_PROMPT_COMPUTE);
	CWaitCursor sandglass;

	const MGCurve* curve1 = dynamic_cast<const MGCurve*>(m_curve1.top_object());
	const MGCurve* curve2 = dynamic_cast<const MGCurve*>(m_curve2.top_object());
	double hint1 = m_curve1.parameter()[0];
	double hint2 = m_curve2.parameter()[0];

	mgcalc::ChamferParam param1;
	mgcalc::ChamferParam param2;
	if(!param1.set(curve1, m_dDistance1, hint1)){
		// failed...
		m_nIDS = IDS_FAIL_CHAMFER;
		return false;
	}
	if(!param2.set(curve2, m_dDistance2, hint2)){
		// failed...
		m_nIDS = IDS_FAIL_CHAMFER;
		return false;
	}
	mgcalc::ChamferResult result(&param1, &param2);
	if(!result.chamfer()){
		// failed...
		m_nIDS = IDS_FAIL_CHAMFER;
		return false;
	}

	if(m_bTrim){
		std::unique_ptr<MGCurve> cham(result.chamfer_line());
		if(!cham.get() && (m_dDistance1 != 0 || m_dDistance2 != 0)){
			// failed...
			m_nIDS = IDS_FAIL_CHAMFER;
			return false;
		}
		std::unique_ptr<MGCurve> trimmed1(result.trimmed_curve(1));
		if(!trimmed1.get()){
			// failed...
			m_nIDS = IDS_FAIL_CHAMFER;
			return false;
		}
		std::unique_ptr<MGCurve> trimmed2(result.trimmed_curve(2));
		if(!trimmed2.get()){
			// failed...
			m_nIDS = IDS_FAIL_CHAMFER;
			return false;
		}

		CMultiActions* action = new CMultiActions(document());

		// 1. c1, c2 を remove
		MGGelPositions gelps;
		gelps.push_back(gelpos(m_curve1));
		gelps.push_back(gelpos(m_curve2));
		action->push_back(new CGelRemoveAction(document(), gelps));

		// 2. 生成されたカーブを add
		if(m_bJoin){
			if(cham.get()){
				std::unique_ptr<MGCurve> result = mgcalc::connect(*cham, *trimmed1);
				assert(result.get());
				result = mgcalc::connect(*result, *trimmed2);
				assert(result.get());
				action->push_back(new CGelAddAction(document(), make_gelpos(result.release())));
			}
			else{
				std::unique_ptr<MGCurve> result = mgcalc::connect(*trimmed1, *trimmed2);
				assert(result.get());
				action->push_back(new CGelAddAction(document(), make_gelpos(result.release())));
			}
		}
		else{
			MGGelPositions gelps;
			gelps.push_back(make_gelpos(trimmed1.release()));
			if(cham.get()){
				// 面取り距離 0, 0 の場合はヌルである
				gelps.push_back(make_gelpos(cham.release()));
			}
			gelps.push_back(make_gelpos(trimmed2.release()));

			action->push_back(new CGelAddAction(document(), gelps));
		}
		action->Do();
	}
	else{
		// 一番シンプルなパターン
		std::unique_ptr<MGCurve> cham(result.chamfer_line());
		if(cham.get()){
			add_object_to_current_group(cham.release());
		}else{
			// failed...
			m_nIDS = IDS_FAIL_CHAMFER;
			return false;
		}
	}

	return true;
}

bool MGCurveChamferTool::input_param(){
	CGLCurveChamferDlg dlg;

	// ダイアログ初期値セット
	dlg.m_dist1 = m_dDistance1;
	dlg.m_dist2 = m_dDistance2;
	dlg.m_bJoin   = m_bJoin;
	dlg.m_bTrim   = m_bTrim;
	
	prompt_message();
	bool bOK = (IDOK == dlg.DoModal());

	// パラメータ回収
	m_dDistance1 = dlg.m_dist1;
	m_dDistance2 = dlg.m_dist2;
	m_bJoin   = (dlg.m_bJoin == TRUE);
	m_bTrim   = (dlg.m_bTrim == TRUE);
	
	return bOK;
}

bool MGCurveChamferTool::OnKeyDown(fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'd':
	case 'D':  // 距離入力
		if(!input_param()){
			return OnCommandEnd(m_nIDS);
		}
		// メッセージ更新
		break;
	case 'j':
	case 'J':  // toggle join option
		m_bJoin = !m_bJoin;
		break;
	case 't':
	case 'T':   // toggle trim option
		m_bTrim = !m_bTrim;
		break;
	default:;
	}
	prompt_message();
	return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGCurveChamferTool::OnSelected(
	fugenView* pView,
	MGPickObjects& curobj,
	MGPickObjects&
	){
	ASSERT(curobj.size() == 1);
	const MGCurve* curve = dynamic_cast<const MGCurve*>(curobj.front().top_object());
	if(!m_curve1.is_null()){
		// 2本目のカーブがセットできるかどうか
		const MGCurve* curve2 = dynamic_cast<const MGCurve*>(m_curve1.top_object());
		if(curve == curve2){
			// 同じカーブをchamferしてはいけない
			prompt_message();
			return false;
		}else{// 2本目のカーブをセット
			m_curve2 = curobj.front();
		}
	}
	else{
		// 1本目のカーブをセット
		m_curve1 = curobj.front();
		lock_so_far();
		// メッセージを更新
		prompt_message();
		return false;
	}

	// 計算開始
	if(!calculate()){
		// failed
		return OnCommandEnd(m_nIDS);
	}
	return OnCommandEnd(1);
}

void MGCurveChamferTool::prompt_message() const{
	CString strTrim;
	strTrim.LoadString(m_bTrim ? IDS_YES : IDS_NO);
	CString strJoin;
	strJoin.LoadString(m_bJoin ? IDS_YES : IDS_NO);

	if(m_curve1.is_null()){
		SetStatusMessage(IDS_PROMPT_CURVE_CHAMFER, 1, m_dDistance1, m_dDistance2, strTrim, strJoin);
	}
	else if(m_curve2.is_null()){
		SetStatusMessage(IDS_PROMPT_CURVE_CHAMFER, 2, m_dDistance1, m_dDistance2, strTrim, strJoin);
	}
	else{
		SetStatusMessage(IDS_PROMPT_PARAMETER);
	}
}
