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

/**
 * @file SurfKnotInsert.cpp
 * @brief MGSurfKnotInsertTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include "mg/SBRep.h"
#include "mg/RSBRep.h"
#include "mg/Knot.h"
#include "mg/KnotArray.h"
#include "Calc/surface.h"
#include "Calc/mgcalc.h"
#include "fugenView.h"
#include "SurfCmd/SurfKnotInsert.h"

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

// MGSurfKnotInsertTool

MGSurfKnotInsertTool::MGSurfKnotInsertTool(fugenDoc* pDoc)
: MGCommandStateOwner(pDoc, ID_SURFACE_KNOT_INSERT),
m_direction(0){
}

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

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

	// 既に曲面がひとつだけ選択されているような場合は
	// スキップ可能
	MGPickObjects curobj(current_objects());
	if(current_object_is_valid(mgAll_SBRep, curobj) && curobj.size() == 1){
		// m_surfをセットすることをお忘れなく
		m_surf = curobj.front();
		// 点入力からスタート
		set_child_current_command(new MGSurfKnotInsertIPoint(this));
	}else if(current_object_is_valid(mgAll_RSBRep, curobj) && curobj.size() == 1){
		m_surf = curobj.front();
		set_child_current_command(new MGSurfKnotInsertIPoint(this));
	}else{
		// 余計な選択モデルをクリアしてスタート
		clear_pick_object();
		set_child_current_command(new MGSurfKnotInsertSSurf(this));
	}
	return false;
}

bool MGSurfKnotInsertTool::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
){
	if(m_plines.empty()){
		// みなしキャンセル
		return MGCommandStateOwner::OnCommandEnd(3);
	}
	if(!calculate()){
		// failed
		return MGCommandStateOwner::OnCommandEnd(3);
	}
	// コマンド終了
	return MGCommandStateOwner::OnCommandEnd(1);
}

void MGSurfKnotInsertTool::display(mgSysGL& sgl,fugenView* pView){
	const MGSurface* sur= dynamic_cast<const MGSurface*>(m_surf.top_object());
	m_uline.reset();
	m_vline.reset();
	if(m_direction==0||m_direction==2){
		// Vカーブを描く
		m_vline.reset(sur->parameter_curve(true, m_uv[0]));
	}
	if(m_direction==1||m_direction==2){
		// Uカーブを描く
		m_uline.reset(sur->parameter_curve(false, m_uv[1]));
	}
	
	MGColor::get_instance(MGColor::SpringGreen).exec(sgl);
	if(m_uline.get()){ // Uノット描画(Vカーブ描画)
		m_uline->drawWire(sgl);
	}
	if(m_vline.get()){
		m_vline->drawWire(sgl);
	}
	std::for_each(m_plines.begin(), m_plines.end(), mgcalc::CreateDrawFunctor(sgl));

	// 既に決まったパラメータラインの描画
	MGColor::get_instance(MGColor::LightGreen).exec(sgl);
	std::for_each(m_ilines.begin(), m_ilines.end(), mgcalc::CreateDrawFunctor(sgl));
}

bool MGSurfKnotInsertTool::calculate(){
	ASSERT(!m_surf.is_null());

	size_t n=m_uvs.size();
	if(n==0){
		// ノットがまったく指定されていないのでキャンセル扱い
		return false;
	}


	// 先にノットデータを準備する。
	MGKnotArray uarray, varray;
	for(size_t i=0; i<n; i++){
		int dir=m_directions[i];
		if(dir==0 || dir==2)
			uarray.push_back(MGKnot(m_uvs[i][0],1));
		if(dir==1 || dir==2)
			varray.push_back(MGKnot(m_uvs[i][1],1));
	}

	// 実際のサーフェスの型に合わせて、
	// コンストラクターを使い分ける。
	MGSurface* newobj = 0; // ドキュメントに追加する新オブジェクト
	MGSurface* source = dynamic_cast<MGSurface*>(m_surf.top_object());
	MGSBRep* sb = dynamic_cast<MGSBRep*>(source);
	if(sb){
		MGSBRep* sbNew=new MGSBRep(*sb);
		sbNew->addKnots(uarray, varray);
		newobj=sbNew;
	}else{
		MGRSBRep* rsb = dynamic_cast<MGRSBRep*>(source);
		if(rsb){
			MGRSBRep* sbNew=new MGRSBRep(*rsb);
			sbNew->addKnots(uarray, varray);
			newobj=sbNew;
		}
	}

	if(!newobj){
		// ここには来ない
		ASSERT(newobj);
		return false;
	}

	replace_object(source, newobj);
	return true;
}

// MGSurfKnotInsertSSurf

MGSurfKnotInsertSSurf::MGSurfKnotInsertSSurf(MGSurfKnotInsertTool* owner)
: MGSelectState(owner,SINGLE_SELECT,mgAll_Surface){
}

bool MGSurfKnotInsertSSurf::initiate_tool(){
	MGSelectState::initiate_tool();

	// 最初のメッセージ
	SetStatusMessage(IDS_PROMPT_SURFACE);
	return false;
}

bool MGSurfKnotInsertSSurf::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);
	ASSERT(dynamic_cast<MGSurface*>(objs.front().top_object()));

	if(current_object_is_valid(mgAll_SBRep, objs) && objs.size() == 1){
		// OK. 次のstateに入る
		state_owner()->m_surf = objs.front();
		set_sibling_next_command(new MGSurfKnotInsertIPoint(state_owner()));
	}
	else if(current_object_is_valid(mgAll_RSBRep, objs) && objs.size() == 1){
		// OK. 次のstateに入る
		state_owner()->m_surf = objs.front();
		set_sibling_next_command(new MGSurfKnotInsertIPoint(state_owner()));
	}

	return false;
}

// MGSurfKnotInsertIPoint

MGSurfKnotInsertIPoint::MGSurfKnotInsertIPoint(MGSurfKnotInsertTool* owner)
: MGLocateOnObjects(owner,
LOCK_SNAP_ATTRIB){  // 面上限定オプション希望
}

bool MGSurfKnotInsertIPoint::initiate_tool(){
	MGLocateOnObjects::initiate_tool();

	//generate the initial parameter lines.
	MGSurfKnotInsertTool* owner = state_owner();
	const MGSurface& surf=*dynamic_cast<const MGSurface*>(owner->m_surf.top_object());
	set_locate_objectives(&surf);

	const MGKnotVector& tu = surf.knot_vector_u();
	int nu = tu.bdim(), ku=tu.order();
	for(int i =ku; i<=nu-1; i++){
		if(MGREqual(tu[i], tu[i-1])) continue;
		owner->m_ilines.emplace_back(surf.parameter_curve(true, tu[i]));
	}
	const MGKnotVector& tv = surf.knot_vector_v();
	int nv = tv.bdim(), kv=tv.order();
	for(int j =kv; j<=nv-1; j++){
		if(MGREqual(tv[j], tv[j-1])) continue;
		owner->m_ilines.emplace_back(surf.parameter_curve(false, tv[j]));
	}
	return false;
}

bool MGSurfKnotInsertIPoint::OnKeyDown(
	fugenView* pView,
	UINT           nChar,
	UINT           nRepCnt,
	UINT           nFlags
	){
	MGSurfKnotInsertTool* owner = state_owner();

	switch(nChar){
	case 'b':
	case 'B':
		// U と V いっぺんに
		owner->m_direction = 2;
		break;
	case 'u':
	case 'U':
		// U 一定のアイソカーブ
		owner->m_direction=0;
		break;
	case 'v':
	case 'V':
		// V 一定のアイソカーブ
		owner->m_direction=1;
		break;
	default:;
		// デフォルト動作
	}
	return MGLocateState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGSurfKnotInsertIPoint::OnLocated(const MGLocateInfo& info){
	MGSurfKnotInsertTool* owner = state_owner();
	const MGSurface& surf = *dynamic_cast<const MGSurface*>(owner->m_surf.top_object());
	if(info.is_deleted()){
		owner->m_uvs.pop_back();
		owner->m_directions.pop_back();
	}else{
		const MGPosition& uv=snappedParam();
		owner->m_uvs.push_back(uv);
		owner->m_directions.push_back(owner->m_direction);
	}

	// カーブを追加
	owner->m_plines.clear();
	size_t n=owner->m_uvs.size();
	for(size_t i=0; i<n; i++){
		int dir=owner->m_directions[i];
		if(dir==0 || dir==2)
			owner->m_plines.emplace_back(surf.parameter_curve(true, owner->m_uvs[i][0]));
		if(dir==1 || dir==2)
			owner->m_plines.emplace_back(surf.parameter_curve(false, owner->m_uvs[i][1]));
	}

	return false;
}

void MGSurfKnotInsertIPoint::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	MGSurfKnotInsertTool* owner = state_owner();
	owner->m_uv=snappedParam();
	owner->display(sgl,pView);
}

void MGSurfKnotInsertIPoint::prompt_message() const{
	const MGSurfKnotInsertTool* owner = state_owner();
	int dir=owner->m_direction;
	CString strUV;
	if(dir==2){
		strUV = _T("U and V");
	}else if(dir==0){
		strUV = _T("U");
	}else{
		strUV = _T("V");
	}

	CString str;
	str.Format(IDS_PROMPT_SURFACE_KNOT_BOTH, strUV);
	SetStatusMessage(str);
}
