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

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

#include "stdafx.h"
#include "mgGL/VBO.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "EvalCmd/EvalCSDevTool.h"
#include "Calc/evalcs.h"

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

// MGEvalCSDevTool

MGEvalCSDevTool::MGEvalCSDevTool(fugenDoc* pDoc)
:MGCommandStateOwner(pDoc, ID_EVAL_CURVE_SURFACE){
}

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

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

	const MGPickObjects& cobj=current_objects();
	m_pocurve= cobj.select_1st_curve();
	m_posurf=cobj.select_1st_fsurface();

	if(!m_pocurve.is_null() && !m_posurf.is_null()){
		draw_pictures();
		return OnCommandEnd(1,false);
	}
	if(is_breaking_command())
		return true;//If breaking into the other command, terminate this command.

	set_current_object(m_pocurve);
	append_current_object(m_posurf);

	if(m_pocurve.is_null()){
		// 曲線選択
		set_child_current_command(new MGEvalCSDevSCurve(this));
		return false;
	}

	if(m_posurf.is_null()){
		// 曲線選択
		set_child_current_command(new MGEvalCSDevSSurf(this));
		return false;
	}

	return false;
}


// Compiles OpenGL's display list of temporary objects for each standard views.
void MGEvalCSDevTool::draw_pictures(){
	const MGCurve* curve=dynamic_cast<const MGCurve*>(m_pocurve.top_object());
	const MGFSurface* surf=dynamic_cast<const MGFSurface*>(m_posurf.top_object());
	assert(curve&&surf);

	MGEvalCSDevSysGL* sysgl=new MGEvalCSDevSysGL(curve, surf);
	sysgl->makeSysGLDisplayList(get_main_glview());
}

// MGEvalCSDevSCurve

MGEvalCSDevSCurve::MGEvalCSDevSCurve(MGCommandStateOwner* owner)
:MGSelectState(owner, MGSelectState::SINGLE_SELECT, mgAll_Curve){}

bool MGEvalCSDevSCurve::initiate_tool(){
	MGSelectState::initiate_tool();
	
	set_add_mode();
	lock_so_far();
	// 最初のメッセージ
	SetStatusMessage(IDS_PROMPT_CURVE);
	return false;
}

bool MGEvalCSDevSCurve::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<const MGCurve*>(objs.front().top_object()));

	MGEvalCSDevTool* owner = state_owner();
	// 曲線をセット
	owner->m_pocurve = objs.front();

	if(!owner->m_posurf.is_null()){
		owner->draw_pictures();
		return owner->OnCommandEnd(1,false);
	}

	// 曲面選択へ進む
	set_sibling_next_command(new MGEvalCSDevSSurf(owner));
	return false;
}

// MGEvalCSDevSSurf

MGEvalCSDevSSurf::MGEvalCSDevSSurf(MGCommandStateOwner* owner)
:MGSelectState(owner, MGSelectState::SINGLE_SELECT, mgAll_FSurface){}

bool MGEvalCSDevSSurf::initiate_tool(){
	MGSelectState::initiate_tool();
	set_add_mode();
	lock_so_far();
	// 最初のメッセージ
	SetStatusMessage(IDS_PROMPT_SURFACE);
	return false;
}

bool MGEvalCSDevSSurf::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);
	// surface / face ...

	MGEvalCSDevTool* owner = state_owner();

	// 曲面をセット
	owner->m_posurf = objs.front();
	if(!owner->m_pocurve.is_null()){
		owner->draw_pictures();
		return owner->OnCommandEnd(1,false);
	}

	// 曲線選択からスタート
	set_sibling_next_command(new MGEvalCSDevSCurve(owner));
	return false;
}

MGEvalCSDevSysGL::MGEvalCSDevSysGL(
	const MGCurve* curve, const MGFSurface* surf
):mgSys2GL(ID_EVAL_CURVE_SURFACE,curve,surf->object_pointer()){;}

const MGCurve* MGEvalCSDevSysGL::curve()const{
	return dynamic_cast<const MGCurve*>(object_id());
}

const MGFSurface* MGEvalCSDevSysGL::surf()const{
	return dynamic_cast<const MGFSurface*>(gel2());
}

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

//Draw this Sysgl.
//This draw is used to draw the pictures for Undo(, Redo) operations.
void MGEvalCSDevSysGL::drawSysGL(){
	mgcalc::MGEvalCSInfoGroup info(curve(), surf());
	double maxdev = 10.;
	int div = 3;
	
	info.evaluate(maxdev, div);
	if(!info.done()){
		// failed
		return;
	}

	mgcalc::MGEvalCSInfoGroup::iterator first = info.begin(), last = info.end();
#ifdef _DEBUG
	mgcalc::MGEvalCSInfoGroup::iterator maxinfo = first;
#endif

	const MGColor& clr1 = MGColor::get_instance(MGColor::Magenta);
	const MGColor& clr2 = MGColor::get_instance(MGColor::Cyan);
	const MGColor& clrm = MGColor::get_instance(MGColor::White);

	for(; first != last; ++first){
		const MGPosition& pos1 = (*first)->position_curve();
		const MGPosition& pos2 = (*first)->position_surf();
		const MGPosition& posm = (pos1 + pos2) * .5;

		clr1.exec(*this);
		drawPoint(pos1);
		clr2.exec(*this);
		drawPoint(pos2);

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

		CString str;
		double dist = (*first)->distance();
		str.Format(IDS_FORMAT_CS_DEVIATION, dist);
		drawString(str, posm);

#ifdef _DEBUG
		if((*maxinfo)->distance() < dist){
			maxinfo = first;
		}
#endif
	}

#ifdef _DEBUG
	TRACE("最大離れ : %lf\n", (*maxinfo)->distance());
#endif
}
