/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/

// EvalPointFromUV.cpp: MGEvalPointFromUVTool クラスのインプリメンテーション

#include "stdafx.h"
#include "mg/Point.h"
#include "fugen.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "EvalCmd/EvalPointFromUV.h"
#include "GLFromUVDlg.h"
#include "Misc/UserPreference.h"

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

// MGEvalPointFromUVTool

MGEvalPointFromUVTool::MGEvalPointFromUVTool(fugenDoc* pDoc)
 : MGSelectState(pDoc, ID_EVAL_POINTS_FROM_UV,
		 MGSelectState::SINGLE_SELECT, // single selection
		 mgAll_FSurface),
	   m_bCreatePoint(false), m_bNormU(false), m_bNormV(false)
{}

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

bool MGEvalPointFromUVTool::initiate_tool(){
	MGSelectState::initiate_tool();
	const UserPreference& pref = UserPreference::getInstance();
	m_bCreatePoint = pref.GetBoolValue(upv_Eval_PointFromUV_CreatePoint);
	m_bNormU = pref.GetBoolValue(upv_Eval_PointFromUV_NormalizeU);
	m_bNormV = pref.GetBoolValue(upv_Eval_PointFromUV_NormalizeV);

	const MGPickObjects& cobjs=current_objects();
	if(is_breaking_command()){
		MGPickObjects surfaces;
		cobjs.select_fsurfaces(surfaces);
		if(!surfaces.empty()){
			calculate(current_objects().front());
		}
		return true;
	}else if(resetCurrentObjects(mgAll_FSurface)){
		calculate(current_objects().front());
		return OnCommandEnd(1);
	}else{
		// 余分な選択を解除
		clear_pick_object();
		CString strYesNo;
		strYesNo.LoadString(m_bCreatePoint ? IDS_YES : IDS_NO);
		SetStatusMessage(IDS_PROMPT_POINTS_FROM_UV, strYesNo);
		return false;
	}
}

bool MGEvalPointFromUVTool::terminate_tool(bool cancel)
{
	UserPreference& pref = UserPreference::getInstance();
	pref.SetBoolValue(upv_Eval_PointFromUV_CreatePoint, m_bCreatePoint);
	pref.SetBoolValue(upv_Eval_PointFromUV_NormalizeU, m_bNormU);
	pref.SetBoolValue(upv_Eval_PointFromUV_NormalizeV, m_bNormV);

	return MGSelectState::terminate_tool(cancel);
}

namespace
{
	// 正規化されている曲面パラメータを元に戻す
	void unnormalize_param(
		const MGFSurface& surf,
		double& t,
		bool    norm_u
		){
		// MGInterval::interpolate() がキモである
		t=surf.box_param2()[norm_u ? 0 : 1].interpolate(t);
	}

	// 指定された uv 値における曲面上の点を返す
	MGPosition evaluate(
		const MGFSurface& surf,
		double            u,
		double            v,
		bool              norm_u,
		bool              norm_v
		){
		// normalize の逆をやる
		if(norm_u)
			unnormalize_param(surf, u, true);

		// normalize の逆をやる
		if(norm_v)
			unnormalize_param(surf, v, false);

		// 計算結果を戻す
		return surf.eval(u, v);
	}
}

void MGEvalPointFromUVTool::calculate(const MGPickObject& pobj){
	MGPosition uv;
	if(!input_param(pobj,uv)){
		// cancel
		return;
	}
	
	// 処理中メッセージ
	SetStatusMessage(IDS_PROMPT_COMPUTE);
	CWaitCursor sandglass;

	// 位置を計算する
	const MGFSurface* surf = dynamic_cast<const MGFSurface*>(pobj.top_object());
	ASSERT(surf);

	MGPosition pos = evaluate(*surf, uv[0], uv[1], m_bNormU, m_bNormV);

	// 必要ならドキュメントを変更
	if(!pos.is_null()){
		if(m_bCreatePoint && !is_breaking_command())
			add_object_to_current_group(new MGPoint(pos));
		
		// 結果を表示
		m_pos = pos;
		draw_temporary();		

		CString str;
		str.Format(IDS_FORMAT_COORD_3D, pos[0], pos[1], pos[2]);
		putInOutputWindow(str);
	}
}

void MGEvalPointFromUVTool::do_make_temporary_display(mgSysGL& sgl,fugenView* pView)
{
	if(m_pos.is_null()){
		return;
	}

	const MGColor& clr = MGColor::get_instance(MGColor::Magenta);
	clr.exec(sgl);
	sgl.drawPoint(m_pos[0], m_pos[1], m_pos[2]);

	CString str;
	str.Format(IDS_FORMAT_COORD_3D, m_pos[0], m_pos[1], m_pos[2]);
	sgl.drawString(str, m_pos);
}

bool MGEvalPointFromUVTool::input_param(const MGPickObject& pobj, MGPosition& uv){
	SetStatusMessage(IDS_PROMPT_PARAMETER);
	
	const MGFSurface* surf = pobj.top_object()->fsurface();
	ASSERT(surf);

	// パラメータの指定が妙に面倒
	const MGBox& range = surf->box_param2();

	CGLFromUVDlg dlg;
	dlg.SetCaption(range[0].low_point(), range[0].high_point(), true);
	dlg.SetCaption(range[1].low_point(), range[1].high_point(), false);
	dlg.SetLimit(
		range[0].low_point(),
		range[0].high_point(),
		range[1].low_point(),
		range[1].high_point()
		);
	dlg.m_bNormU = m_bNormU;
	dlg.m_bNormV = m_bNormV;

	bool bOK = (IDOK == dlg.DoModal());
	if(bOK){
		// ダイアログボックスの指示をすべて記憶
		m_bNormU   = (dlg.m_bNormU == TRUE);
		m_bNormV   = (dlg.m_bNormV == TRUE);
		uv= MGPosition(dlg.m_dU,dlg.m_dV);
	}
	return bOK;
}

bool MGEvalPointFromUVTool::OnKeyDown(fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'c':
	case 'C':
		// 点生成オプションを切り替える
		m_bCreatePoint = !m_bCreatePoint;

		// プロンプトを更新（手抜き）
		{
			CString strYesNo;
			strYesNo.LoadString(m_bCreatePoint ? IDS_YES : IDS_NO);
			SetStatusMessage(IDS_PROMPT_POINTS_FROM_UV, strYesNo);
		}
		break;
	default:;
	}
	return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGEvalPointFromUVTool::OnSelected(
	fugenView* pView,
	MGPickObjects& curobj,
	MGPickObjects& rejobj
){
	ASSERT(curobj.size() == 1);
	ASSERT(dynamic_cast<MGFSurface*>(curobj.front().top_object()));
	// 計算して終了
	calculate(current_objects().front());
	return OnCommandEnd(1);
}
