/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
/**
 * @file CurveContourTool.cpp
 * @brief MGCurveContourTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include "fugenDoc.h"
#include "fugenView.h"
#include "CurveCmd/CurveContourTool.h"
#include <numeric>
#include "Common/SelectState.h"

#include "Calc/mgcalc.h"
#include "Calc/mgfunctor.h"

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

// MGCurveContourTool

MGCurveContourTool::MGCurveContourTool(fugenDoc* pDoc)
	 : MGSelectState(
		 pDoc,
		 ID_CURVE_CONTOUR,
		 MGSelectState::MULTIPLE_SELECT,
		 mgAll_FSurface),
	   m_nIDS(1)
{
}

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

bool MGCurveContourTool::initiate_tool(){
	MGSelectState::initiate_tool();
	if(current_object_is_valid(mgAll_FSurface, m_surf)){
		set_current_object(m_surf);
		if(!input_param()){
			// cancel
			return OnCommandEnd(m_nIDS);
		}
		if(!calculate()){
			return OnCommandEnd(m_nIDS);
		}
		return OnCommandEnd(1);
	}
	set_add_mode();
	prompt_message();
	return false;
}

namespace{
	const MGVector& contour_direction(int i){
		switch(i){
		case 0:
			return mgX_UVEC;
		case 1:
			return mgY_UVEC;
		case 2:
			return mgZ_UVEC;
		default:
			ASSERT(FALSE);
			return mgZERO_VEC;
		}
	}
}

bool MGCurveContourTool::calculate(){
	SetStatusMessage(IDS_PROMPT_COMPUTE);
	CWaitCursor sandglass;
	
	MGGelPositions gelps;
	BOOL coord[3] = {m_dlg.m_bSliceX, m_dlg.m_bSliceY, m_dlg.m_bSliceZ};
	double through[3] = {m_dlg.m_dThroughX, m_dlg.m_dThroughY, m_dlg.m_dThroughZ};

	for(int i = 0; i < 3; i++){
		if (!coord[i])
			continue;
		const double pitch = fabs(m_dlg.GetContourPitch());
		if(pitch == 0.)
			continue;

		double lower = m_dlg.GetBoundingBox()[i].low_point();
		const double upper = m_dlg.GetBoundingBox()[i].high_point();
		const int ndiv = int((upper-lower)/pitch);
		const MGVector& normal = contour_direction(i);
		MGVector pv = normal * pitch;

		// 等高線のlowest値を求める
		double ctlower = through[i];
		if(ctlower < lower){// 下から近づく
			while(ctlower < lower)
				ctlower += pitch;
			lower = ctlower;
		}else{// 上から近づく
			while(lower < ctlower)
				ctlower -= pitch;
			lower = ctlower + pitch;
		}

		MGPlane plane(normal, lower);
		typedef std::vector<MGCurve*> Holder;
		Holder tmp;

		MGPickObjects::iterator cur = m_surf.begin(), last = m_surf.end();
		mgcalc::MGContourLine<Holder> calc(plane, pv, ndiv, tmp);
		for(; cur != last; ++cur){
			MGGroup* grp=(*cur)->bottom_group();
			const MGSurface* srf = dynamic_cast<const MGSurface*>((*cur)->top_object());
			if(srf)
				calc(srf);
			else{
				const MGFace* f = dynamic_cast<const MGFace*>((*cur)->top_object());
				if(f)
					calc(f);
			}
			for (auto i = tmp.begin(), ie = tmp.end(); i != ie; i++) {
				gelps.push_back(MGGelPosition(grp, *i));
			}
			tmp.clear();
		}
	}
	// ドキュメント変更
	add_object_to_document(gelps);	
	return true;
}

bool MGCurveContourTool::input_param(){
	SetStatusMessage(IDS_PROMPT_PARAMETER);
	MGBox bbox;
	for (auto& srfi : m_surf)
		bbox |= srfi->leaf_object()->box();
	m_dlg.SetBorder(bbox);
	::ReleaseCapture();
	bool bOK = (IDOK == m_dlg.DoModal());
	if(bOK){
		if(MGAZero(m_dlg.GetContourPitch())){
			// キャンセル
			m_nIDS = IDS_FAIL_PARAMETER;
		}
	}
	return bOK;
}

bool MGCurveContourTool::OnKeyDown(fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case VK_RETURN:
		if(current_objects().empty()){
			// キャンセル扱い
			return OnCommandEnd(3);
		}
		m_surf = current_objects().select(mgAll_FSurface);
		if(!input_param()){
			// cancel
			return OnCommandEnd(m_nIDS);
		}
		if(!calculate()){
			return OnCommandEnd(m_nIDS);
		}
		return OnCommandEnd(1);
	default:
		return MGSelectState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
	}
}

void MGCurveContourTool::prompt_message() const{
	SetStatusMessage(IDS_PROMPT_SURFACE);
}
