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

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

// MGCurveFilletTool

MGCurveFilletTool::MGCurveFilletTool(fugenDoc* pDoc)
	 : MGSelectState(
		 pDoc,
		 ID_CURVE_FILLET,
		 MGSelectState::SINGLE_SELECT,
		 mgAll_Curve),
	   m_dRadius(1.0),
	   m_nIDS(1),
	   m_bJoin(true),
	   m_bTrim(true)
{
}

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

bool MGCurveFilletTool::initiate_tool(){
	MGSelectState::initiate_tool();
	UserPreference& pref = UserPreference::getInstance();
	m_dRadius = pref.GetDoubleValue(upv_Curve_Fillet_Radius);
	m_bJoin = pref.GetBoolValue(upv_Curve_Fillet_Join);
	m_bTrim = pref.GetBoolValue(upv_Curve_Fillet_Trim);

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

bool MGCurveFilletTool::terminate_tool(bool cancel)
{
	UserPreference& pref = UserPreference::getInstance();
	pref.SetDoubleValue(upv_Curve_Fillet_Radius, m_dRadius);
	pref.SetBoolValue(upv_Curve_Fillet_Join, m_bJoin);
	pref.SetBoolValue(upv_Curve_Fillet_Trim, m_bTrim);

	return MGSelectState::terminate_tool(cancel);
}

bool MGCurveFilletTool::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];

	if(m_bTrim){
		std::unique_ptr<mgcalc::MGCurveFilletInfo> info(
			mgcalc::fillet(*curve1,hint1,*curve2,hint2,m_dRadius)
			);

		if(!info.get()){
			// failed...
			m_nIDS = IDS_FAIL_FILLET;
			return false;
		}
		assert(info->m_fillet.get());
		assert(info->m_curve1.get());
		assert(info->m_curve2.get());

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

		// 1. c1, c2 を remove
		// 現状では MGGelPosition が取得できないので後回し
		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){
			std::unique_ptr<MGCurve> result = mgcalc::connect(*info->m_curve1, *info->m_fillet);
			assert(result.get());
			result = mgcalc::connect(*result, *info->m_curve2);
			assert(result.get());

			action->push_back(new CGelAddAction(document(), make_gelpos(result.release())));
		}else{
			MGGelPositions gelps;
			gelps.push_back(make_gelpos(info->m_curve1.release()));
			gelps.push_back(make_gelpos(info->m_fillet.release()));
			gelps.push_back(make_gelpos(info->m_curve2.release()));

			action->push_back(new CGelAddAction(document(), gelps));
		}
		action->Do();
	}else{
		// 一番シンプルなパターン
		std::unique_ptr<MGCurve> fil(
			mgcalc::fillet_arc(*curve1, hint1, *curve2, hint2, m_dRadius));
		if(fil.get()){
			add_object_to_current_group(fil.release());
		}else{
			// failed...
			m_nIDS = IDS_FAIL_FILLET;
			return false;
		}
	}

	return true;
}

bool MGCurveFilletTool::input_param(){
	CGLCurveFilletDlg dlg;

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

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

bool MGCurveFilletTool::OnKeyDown(fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'j':
	case 'J':  // toggle join option
		m_bJoin = !m_bJoin;
		break;
	case 'r':
	case 'R':  // 半径値入力
		if(!input_param()){
			return OnCommandEnd(m_nIDS);
		}
		break;
	case 't':
	case 'T':   // toggle trim option
		m_bTrim = !m_bTrim;
		break;
	default:;
	}
	prompt_message();
	return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGCurveFilletTool::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* curve1 = dynamic_cast<const MGCurve*>(m_curve1.top_object());
		if(curve == curve1){
			// 同じカーブを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 MGCurveFilletTool::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_FILLET, 1, m_dRadius, strTrim, strJoin);
	}else if(m_curve2.is_null()){
		SetStatusMessage(IDS_PROMPT_CURVE_FILLET, 2, m_dRadius, strTrim, strJoin);
	}else{
		SetStatusMessage(IDS_PROMPT_PARAMETER);
	}
}
