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

#include "stdafx.h"
#include "mgGL/VBO.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "EvalCmd/EvalCurveDevi.h"

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

// MGEvalCurveDeviTool

MGEvalCurveDeviTool::MGEvalCurveDeviTool(fugenDoc* pDoc)
	 : MGSelectState(
		 pDoc,
		 ID_EVAL_CURVE_DEVIATION,
		 MGSelectState::MULTIPLE_SELECT, // multiple seletion
		 mgAll_Curve
){}

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

bool MGEvalCurveDeviTool::initiate_tool(){
	MGSelectState::initiate_tool();
	const MGPickObjects& cobjs=current_objects();
	if(is_breaking_command()){
		MGPickObjects curves;
		cobjs.select_curves(curves);
		if(curves.size()>=2){
			show(curves[0],curves[1]);
		}
		return true;
	}

	set_add_mode();
	int nth=1;
	if(resetCurrentObjects(mgAll_Curve)){
		m_curve1=cobjs[0];
		if(cobjs.size()>=2){
			m_curve2=cobjs[1];
			return OnCommandEnd(1,false);
		}else{
			nth=2;
		}
	}
	SetStatusMessage(IDS_PROMPT_CURVE_NTH, nth);
	return false;
}

bool MGEvalCurveDeviTool::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).
){
	const MGPickObjects& cobjs=current_objects();

	size_t nobjs=cobjs.size();
	if(!nobjs){
		m_curve1.set_null();
	}else{
		m_curve1=cobjs[0];
		if(nobjs>=2){
			m_curve2=cobjs[1];
		}
	}

	if(!m_curve2.is_null()){
		return OnCommandEnd(1,false);
	}

	int nth=2;
	if(m_curve1.is_null())
		nth=1;
	SetStatusMessage(IDS_PROMPT_CURVE_NTH, nth);
	return false;
}

//Invoked when command is to terminate as a nomal end.
//display normal end message.
bool MGEvalCurveDeviTool::OnCommandEnd(
		UINT nIDS,	//=0: erase the current message, and display no messages.
				//=1: display "xxxx" normally end.
				//=2: display "xxxx" failed.
				//otherwise: nIDS is a string id, and load the message from string table to display.
		bool erase_temporary_display
				//true when draw_temporary() pictures are to erase.
				//false if draw_temporary() pictures are to stay displayed.
){
	set_current_object();
	
	MGPickObjects objs;
	objs.push_back(m_curve1);
	objs.push_back(m_curve2);
	show(m_curve1, m_curve2);
	set_current_object(objs);

	return MGSelectState::OnCommandEnd(1,false);
}

//Display the deviation of the two curves.
void MGEvalCurveDeviTool::show(MGPickObject& curve1, MGPickObject& curve2){
	MGEvalCurveDeviSysGL* sysgl=new MGEvalCurveDeviSysGL(curve1,curve2);
	draw_temporary(sysgl);
}

// MGEvalCurveDeviSysGL implementation

MGEvalCurveDeviSysGL::MGEvalCurveDeviSysGL(
	MGPickObject curve1, MGPickObject curve2)
	 : mgSysGL(ID_EVAL_CURVE_DEVIATION,curve1.top_object()),
	   m_gel2(dynamic_cast<MGCurve*>(curve2.top_object())){
}

//Construct new object by copying to newed area.
//User must delete this copied object by "delete".
mgSysGL* MGEvalCurveDeviSysGL::clone()const{
	return new MGEvalCurveDeviSysGL(*this);
}

//Draw this Sysgl.
//This draw is used to draw the pictures for Undo(, Redo) operations.
void MGEvalCurveDeviSysGL::drawSysGL(){
	auto c1 = dynamic_cast<const MGCurve*>(object_id());
	auto c2 = dynamic_cast<const MGCurve*>(m_gel2);
	std::vector<MGPosition> sts;
	c1->eval_discrete_deviation(*c2,sts);
	
	// 離れ評価系の描画のようにする。
	const MGColor& clr1 = MGColor::get_instance(MGColor::Magenta);
	const MGColor& clr2 = MGColor::get_instance(MGColor::Cyan);
	const MGColor& clrm = MGColor::get_instance(MGColor::White);
	MGOpenGLView& view=*(MGOpenGLView::getCurrentOpenGLView());
	for(size_t i = 0; i < sts.size(); i++){
		const MGPosition& sti=sts[i];
		const MGPosition& pos1=c1->eval(sti[0]);
		const MGPosition& pos2=c2->eval(sti[1]);
		const MGPosition& posm = (pos1 + pos2) * .5;

		Color(clr1);//clr1.exec();
		drawPoint(pos1);
		Color(clr2);//clr2.exec();
		drawPoint(pos2);

		Begin(GL_LINE_STRIP);
			Color(clr1);//clr1.exec();
			Vertex3dv(pos1.data());
			Color(clrm);//clrm.exec();
			Vertex3dv(posm.data());
			Color(clr2);//clr2.exec();
			Vertex3dv(pos2.data());
		End();

		CString str;
		double gap=pos1.distance(pos2);
		str.Format(IDS_FORMAT_DISTANCE,gap);
		drawString(str, posm);
	}
}

//Test if this mgSysGL includes gel(return true) or not.
bool MGEvalCurveDeviSysGL::includes(const MGGel* gel)const
{
	return gel==object_id() || gel==m_gel2;
}

//replace gel_old to gel_new.
//If gel_old is not included in this, do nothing.
void MGEvalCurveDeviSysGL::replace(const MGGel* gel_old, const MGGel* gel_new)
{
	if(gel_old==object_id()){
		set_object_id(const_cast<MGGel*>(gel_new));
	}else if(gel_old==m_gel2){
		m_gel2=dynamic_cast<MGCurve*>(const_cast<MGGel*>(gel_new));
	}
}

// Output virtual function.
//Output to stream file:メンバデータを標準出力に出力する。
std::ostream& MGEvalCurveDeviSysGL::out(std::ostream& ostrm) const
{
	mgSysGL::toString(ostrm);
	ostrm<<",m_gel2="<<m_gel2;
	return ostrm;
}
