/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
/**
 * @file SurfKnotRemove.cpp
 * @brief SurfKnotRemove.h の実装
 */
#include "stdafx.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "SurfCmd/SurfKnotRemove.h"
#include "mg/SBRep.h"
#include "mg/LBRep.h"
#include "Undo/GelAddAction.h"
#include "Undo/GelReplaceAction.h"
#include "Undo/MultiActions.h"
#include "Undo/IActionManager.h"

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

// 動作を CurveKnotRemove のように修正したいが、
// 面のノットにスナップする機能がないので断念。
// 当面このままで行く。
//
// このコマンドは途中で割り込まれたり何かすると、
// 内部状態が壊れて動作が不安定になるはず。

// MGSurfKnotRemoveTool

MGSurfKnotRemoveTool::MGSurfKnotRemoveTool(fugenDoc* pDoc)
:MGCommandStateOwner(pDoc, ID_SURFACE_KNOT_REMOVE){}

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

bool MGSurfKnotRemoveTool::initiate_tool(){
	MGCommandStateOwner::initiate_tool();
	const MGPickObjects& curobj = current_objects();
	// 初期状態で曲面(MGSBRep)が一本だけ選択されている
	if(curobj.size()==1 && curobj.front().top_object()->type_is(mgAll_SBRep)){
		// 曲面をキープ
		m_surf = curobj.front();
		// ノット指定からスタート
		set_child_current_command(new MGSurfKnotRemoveSKnot(this));
	}else{
		// 余計な選択をクリア
		clear_pick_object();
		// 曲面選択状態からスタート
		set_child_current_command(new MGSurfKnotRemoveSSurf(this));
	}
	return false;
}

bool MGSurfKnotRemoveTool::OnCommandCanceled(UINT nIDS){
	// 後始末をしてからキャンセル
	if(m_surf_tmp.get()){
		document()->GetActionManager()->Undo(); // これで初期状態
	}
	clear_pick_object();
	return MGCommandStateOwner::OnCommandCanceled(nIDS);
}

bool MGSurfKnotRemoveTool::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_surf_tmp.get()){
		document()->GetActionManager()->Undo(); // これで初期状態

		// 結構面倒
		MGSBRep* snew = new MGSBRep(*m_surf_tmp);
		replace(m_surf, snew);
	}
	// コマンド終了
	return MGCommandStateOwner::OnCommandEnd(nIDS);
}

// 曲面ノット除去コマンドの場合
// ノット位置にパラメータラインを描画することによって
// ノットを示す
bool MGSurfKnotRemoveTool::calc_knot(){
	ASSERT(m_ucurve.empty());
	ASSERT(m_vcurve.empty());

	MGSBRep* surf = dynamic_cast<MGSBRep*>(m_surf.top_object());
	ASSERT(surf);

	MGGelPositions gelps;
	calc_param_line(gelps, surf);

	// 3. update document
	add_object_to_document(gelps);
	return true;
}

void MGSurfKnotRemoveTool::calc_param_line(MGGelPositions& gelps, const MGSBRep* surf){
	// 1. extract u-const isoparameteric curve. (V-curve)

	const MGKnotVector& tu = surf->knot_vector_u();
	int bdimu = tu.bdim();
	m_vcurve.clear();
	MGGroup* rootgroup=doc_root();
	for(int i = surf->order_u(); i < bdimu; i++){
		// includes multi-knots
		MGGelPosition gelp(rootgroup,surf->parameter_curve(true, tu[i]));
		m_vcurve.push_back(MGPickObject(gelp));
		gelps.push_back(gelp);
	}

	// 2. v-const (U-curve) extraction

	const MGKnotVector& tv = surf->knot_vector_v();
	int bdimv = tv.bdim();
	m_ucurve.clear();
	for(int i = surf->order_v(); i < bdimv; i++){
		// includes multi-knots
		MGGelPosition gelp(rootgroup,surf->parameter_curve(false, tv[i]));
		m_ucurve.push_back(MGPickObject(gelp));
		gelps.push_back(gelp);
	}
}

// po がピックされたパラメータラインを意味する
bool MGSurfKnotRemoveTool::remove_knot(MGPickObject& po){
	const MGSBRep* surf = (m_surf_tmp.get()) ?
		m_surf_tmp.get():dynamic_cast<MGSBRep*>(m_surf.top_object());
	ASSERT(surf);
	auto c2 = dynamic_cast<const MGCurve*>(po.top_object());

	int ncurvev = (int)m_vcurve.size();
	for(int i = 0; i < ncurvev; i++){
		auto c1 = dynamic_cast<const MGCurve*>(m_vcurve[i].top_object());
		if(c1==c2){
			return remove_knot(i + surf->order_u(), true);
		}
	}

	int ncurveu = (int)m_ucurve.size();
	for(int i = 0; i < ncurveu; i++){
		auto c1 = dynamic_cast<const MGCurve*>(m_vcurve[i].top_object());
		if(c1==c2){
			return remove_knot(i + surf->order_v(), false);
		}
	}

	return false;
}

bool MGSurfKnotRemoveTool::remove_knot(int id, bool bU){
	const MGSBRep* surf = (m_surf_tmp.get())
		? m_surf_tmp.get()
		: dynamic_cast<MGSBRep*>(m_surf.top_object());
	ASSERT(surf);

	MGSBRep* snew = new MGSBRep(*surf);
	double dummy;
	int ret = snew->remove_knot_one(-1., id, dummy, bU);

	MGGelPosition gelold(m_surf);
	MGGelPosition gelp(doc_root(),snew);

	document()->GetActionManager()->Undo();
	CMultiActions* ma = new CMultiActions(document());
	ma->push_back(new CGelReplaceAction(document(), MGGelPositions(gelold), gelp));

	// ここから先数行はcalc_knot()
	
	MGGelPositions gelps;
	calc_param_line(gelps, snew);
	ma->push_back(new CGelAddAction(document(), gelps));
	ma->Do();

	// m_surf_tmpを更新
	m_surf_tmp.reset(new MGSBRep(*snew));
	return true;
}

// MGSurfKnotRemoveSSurf

MGSurfKnotRemoveSSurf::MGSurfKnotRemoveSSurf(MGSurfKnotRemoveTool* owner)
:MGSelectState(owner, SINGLE_SELECT, mgAll_SBRep){
}

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

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

bool MGSurfKnotRemoveSSurf::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()));
	MGSBRep* surf = dynamic_cast<MGSBRep*>(objs.front().top_object());
	ASSERT(surf);

	// 曲面をキープ
	state_owner()->m_surf = objs.front();
	
	// 次のstateへ進む
	set_sibling_next_command(new MGSurfKnotRemoveSKnot(state_owner()));
	return false;
}

// MGSurfKnotRemoveSKnot

MGSurfKnotRemoveSKnot::MGSurfKnotRemoveSKnot(MGSurfKnotRemoveTool* owner)
:MGSelectState(owner, SINGLE_SELECT, mgAll_LBRep){
}

bool MGSurfKnotRemoveSKnot::initiate_tool(){
	ASSERT(state_owner());
	ASSERT(!state_owner()->m_surf.is_null());

	MGSelectState::initiate_tool();

	// ノットを計算
	state_owner()->calc_knot();

	// メッセージを更新
	SetStatusMessage(IDS_PROMPT_KNOT);
	return false;
}

bool MGSurfKnotRemoveSKnot::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<MGLBRep*>(objs.front().top_object()));

	// removal
	state_owner()->remove_knot(objs.front());
	// コマンドは終わらない
	return false;
}
