/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/* ***************************************************** */
/********************************************************************/
/**
 * @file SplineInterpTool.cpp
 * @brief クラス MGSplineInterpTool の実装
 */
#include "stdafx.h"
#include "mg/LBRep.h"
#include "mg/LBRepEndC.h"
#include "Calc/curve.h"
#include "Calc/mgcalc.h"
#include "fugenView.h"
#include "GLInputNumberDlg.h"
#include "Misc/UserPreference.h"
#include "CurveCmd/SplineInterpTool.h"

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

// MGSplineInterpTool

namespace
{
	///Set endc from info.
	void set_endc(
		const MGLocateInfo& info,//input MGLocateInfo
		MGLBRepEndC& endc//end condition will be set.
	){
		endc.initialize();
		MGSnapPositions::snap_kind sk=info.snap_kind();
		if(sk==MGSnapPositions::endpos || sk==MGSnapPositions::nearpos){
			const MGCurve* cv=static_cast<const MGCurve*>(info.object());
			double t=info.curve_parameter();
			MGUnit_vector deriv(cv->eval(t,1));
			endc.set_1st(deriv);
		}
	}
}

MGSplineInterpTool::MGSplineInterpTool(fugenDoc* pDoc)
	 : MGLocateState(pDoc,ID_CURVE_SPLINE,NO_RUBBER,POINT_IPDRAW),
	   m_nIDS(1), 
	   m_nDegree(3),
	   m_1stDerivS(false),
	   m_1stDerivE(false),
	   m_bClose(false),
	   m_bSharp(false){
}

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

bool MGSplineInterpTool::initiate_tool(){
	MGLocateState::initiate_tool();
	const UserPreference& pref = UserPreference::getInstance();
	m_nDegree = pref.GetIntValue(upv_Curve_SplineInterp_Degree);
	m_bSharp = pref.GetBoolValue(upv_Curve_SplineInterp_Sharp);
	return false;
}

bool MGSplineInterpTool::terminate_tool(bool cancel)
{
	UserPreference& pref = UserPreference::getInstance();
	pref.SetIntValue(upv_Curve_SplineInterp_Degree, m_nDegree);
	pref.SetBoolValue(upv_Curve_SplineInterp_Sharp, m_bSharp);

	return MGLocateState::terminate_tool(cancel);
}

bool MGSplineInterpTool::get_curve(const std::vector<MGPosition>& ip){
	size_t n=ip.size();
	if(n<=1){
		// cancel扱い
		m_nIDS = 1;
		return false;
	}

	// 計算開始
	int error = 0;
	int order = m_nDegree + 1;
	MGBPointSeq bp(ip);
	if(m_bClose){
		bp.insert_at(bp.length(), ip.front());
		bool periodic = (m_bClose && !m_bSharp) ? true : false;
		MGLBRep* lb=new MGLBRep;
		lb->buildByInterpolation(bp, order, periodic);
		m_curve.reset(lb);
	}else{
		MGNDDArray tau(bp);
		if(m_cndStart.cond()==MGENDC_1D){
			MGVector V01=ip[1]-ip[0];
			MGVector deriS=m_cndStart.first();
			if(V01%deriS < 0.)
				m_cndStart.set_1st(-deriS);
		}
		if(m_1stDerivE){
			set_endc(m_linfo,m_cndEnd);
			if(m_cndEnd.cond()==MGENDC_1D){
				MGVector Vn=ip[n-1]-ip[n-2];
				MGVector deriE=m_cndEnd.first();
				if(Vn%deriE < 0.)
					m_cndEnd.set_1st(-deriE);
			}
		}
		MGLBRep* lb=new MGLBRep;
		lb->buildByInterpolationEC(m_cndStart, m_cndEnd, tau, bp, order);
		m_curve.reset(lb);
	}
	if(error){
		m_curve.reset();
		m_nIDS = IDS_FAIL_GENERATE_CURVE;
		return false;
	}
	return true;
}

bool MGSplineInterpTool::calculate(){
	const LInfoVec& linfos=locates();
	std::vector<MGPosition> iposes;
	extract_points_world(linfos,iposes);
	if(get_curve(iposes)){
		add_object_to_current_group(m_curve.release());// ドキュメントを変更
		return true;
	}else
		return false;
}

void MGSplineInterpTool::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	const LInfoVec& linfos=locates();
	if(linfos.size()){
		std::vector<MGPosition> iposes;
		extract_points_world(linfos,iposes);
		iposes.push_back(cursor());
		get_curve(iposes);
	}else{
		m_curve.reset();
	}

	if(m_curve.get()){
		MGColor::get_instance(MGColor::SpringGreen).exec(sgl);
		m_curve->drawWire(sgl);
	}
}

bool MGSplineInterpTool::OnKeyDown(fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	size_t np = locates().size();
	
	switch(nChar){
	case 'c':
	case 'C':
		if(np > 1){// 'Close'
			m_bClose = true;
			return calculate() ? OnCommandEnd(1) : OnCommandEnd(m_nIDS);
		}
		break;
	case 'd':
	case 'D':
		// 次数の指定
		{
			CString strCaption;
			VERIFY(strCaption.LoadString(IDS_CAPTION_DEGREE));

			CGLInputNumberDlg dlg(CString(), strCaption);

			dlg.SetValue(m_nDegree);
			if(IDOK == dlg.DoModal()){
				int n = dlg.GetValue();
				if(n >= 2){
					m_nDegree = n;
				}
			}
			pView->SetFocus();
		}
		break;
	case 'e':
	case 'E':
		if(np >= 1){
			//m_1stDerivEをセット
			m_1stDerivE=!m_1stDerivE;

			// おまけに端点・接点スナップも有効化する。
			if(m_1stDerivE){
				turn_on_extract_end();
				turn_on_extract_near();
			}
		}
		break;
	case 's':
	case 'S':
		if(np==0){
			//m_1stDerivSをセット
			m_1stDerivS=!m_1stDerivS;

			// おまけに接点スナップも有効化する。
			if(m_1stDerivS){
				turn_on_extract_near();
				turn_on_extract_end();
			}
		}else{
			// 'Sharp'
			m_bSharp = !m_bSharp;
		}
		break;
	case VK_RETURN:
		return calculate() ? OnCommandEnd(1) : OnCommandEnd(m_nIDS);
	default:;
	}
	return MGLocateState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGSplineInterpTool::OnLocated(const MGLocateInfo& info){
	size_t np = locates().size();
	if(np == 0){
		m_1stDerivS = false;
		m_cndStart.initialize();
	}else if(np==1){
		if(!info.is_deleted()){
			if(m_1stDerivS){
				//If this is the 1st input.
				set_endc(info,m_cndStart);
			}else{
				m_cndStart.initialize();
			}
		}
	}else
		m_linfo=info;//store for the last point.

	if(m_1stDerivE && np>1){
		// 終点を指定したとみなして、計算処理に入る。
		return calculate() ? OnCommandEnd(1) : OnCommandEnd(m_nIDS);
	}

	return false;
}

void MGSplineInterpTool::prompt_message()const{
	size_t np = locates().size();
	switch(np){
	case 0:
		if(m_1stDerivS){
			SetStatusMessage(IDS_PROMPT_CURVE_INTERP_S);
		}else{
			SetStatusMessage(IDS_PROMPT_CURVE_INTERP_0, m_nDegree);
		}
		break;
	case 1:
		if(m_1stDerivE){
			SetStatusMessage(IDS_PROMPT_CURVE_INTERP_E);
		}else{
			SetStatusMessage(IDS_PROMPT_CURVE_INTERP_1, m_nDegree);
		}
		break;
	default:
		if(m_1stDerivE){
			SetStatusMessage(IDS_PROMPT_CURVE_INTERP_E);
		}else{
			CString strYesNo;
			strYesNo.LoadString(m_bSharp ? IDS_YES : IDS_NO);
			SetStatusMessage(IDS_PROMPT_CURVE_INTERP, m_nDegree, strYesNo);
		}
		break;
	}
}
