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

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

#include "stdafx.h"
#include "Tl2/TL2Triangles.h"
#include "fugen.h"
#include "Misc/UserPreference.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "EvalCmd/EvalSurfGauss.h"
#include "SrfCurvaDlg.h"

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

// MGEvalSurfGaussToolTool

MGEvalSurfGaussTool::MGEvalSurfGaussTool(fugenDoc* pDoc)
:MGSelectState(pDoc,ID_EVAL_SURFACE_GAUSS,
	 MGSelectState::MULTIPLE_SELECT, // multiple selection
	 mgAll_2Manifold // surface, face, and shell
),m_nType(0){
	std::fill_n(m_min_curvature, 4, 1.0);
	std::fill_n(m_max_curvature, 4, 0.0);

	attachModelessDialogue<CSrfCurvaDlg>(this);
}

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

bool MGEvalSurfGaussTool::initiate_tool(){
	MGSelectState::initiate_tool();
	const UserPreference& pref = UserPreference::getInstance();
	m_nType = pref.GetIntValue(upv_Eval_SurfGauss_Type);

	// 画面に絵が残っている場合があるため、クリアを確実にする。
	document()->DeleteDisplayList(ID_EVAL_SURFACE_GAUSS);
	
	if(is_breaking_command()){
		MGPickObjects surfs;
		current_objects().select_fsurfaces(surfs);
		draw_pictures(surfs);
		return true;
	}

	if(resetCurrentObjects(mgAll_2Manifold)){
		MGPickObjects selected(current_objects());
		MGPickObjects unselected;
		OnSelected(0, selected, unselected);
		return false;
	}else{
		clear_pick_object();
		// 最初のメッセージ
		SetStatusMessage(IDS_PROMPT_SURFACE);
		return false;
	}
}

bool MGEvalSurfGaussTool::terminate_tool(bool cancel){
	UserPreference& pref = UserPreference::getInstance();
	pref.SetIntValue(upv_Eval_SurfGauss_Type, m_nType);

	return MGSelectState::terminate_tool(cancel);
}

bool MGEvalSurfGaussTool::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
){
	return MGSelectState::OnCommandEnd(1,false);//Curvature display stays displayed.
}

bool MGEvalSurfGaussTool::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).
){
	// 計算開始
	SetStatusMessage(IDS_PROMPT_COMPUTE);
	CWaitCursor sandglass;

	// 選択され直すたびに再描画をする
	CSrfCurvaDlg* tool = getSrfCurvaDialog();
	if(!tool->IsWindowVisible()){
		CPoint pt;
		::GetCursorPos(&pt);
		pt.Offset(-4, -4);
		tool->SetWindowPos(&CWnd::wndTop, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
	}

	m_nType = tool->GetCurvaKind();
	draw_pictures(current_objects());
	SetStatusMessage(IDS_PROMPT_EXIT);

	// コマンドはここでは終了しない
	return false;
}

// テセレーションはリセットせず、描画だけをリセットするパターン。
void MGEvalSurfGaussTool::draw_pictures(
	MGCL::SURFACE_CURVATURE_KIND kind,
	double dRangeMin,
	double dRangeMax
){
	size_t n = m_sysgl.size();
	for(size_t i=0; i<n; i++){
		MGEvalSurfGaussToolSysGL
			* oldsys = m_sysgl[i];
		const MGGel* id = oldsys->object_id();

		mgTL2Triangles tris;
		oldsys->release_tld(tris);
		MGEvalSurfGaussToolSysGL* newsys = new MGEvalSurfGaussToolSysGL(
			std::move(tris), id, kind, dRangeMin, dRangeMax);

		erase_temporary(id, false);
		draw_temporary(newsys, false, false);
		m_sysgl[i] = newsys;
	}
	InvalidateAllStdViews();
}

// テセレーションから描画データを作り直すパターン
void MGEvalSurfGaussTool::draw_pictures(const MGPickObjects& surfs){

	// m_min_curvature, m_max_curvature を更新してtldsを求める。
	std::vector<mgTL2Triangles> tlds;
	ComputeExtrema(surfs, tlds);

	// 割り込み時と通常時でレンジの値を使い分ける。
	CSrfCurvaDlg* tool = getSrfCurvaDialog();
	const double dRangeMin = tool ? tool->GetRangeMin() : m_min_curvature[m_nType];
	const double dRangeMax = tool ? tool->GetRangeMax() : m_max_curvature[m_nType];

	MGCL::SURFACE_CURVATURE_KIND kind = static_cast<MGCL::SURFACE_CURVATURE_KIND>(m_nType);

	size_t nSys = m_sysgl.size();
	for(size_t i = 0; i < nSys; ++i){
		erase_temporary(m_sysgl[i]->object_id(), false);
	}
	size_t n = tlds.size();
	m_sysgl.resize(n);
	for(size_t i=0; i<n; i++){
		m_sysgl[i]=
		new MGEvalSurfGaussToolSysGL(std::move(tlds[i]),surfs[i].top_object(),kind,dRangeMin,dRangeMax);
		draw_temporary(m_sysgl[i],false,false);
	}
	InvalidateAllStdViews();
}

// 各種曲率の（計測対象範囲内の）最小値と最大値を計算してtldsを求める。
// ダイアログの「自動」ボタンの処理用に必要あり。
void MGEvalSurfGaussTool::ComputeExtrema(
	const MGPickObjects& surfs,
	std::vector<mgTL2Triangles>& tlds
){
	MGDrawParam tlparam(*( document()->context()));

	//Get tessellated data in tlds.
	const size_t nsurf=surfs.size();
	for(size_t i=0; i<nsurf; i++){
		const MGObject* obji=surfs[i].top_object();
		const MGFSurface* fsrf=obji->fsurface();
		if(fsrf){
			std::vector<mgTL2Triangles> tldi;
			fsrf->triangulate(tlparam,MGCL::UV,tldi);
			std::vector<mgTL2Triangles>::iterator jend=tldi.end(), j=tldi.begin();
			for(; j!=jend; j++)
				tlds.push_back(std::move(*j));
		}
	}

	//Compute extremes and set the data in m_min_curvature and m_max_curvature.
	size_t ntris=tlds.size();
	for(size_t i=0; i<ntris; i++){
		const mgTL2Triangles& tld = tlds[i];

		const MGSurface& surf = *(tld.surface());
		mgTL2Triangles::const_iterator j = tld.begin(), jend=tld.end();
		for(; j!=jend; j++){
			const mgTL2Triangle& trij=**j;
			mgTL2Triangle::const_iterator k, kbegin = trij.begin(), kend=trij.end();
			for(k=kbegin; k!=kend; k++){
				const MGPosition& uv = *k;

				// MGSurface::curvatures メソッドにて 4 種類の曲率を得る。
				///<  [0]: ガウス曲率, [1]: 平均曲率, [2]: 最小曲率, [3]: 最大曲率
				double curva[4];
				MGUnit_vector N;
				surf.curvatures(uv, curva, N);

				if(k==kbegin){// ループ初回				
					std::copy(curva, curva + 4, m_min_curvature);
					std::copy(curva, curva + 4, m_max_curvature);
				}else{// ループ 2 回目以降				
					for(int j = 0; j < 4; ++j){
						m_min_curvature[j] = (std::min)(curva[j], m_min_curvature[j]);
						m_max_curvature[j] = (std::max)(curva[j], m_max_curvature[j]);
					}
				}
			}
		}

	}
}

MGEvalSurfGaussToolSysGL::MGEvalSurfGaussToolSysGL(const MGEvalSurfGaussToolSysGL& sgaus2)
:mgSysGL(sgaus2.function_code(),sgaus2.object_id()),m_tld(std::move(sgaus2.m_tld)),
m_kind(sgaus2.m_kind),m_minimum(sgaus2.m_minimum),m_maximum(sgaus2.m_maximum){
}
MGEvalSurfGaussToolSysGL::MGEvalSurfGaussToolSysGL(MGEvalSurfGaussToolSysGL && sgaus2)
:mgSysGL(sgaus2.function_code(),sgaus2.object_id()),m_tld(std::move(sgaus2.m_tld)),
m_kind(sgaus2.m_kind),m_minimum(sgaus2.m_minimum),m_maximum(sgaus2.m_maximum){
}
MGEvalSurfGaussToolSysGL & MGEvalSurfGaussToolSysGL::operator=(
	const MGEvalSurfGaussToolSysGL & sgaus2
){
	set_function_code(sgaus2.function_code());
	set_object_id(sgaus2.object_id());
	m_tld=std::move(sgaus2.m_tld);
	m_kind=sgaus2.m_kind;
	m_minimum=sgaus2.m_minimum;
	m_maximum=sgaus2.m_maximum;
	return *this;
}
MGEvalSurfGaussToolSysGL & MGEvalSurfGaussToolSysGL::operator=(
	MGEvalSurfGaussToolSysGL && sgaus2
){
	set_function_code(sgaus2.function_code());
	set_object_id(sgaus2.object_id());
	m_tld=std::move(sgaus2.m_tld);
	m_kind=sgaus2.m_kind;
	m_minimum=sgaus2.m_minimum;
	m_maximum=sgaus2.m_maximum;
	return *this;
}

MGEvalSurfGaussToolSysGL::MGEvalSurfGaussToolSysGL(
	mgTL2Triangles&& tld,
	const MGGel* surf,
	MGCL::SURFACE_CURVATURE_KIND kind,
	double minimum,
	double maximum
):mgSysGL(ID_EVAL_SURFACE_GAUSS, surf),m_tld(std::move(tld)),m_kind(kind),
m_minimum(minimum),m_maximum(maximum){
}

// Note: this->m_tld は空になる。
mgSysGL* MGEvalSurfGaussToolSysGL::clone()const{
	return new MGEvalSurfGaussToolSysGL(*this);
}

//Draw this Sysgl.
//This draw is used to draw the pictures for Undo(, Redo) operations.
void MGEvalSurfGaussToolSysGL::drawSysGL(){
	// ここでは glColor 系で着色している。
	drawSurfaceCurvature(m_tld,m_kind,m_minimum,m_maximum);
}

//Release this mgTL2Triangles into trisNew.
void MGEvalSurfGaussToolSysGL::release_tld(mgTL2Triangles& trisNew){
	trisNew = std::move(m_tld);
}
