/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
// MeshTool.cpp: MGMeshTool クラスのインプリメンテーション

#include "stdafx.h"
#include "Tl2/TL2Triangles.h"
#include "Tl2/TL2Triangle.h"
#include "Tl2/TL2Face.h"
#include "fugenDoc.h"
#include "MeshCmd/MeshTool.h"
#include "GLMeshFromObjDlg.h"
#include "fugenView.h"
#include "IO/GLFileManager.h"
#include "IO/GLFileExporter.h"

#include "GLFileDialog.h"

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

MGMeshTool::MGMeshTool(fugenDoc* pDoc)
:MGSelectState(pDoc, ID_MESH, MGSelectState::MULTIPLE_SELECT, mgAll_2Manifold),
m_pDlg(0), m_bDirty(false){
	// check post-condition
	ASSERT(!m_pDlg);
	ASSERT(!m_bDirty);
}

MGMeshTool::~MGMeshTool(){
	kill_dialog(); // 多分破棄し終えている。

	// check post-condition
	ASSERT(!m_pDlg);
}

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

bool MGMeshTool::initiate_tool(){
	// check pre-condition
	ASSERT(!m_pDlg);
	ASSERT(!m_bDirty);
	MGSelectState::initiate_tool();

	m_target = select_from_current_objects(mgAll_2Manifold);
	m_target.remove(mgAll_Plane);

	//set_current_object(m_target);
	m_target.setNoDisplay();

	if(!m_target.empty()){
		// ダイアログの入力待ち状態となる。
		SetModifiedFlag(true);
		UpdateUI();
	}else{
		// 選択待ち状態となる。
		// 最初のメッセージ
		SetStatusMessage(IDS_PROMPT_SURFACE);
	}

	// コマンド続行
	return false;
}

bool MGMeshTool::terminate_tool(bool cancel){
	m_target.setDisplay();
	set_current_object(m_target);
	kill_dialog();

	// check post-condition
	ASSERT(!m_pDlg);
	return MGSelectState::terminate_tool(cancel);
}

// Compiles OpenGL's display list of temporary objects for each
//standard views.
void MGMeshTool::do_make_temporary_display(mgSysGL& sgl,fugenView* pSView){
	// ダイアログ表示前は何もしない
	if(!m_pDlg || !::IsWindow(*m_pDlg)){
		return;
	}

	// 対象のボリュームを描画する
	CGLMeshDisplayDlg& disp = m_pDlg->GetDisplay();
	const MGCL::VIEWMODE vmode = disp.GetViewMode();
	size_t n = m_vecStl.size();
	GLenum pmode=GL_LINE;
	MGColor::get_instance(MGColor::SpringGreen).exec(sgl);
	if(vmode == MGCL::SHADING || vmode == MGCL::WIRE_AND_SHADING){
		//ライトON
		mgLightModeSwitcher switcher(sgl, mgGLSL::Shading);
		for(size_t i = 0; i < n; i++){
			sgl.drawSTL(*m_vecStl[i], MGCL::WIRE,GL_FILL);
		}
	}
	if(vmode == MGCL::WIRE || vmode == MGCL::WIRE_AND_SHADING){
		mgLightModeSwitcher switcher(sgl, mgGLSL::NoShading);
		for(size_t i = 0; i < n; i++){
			sgl.drawSTL(*m_vecStl[i], MGCL::WIRE,GL_LINE);
		}
	}
}

bool MGMeshTool::OnSelected(
	fugenView*, //The fugenView pointer where point input event took place.
	MGPickObjects&, //selected objects at this selection operation.
	MGPickObjects&  //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).
){
	m_target.setDisplay();
	m_target = select_from_current_objects(mgAll_2Manifold);
	m_target.remove(mgAll_Plane);
	m_target.remove(mgAll_STL);

	//set_current_object(m_target);
	m_target.setNoDisplay();
	SetModifiedFlag(true);

	//表示されていない(或いは初めての)場合，ダイアログ生成．
	if(!m_pDlg){
		ShowParamDlg();
	}

	// 現在の選択を基にメッシュを作成する
	make_stl();
	UpdateUI();

	return false;
}

void MGMeshTool::UpdateUI(){
	if(m_target.empty()){
		// ビューを更新
		draw_temporary();
		return;
	}

	//表示されていない(或いは初めての)場合，ダイアログ生成．
	if(!m_pDlg){
		ShowParamDlg();
	}
	if(m_bDirty){
		make_stl();
	}

	// ダイアログ更新

	// 三角形数、頂点数を取得しダイアログのコントロール変数に設定
	// 最後にダイアログの表示を更新
	CGLMeshInfoDlg& info = m_pDlg->GetInfo();
	info.SetTriangleEdit(m_vecStl);

	// ビューを更新
	draw_temporary();
}

void MGMeshTool::ShowParamDlg(){
	ASSERT(!m_pDlg);

	m_pDlg = new CGLMeshFromObjDlg;
	m_pDlg->SetCmd(this);

	// メッシュ化パラメータ変更ダイアログを表示する
	m_pDlg->Create(CGLMeshFromObjDlg::IDD);
	if(!m_pDlg->IsWindowVisible()){
		CPoint pt;
		::GetCursorPos(&pt);
		pt.Offset(-4, -4);
		m_pDlg->SetWindowPos(&CWnd::wndTop, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
	}
	SetStatusMessage(IDS_PROMPT_PARAMETER);

	// メッシュ化パラメータ変更ダイアログに値を設定
	mgTLInputParam tlparam;
	m_pDlg->GetTess().GetTesselValue(tlparam);
	if(tlparam.crvTol() == -1.)//プログラム起動後初めてコマンドが呼ばれたときのみ初期化
		m_pDlg->GetTess().SetTesselValue(document()->context()->tessellate_param());

	ASSERT(::IsWindow(m_pDlg->GetSafeHwnd()) && m_pDlg->IsWindowVisible());
}

void MGMeshTool::SetModifiedFlag(bool bDirty){
	m_bDirty = bDirty;
}

void MGMeshTool::DetachDialog(){
	m_pDlg = 0; // 単にポイントを外すだけとする。余計な破棄処理は不要。
}

// 仮表示中のMGStlオブジェクトをドキュメントに追加した後、ファイルへの保存を行う
// その後、ドキュメントから削除する
void MGMeshTool::OnMeshSave(){
	ASSERT(m_pDlg && ::IsWindow(*m_pDlg));

	// 全ての拡張子とエクスポーターオブジェクトのマップを取得
	CGLFileManager::OMap mapo;
	CGLFileManager::GetInstance().GetAllSaveHandler(mapo);

	// フィルター、拡張子格納用の変数
	CString filter;

	// ファイルダイアログのフィルターに用いる文字列を作成
	const UINT exts[4] = {
		IDS_FILTER_STL_EXT,
		IDS_FILTER_OBJ_EXT,
		IDS_FILTER_NAS_EXT,
		IDS_FILTER_UNV_EXT,
	};

	int nMaxCustFilter = 0;
	const size_t nExt = 4;
	for(size_t j = 0; j < nExt; ++j){
		CString strExt;
		VERIFY(strExt.LoadString(exts[j]));
		std::map<CString, CGLFileExporter*>::iterator i = mapo.find(strExt);
		if(i != mapo.end()){
			filter += i->second->GetTypeString(); // フィルター文字列
			filter += (TCHAR)'\0';
			filter += i->first;
			filter += (TCHAR)'\0';
			nMaxCustFilter++;
		}
	}

	// 保存用のファイルダイアログを生成
	CGLFileDialog dlg(FALSE);

	// フィルター文字列、拡張子をファイルダイアログに設定
	dlg.m_ofn.nMaxCustFilter = nMaxCustFilter;
	dlg.m_ofn.lpstrFilter = filter;

	// ファイルダイアログ表示
	if(dlg.DoModal() != IDOK){
		return;
	}

	// [保存]押下時の処理
	CString fileName(dlg.GetPathName());
	if(dlg.GetFileExt().IsEmpty()){
		// 拡張子を入力しなかった場合
		// ファイルパスに拡張子を付け足す
		CString strExt;
		VERIFY(strExt.LoadString(exts[dlg.m_ofn.nFilterIndex - 1]));

		::PathAddExtension(
			fileName.GetBuffer(MAX_PATH), ::PathFindExtension(strExt));
		fileName.ReleaseBuffer();
	}

	// 仮表示中のMGStlをファイルへ保存する
	std::vector<UniqueStl>& stls = GetVecStl();
	size_t n=stls.size();
	MGGroup work;
	for(size_t i=0; i<n; i++)
		work.append( stls[i].get());// workのmemeberのdeleteをしてはいけない

	CGLFileExporter* pHandler = CGLFileManager::GetInstance().GetExporter(fileName);
	if(pHandler)
		pHandler->Save(work);

	// workのmemeberのdeleteをしてはいけない
	MGGroup::MYLIST::iterator i=work.begin(), iend=work.end();
	for(; i!=iend; i++){
		i->release();
	}
}

//Make stl data in m_vecStl from m_target.
void MGMeshTool::make_stl(){
	if(!m_bDirty){
		return;
	}

	// ダイアログからパラメータを取得する
	mgTLInputParam tlparam;
	m_pDlg->GetTess().GetTesselValue(tlparam);

	m_vecStl.clear();
	double cerr=tlparam.crvTol();
	double serr=tlparam.surfTol();
	double elen=tlparam.max_edge_len();
	MGDrawParam dpara(cerr,serr,elen);
	double error=cerr>=serr ? cerr:serr;
	size_t n=m_target.size();
	for(size_t i=0; i<n; i++){
		const MGObject* obji=m_target[i].top_object();
		auto f = dynamic_cast<const MGFSurface*>(obji);
		if(f){
			mgTL2Triangles tris(MGCL::XYZ,f->get_surface_pointer());//Tri of (u,v) value data.
			mgTL2Face face(*f,cerr,serr,elen);
			face.tessellate(tris);
			m_vecStl.emplace_back(new MGStl(error,tris));
		}else{
			auto shel = dynamic_cast<const MGShell*>(obji);
			std::vector<UniqueLBRep> boundaries;//Container of the boundary data.
			std::vector< std::vector<SHLL_COM_EDGES> > polylines;
			set_up_shell_shade(*shel,dpara,boundaries,polylines);//shade

			MGComplex::const_iterator i=shel->pcell_begin();
			size_t nfaces=shel->number_of_faces();
			for(size_t j=0; j<nfaces; j++,i++){
				const MGFace& fj=*(shel->face(i));
				mgTL2Face face(dpara,fj,&(polylines[j]));
				mgTL2Triangles tris(MGCL::XYZ,fj.surface());
				face.tessellate(tris);	
				m_vecStl.emplace_back(new MGStl(error,tris));
			}

		}
	}

	SetModifiedFlag(false);
}

bool MGMeshTool::kill_dialog(){
	if(!m_pDlg)
		return true;
	if(!::IsWindow(m_pDlg->GetSafeHwnd()))
		return false;

	// モードレスダイアログを破棄する。
	// "detach"
	m_pDlg->SetCmd(0);

	// OnCancel で閉じるようにする。
	// こいつは自ら delete this するので、ここで delete することは不可能。
	CDialog* pDlg = m_pDlg; // 単にポイントを外す
	DetachDialog();
	pDlg->SendMessage(WM_COMMAND, IDCANCEL);
	return true;
}

bool MGMeshTool::OnCommandEnd(UINT nIDS, bool bEraseDraw){
	// メッシュ化パラメータ変更ダイアログを終わらせる
	kill_dialog();
	return MGSelectState::OnCommandEnd(nIDS, bEraseDraw);
}

bool MGMeshTool::OnCommandCanceled(UINT nIDS){
	// メッシュ化パラメータ変更ダイアログを終わらせる
	kill_dialog();
	return MGSelectState::OnCommandCanceled(nIDS);
}
