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

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

#include "stdafx.h"
#include "mg/Straight.h"
#include "mg/FSurface.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "EvalCmd/EvalSurfUVDisplay.h"

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

namespace{
	static bool point_create=false;
	static bool uv_normalize=false;
}

MGEvalSurfUVDisplayTool::MGEvalSurfUVDisplayTool(fugenDoc* pDoc)
:MGCommandStateOwner(pDoc, ID_EVAL_UV)
,m_bCreate(point_create),m_bNormalize(uv_normalize),m_surf(0){
}

MGEvalSurfUVDisplayTool::~MGEvalSurfUVDisplayTool(){
}

bool MGEvalSurfUVDisplayTool::can_break_into() const{
	const MGPickObjects& curobj = current_objects();
	MGPickObjects surfs = curobj.select(mgAll_FSurface);
	return surfs.size();
}

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

bool MGEvalSurfUVDisplayTool::initiate_tool(){
	MGCommandStateOwner::initiate_tool();

	// TODO: m_bCreate と m_bNormalize を
	// UserPreference からロードする。

	fugenDoc* doc=document();
	MGPickObjects& curobj = current_objects();
	curobj.reset_objects(mgAll_FSurface);
	// 初期状態で曲面が一つ選択されている
	if(curobj.size() && curobj.front().leaf_object()->type_is(mgAll_FSurface)){
		// 曲面をセット
		MGPickObject srfpobj = curobj.front();
		m_surf = srfpobj.leaf_object()->fsurface();
		// UV値指定stateからスタート
		set_current_object(srfpobj,true);
		set_child_current_command(new MGSurfUVLocate(this, m_surf));
	}else{
		// 余計な選択をクリア
		clear_pick_object();
		// 曲面選択stateからスタート
		set_child_current_command(new MGSurfUVDisplaySSurf(this));
	}
	return false;
}

bool MGEvalSurfUVDisplayTool::terminate_tool(bool cancel){
	// TODO: m_bCreate と m_bNormalize を
	// UserPreference にセーブする。
	
	//Save the last state.
	point_create=m_bCreate;
	uv_normalize=m_bNormalize;
	return MGCommandStateOwner::terminate_tool(cancel);
}

void MGEvalSurfUVDisplayTool::ToggleCreateMode(){
	if(is_breaking_command()){
		m_bCreate = false;
	}else{
		m_bCreate = !m_bCreate;
	}
}

//カーソルと面の交点を求めてメンバデータにセットする
//出力：交点の座標ダイアログのデータにセット
bool MGEvalSurfUVDisplayTool::calculate(const MGLocateInfo& info
){
	MGSnapPositions::snap_kind sk=info.snap_kind();
	if(sk==MGSnapPositions::DELETE_POINT)
		return false;

	MGPosition uv;//To store the parameter value.
	if(sk==MGSnapPositions::centerpos || sk==MGSnapPositions::vertexpos
		|| sk==MGSnapPositions::ON_SURFACE){
		uv=info.parameter();
	}else{
		return false;
	}

	// 座標値を出力
	CString str;
	if(IsNormalizeMode()){
		const MGInterval& urange = m_surf->box_param2()[0];
		const MGInterval& vrange = m_surf->box_param2()[1];
		double unorm = (uv[0] - urange.low().value())/urange.length().value();
		double vnorm = (uv[1] - vrange.low().value())/vrange.length().value();
		m_uv = MGPosition(unorm, vnorm);
		str.Format(IDS_PROMPT_EVAL_UV_POINT_RESULT, unorm, vnorm);
	}else{
		m_uv = uv;
		str.Format(IDS_PROMPT_EVAL_UV_POINT_RESULT, uv[0], uv[1]);
	}
	putInOutputWindow(str);

	// 点を作成
	if(IsCreateMode() && !is_breaking_command()){
		add_object_to_current_group(new MGPoint(m_surf->eval(uv)));
	}
	m_pos = m_surf->eval(uv);
	return true;
}

//Invoked when command is to terminate as a nomal end.
bool MGEvalSurfUVDisplayTool::OnCommandEnd(
	UINT nIDS,	//=0: erase the current message, and display no messages.
				//=1: display "xxxx" normally end.
				//otherwise: nIDS is a string id, and load the message from string table to display.
	bool erase_temporary_display
){
	return MGCommandStateOwner::OnCommandEnd(1,false);
}

void MGEvalSurfUVDisplayTool::prompt_message()const{
	bool bBreaking = is_breaking_command();
	if(is_breaking_command()){
		CString strYesNoN;
		strYesNoN.LoadString(IsNormalizeMode() ? IDS_YES : IDS_NO);

		if(!m_surf){
			SetStatusMessage(IDS_PROMPT_EVAL_UV_POINT_SEL_BRK, strYesNoN);
		}else{
			SetStatusMessage(IDS_PROMPT_EVAL_UV_POINT_LOCATE_BRK, strYesNoN);
		}
	}else{
		CString strYesNoC, strYesNoN;
		strYesNoC.LoadString(IsCreateMode() ? IDS_YES : IDS_NO);
		strYesNoN.LoadString(IsNormalizeMode() ? IDS_YES : IDS_NO);

		if(!m_surf){
			SetStatusMessage(IDS_PROMPT_EVAL_UV_POINT_SEL, strYesNoC, strYesNoN);
		}else{
			SetStatusMessage(IDS_PROMPT_EVAL_UV_POINT_LOCATE, strYesNoC, strYesNoN);
		}
	}
}

//MGSurfUVDisplaySSurf
MGSurfUVDisplaySSurf::MGSurfUVDisplaySSurf(MGEvalSurfUVDisplayTool* owner)
	 : MGSelectState(owner, MGSelectState::SINGLE_SELECT, mgAll_2Manifold){
}

bool MGSurfUVDisplaySSurf::initiate_tool(){
	MGSelectState::initiate_tool();
	state_owner()->prompt_message();
	return false;
}

bool MGSurfUVDisplaySSurf::OnKeyDown(
	fugenView* window,
	UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'c':
	case 'C':
		state_owner()->ToggleCreateMode();
		state_owner()->prompt_message();
		break;
	case 'n':
	case 'N':
		state_owner()->ToggleNormalizeMode();
		state_owner()->prompt_message();
		break;
	default:;
	}
	return MGSelectState::OnKeyDown(window, nChar, nRepCnt, nFlags);
}

bool MGSurfUVDisplaySSurf::OnSelected(
	fugenView* window,//The fugenView pointer where point input event took place.
	MGPickObjects&	objs,	//selected objects at this selection operation.
	MGPickObjects&	unselected_objects	//unselected objects at this selection operation.
		//unselected_objects.size()>=1 only when the already selected objects are selected
		//when add mode is set(or when operation is done with a crtl key pressed).
){
	ASSERT(objs.size() == 1);

	state_owner()->m_surf=dynamic_cast<const MGFSurface*>(objs.front().leaf_object()->fsurface());

	// 次のstateへ進む
	set_sibling_next_command(new MGSurfUVLocate(state_owner(),state_owner()->m_surf));
	MGSelectState::OnSelected(window,objs,unselected_objects);
	return false;
}

// MGSurfUVLocate
MGSurfUVLocate::MGSurfUVLocate(
MGEvalSurfUVDisplayTool* owner, const MGFSurface* surf
):MGLocateOnObjects(owner,UNLOCK_SNAP_ATTRIB,NO_RUBBER,NO_IPDRAW){
	turn_on_vertex();
	turn_on_center();
}

bool MGSurfUVLocate::initiate_tool(){
	const MGObject* surf = state_owner()->m_surf->object_pointer();
	set_locate_objectives(surf);
	MGLocateOnObjects::initiate_tool();
	return false;
}

bool MGSurfUVLocate::OnKeyDown(
	fugenView* window,
	UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'c':
	case 'C':
		state_owner()->ToggleCreateMode();
		break;
	case 'n':
	case 'N':
		state_owner()->ToggleNormalizeMode();
		break;
	default:;
	}
	return MGLocateOnObjects::OnKeyDown(window, nChar, nRepCnt, nFlags);
}

//MGLocateOnObjectsのサブクラスはこれを実装しなければならない
bool MGSurfUVLocate::OnLocated(const MGLocateInfo& info){
	state_owner()->calculate(info);

	draw_temporary();
	return false;
}

void MGSurfUVLocate::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	MGEvalSurfUVDisplayTool* owner=state_owner();
	const MGPosition& pos=owner->m_pos;
	if(pos.is_null()){
		return;
	}

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

	const MGPosition& uv=owner->m_uv;
	CString str;
	str.Format(IDS_EVAL_UV, uv[0], uv[1]);
	sgl.drawString(str, pos);
}
void MGSurfUVLocate::prompt_message()const{
	state_owner()->prompt_message();
}

