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

/**
 * @file CurveKnotRemove.cpp
 * @brief MGCurveKnotRemoveTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include <memory>
#include "mg/Point.h"
#include "mg/LBRep.h"
#include "Calc/mgcalc.h"
#include "fugenView.h"
#include "CurveCmd/CurveKnotRemove.h"

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

// MGCurveKnotRemoveTool

MGCurveKnotRemoveTool::MGCurveKnotRemoveTool(fugenDoc* pDoc)
:MGCommandStateOwner(pDoc, ID_CURVE_REMOVE_KNOT){
}

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

bool MGCurveKnotRemoveTool::initiate_tool(){
	MGCommandStateOwner::initiate_tool();
	fugenDoc* doc=document();
	const MGPickObjects& curobj = current_objects();
	// 初期状態で曲線(MGLBRep)が一本だけ選択されている
	if(curobj.size() == 1
	   && curobj.front().top_object()->type_is(mgAll_LBRep)){
		// 曲線をキープ
		m_curve = curobj.front();
		// ノット指定stateからスタート
		set_child_current_command(new MGCurveKnotRemoveSKnot(this));
	}else{
		// 余計な選択をクリア
		clear_pick_object();
		// 曲線選択stateからスタート
		set_child_current_command(new MGCurveKnotRemoveSCurve(this));
	}
	return false;
}

bool MGCurveKnotRemoveTool::OnCommandCanceled(UINT nIDS){
	// 後始末をしてからキャンセル
	clear_pick_object();
	return MGCommandStateOwner::OnCommandCanceled(nIDS);
}

bool MGCurveKnotRemoveTool::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_knotIDs.empty())
		replace(m_curve,remove_knot());
	erase_temporary();

	return MGCommandStateOwner::OnCommandEnd(nIDS);
}

//m_pointsに格納されたノットを除去する
MGLBRep* MGCurveKnotRemoveTool::remove_knot(){
	MGLBRep* c = dynamic_cast<MGLBRep*>(m_curve.top_object()->clone());
	ASSERT(c);

	//選択されたノットをリムーブする
	//ノットが除去されるとIDが振りなおされる為
	//一番後の要素のIDから除去していく
	//そうすれば，IDが振りなおされても影響を受けない．
	std::sort(m_knotIDs.begin(), m_knotIDs.end());
	m_knotIDs.erase(
		std::unique(m_knotIDs.begin(), m_knotIDs.end()),
		m_knotIDs.end());

	std::vector<int>::reverse_iterator
		first = m_knotIDs.rbegin(), last = m_knotIDs.rend();
	for(; first != last; ++first){
		double tol;
		int n;
		c->remove_knot_one(-1., *first, tol, n);
	}
	return c;
}

const MGPickObject& MGCurveKnotRemoveTool::curve() const{
	return m_curve;
}

void MGCurveKnotRemoveTool::set_curve(const MGPickObject& curve){
	m_curve = curve;
}

void MGCurveKnotRemoveTool::insert_knot(int nId){
	m_knotIDs.push_back(nId);
}

void MGCurveKnotRemoveTool::cancel_last_insertion(){
	if(m_knotIDs.empty()){
		return;
	}
	m_knotIDs.pop_back();
}

// MGCurveKnotRemoveSCurve

MGCurveKnotRemoveSCurve::MGCurveKnotRemoveSCurve(MGCurveKnotRemoveTool* owner)
:MGSelectState(owner, SINGLE_SELECT, mgAll_LBRep){
}

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

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

bool MGCurveKnotRemoveSCurve::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<MGCurve*>(objs.front().top_object()));
	MGLBRep* curve = dynamic_cast<MGLBRep*>(objs.front().top_object());
	ASSERT(curve);

	// 曲線をキープ
	state_owner()->set_curve(objs.front());
	
	// 次のstateへ進む
	set_sibling_next_command(new MGCurveKnotRemoveSKnot(state_owner()));
	return false;
}

// MGCurveKnotRemoveSKnot

MGCurveKnotRemoveSKnot::MGCurveKnotRemoveSKnot(MGCurveKnotRemoveTool* owner)
:MGLocateOnObjects(owner,UNLOCK_SNAP_ATTRIB,NO_RUBBER,NO_IPDRAW){
	turn_on_knot();
}

bool MGCurveKnotRemoveSKnot::initiate_tool(){
	MGLocateOnObjects::initiate_tool();
	set_locate_objectives(state_owner()->curve().top_object());

	// 選択カーブの最初と最後を除くノット位置に
	// MGPointを一時的に描画する
	draw_temporary();

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

bool MGCurveKnotRemoveSKnot::OnLocated(
	const MGLocateInfo& linfo
){
	if(linfo.is_deleted()){
		state_owner()->cancel_last_insertion();
	}else{
		//選択されたノットをMGCurveKnotRemoveToolクラスの
		//メンバデータにpush_backする
		if(linfo.object()){
			const MGLBRep* pickedCurve=dynamic_cast<const MGLBRep*>(linfo.object());
			double param = linfo.curve_parameter();
			int index = pickedCurve->knot_vector().locate(param);
			state_owner()->insert_knot(index);
		}
	}
	return false;
}

void MGCurveKnotRemoveSKnot::do_make_temporary_display(mgSysGL& sgl,fugenView* pSView){
	MGPickObject& curve=state_owner()->m_curve;
	ASSERT(!curve.is_null());
	MGLBRep* c = dynamic_cast<MGLBRep*>(curve.top_object());
	ASSERT(c);

	const MGKnotVector& t = c->knot_vector();
	int len = t.bdim();

	//曲線上のノットを取得して描画(端点のノットは除く)
	for(int i = c->order(); i < len; i++){
		// includes multi-knots.
		sgl.drawPoint(c->eval(t[i]));
	}

	//ピックしたノットのハイライト
	std::vector<MGPosition> knots;
	std::vector<int>::reverse_iterator
		first = state_owner()->m_knotIDs.rbegin(), last = state_owner()->m_knotIDs.rend();
	for(; first != last; ++first){
		knots.push_back(c->eval(t[*first]));
	}
	const MGColor& black=MGColor::get_instance(MGColor::Black);
	const MGColor& yellow=MGColor::get_instance(MGColor::Yellow);
	sgl.drawPoints(black,yellow,knots);
}
