/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/**
 * @file SplineCPointTool.cpp
 * @brief MGSplineCPointTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include "mg/LBRep.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "Misc/UserPreference.h"
#include "GLInputNumberDlg.h"
#include "CurveCmd/SplineCPointTool.h"

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

/**
 *  @brief  制御点を与えて曲線を生成
 *  @param  cv      制御点列
 *  @param  degree  次数
 *  @param  close   閉曲線にするかどうか, close=trueが入力されたとき終点＝始点と
 *  @param  sharp   close == true のとき、閉曲線の端点を尖らせてもよいか。
 *                  要するに内部的には開曲線になる。
 */
std::unique_ptr<MGCurve> create_curve_from_cv(
	const std::vector<MGPosition>& cv,
	int                            degree,
	bool                           close,
	bool                           sharp
){
	size_t np1=cv.size();
	if(degree<1 || np1<2 || (close&&(np1<3)))
		return std::unique_ptr<MGCurve>(nullptr);

	const MGPosition& P0=cv[0];
	MGBPointSeq bp(cv);
	if(close){
		int n=bp.length();
		bp.insert_at(n, P0);
		if(!sharp){
			MGPosition Pm1=P0-(cv[1]-P0);
			bp.insert_at(n, Pm1);
		}
	}

	int order = degree + 1;
	int npoint=bp.length();
	if(order > npoint)
		order=npoint;

	MGKnotVector t(order, npoint, 0.);
	auto curve(std::make_unique<MGLBRep>());
	curve->buildLBRepFromMemberData(std::move(t), std::move(bp));
	return curve;
}

// MGSplineCPointTool

MGSplineCPointTool::MGSplineCPointTool(fugenDoc* pDoc)
: MGLocateState(pDoc, ID_CURVE_SPLINE_CPOINT, LINE_RUBBER, POINTLINE_IPDRAW),
m_nIDS(1), m_nDegree(3), m_bClose(false), m_bSharp(false){
}

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

bool MGSplineCPointTool::initiate_tool(){
	MGLocateState::initiate_tool();
	const UserPreference& pref = UserPreference::getInstance();
	m_nDegree = pref.GetIntValue(upv_Curve_SplineCPoint_Degree);
	m_bSharp = pref.GetBoolValue(upv_Curve_SplineCPoint_Sharp);
	clear_pick_object();
	return false;
}

bool MGSplineCPointTool::terminate_tool(bool cancel){
	UserPreference& pref = UserPreference::getInstance();
	pref.SetIntValue(upv_Curve_SplineCPoint_Degree, m_nDegree);
	pref.SetBoolValue(upv_Curve_SplineCPoint_Sharp, m_bSharp);

	return MGLocateState::terminate_tool(cancel);
}

bool MGSplineCPointTool::calculate(){
	if(locates().size()<=1){
		// cancel扱い
		m_nIDS = 1;
		return false;
	}

	const LInfoVec& linfos=locates();
	std::vector<MGPosition> iposes;
	extract_points_world(linfos,iposes);
	std::unique_ptr<MGCurve> curve=create_curve_from_cv(iposes,m_nDegree,m_bClose,m_bSharp);
	if(!curve.get()){
		m_nIDS = IDS_FAIL_GENERATE_CURVE;
		return false;
	}

	// ドキュメントを変更
	add_object_to_current_group(curve.release());
	return true;
}

void MGSplineCPointTool::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	const LInfoVec& linfos=locates();
	std::vector<MGPosition> iposes;
	extract_points_world(linfos,iposes);
	iposes.push_back(cursor());
	std::unique_ptr<MGCurve> curve=create_curve_from_cv(iposes,m_nDegree,m_bClose,m_bSharp);

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

bool MGSplineCPointTool::OnKeyDown(fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	size_t np = locates().size();	
	switch(nChar){
	case 'c':
	case 'C':
		// 'Close'
		if(np>2){
			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 's':
	case 'S':
		// 'Sharp'
		if(np > 1){
			m_bSharp = !m_bSharp;
		}
		break;
	case VK_RETURN:
		return calculate() ? OnCommandEnd(1) : OnCommandEnd(m_nIDS);
	default:;
	}
	return MGLocateState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGSplineCPointTool::OnLocated(const MGLocateInfo& info){
	return false;
}

void MGSplineCPointTool::prompt_message() const{
	switch(locates().size()){
	case 0:
		SetStatusMessage(IDS_PROMPT_CURVE_CPOINT_0, m_nDegree);
		break;
	case 1:
		SetStatusMessage(IDS_PROMPT_CURVE_CPOINT_1, m_nDegree);
		break;
	default:
		{
			CString strYesNo;
			strYesNo.LoadString(m_bSharp ? IDS_YES : IDS_NO);
			SetStatusMessage(IDS_PROMPT_CURVE_CPOINT, m_nDegree, strYesNo);
		}
		break;
	}
}
