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

/**
 * @file SurfSweepExtrudeTool.cpp
 * @brief MGSurfSweepExtrudeTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include "mg/Straight.h"
#include "Calc/mgcalc.h"
#include "Calc/mgfunctor.h"
#include "fugenView.h"
#include "SurfCmd/SurfSweepExtrudeTool.h"

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

// MGSurfSweepExtrudeTool

MGSurfSweepExtrudeTool::MGSurfSweepExtrudeTool(fugenDoc* pDoc)
: MGCommandStateOwner(pDoc, ID_SURFACE_EXTRUDE_STRAIGHT), m_bBoth(false){
}

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

bool MGSurfSweepExtrudeTool::initiate_tool(){
	MGCommandStateOwner::initiate_tool();
	if(check_current()){
		// スイープ方向決定 state へ
		set_child_current_command(new MGSurfSweepStraightIPoint(this));
	}else{
		clear_pick_object();
		// 曲線を選ばせるところからコマンドスタート
		set_child_current_command(new MGSurfSweepStraightSCurve(this));
	}
	return false;
}

bool MGSurfSweepExtrudeTool::OnCommandCanceled(UINT nIDS){
	clear_pick_object();
	return MGCommandStateOwner::OnCommandCanceled(nIDS);
}

bool MGSurfSweepExtrudeTool::calculate(){
	MGVector dir(m_dest - m_org);
	double param_s = m_bBoth ? -dir.len() : 0.;
	double param_e = dir.len();

	const MGPickObjects& curobj = current_objects();
	size_t n = curobj.size();
	std::vector<const MGCurve*> tmp(n);
	for (size_t i = 0; i < n; i++)
		tmp[i] = dynamic_cast<const MGCurve*>(curobj[i].top_object());

	MGGelPositions gelps;
	std::vector<const MGCurve*>::iterator cur = tmp.begin(), last = tmp.end();
	MGPickObjects::const_iterator cur2 = curobj.begin();
	for(; cur != last; ++cur, ++cur2){
		MGSurface* surf = (*cur)->sweep(dir, param_s, param_e);
		ASSERT(surf);

		gelps.push_back(MGGelPosition((*cur2)->bottom_group(),surf));
	}

	add_object_to_document(gelps);
	return true;
}

bool MGSurfSweepExtrudeTool::check_current(){
	reset_current_objects(mgAll_Curve);
	const MGPickObjects& curobj = current_objects();
	if(curobj.empty())
		return false;

	// define m_org.
	const MGCurve* c = dynamic_cast<const MGCurve*>(curobj.front().top_object());
	ASSERT(c);

	m_org = c->center();
	size_t n = curobj.size();
	m_draw.resize(n);
	for (size_t i = 0; i < n; i++)
		m_draw[i] = dynamic_cast<const MGCurve*>(curobj[i].top_object());
	return true;
}

// MGSurfSweepStraightSCurve

MGSurfSweepStraightSCurve::MGSurfSweepStraightSCurve(MGSurfSweepExtrudeTool* owner)
: MGSelectState(owner,MGSelectState::MULTIPLE_SELECT,mgAll_Curve){
}

bool MGSurfSweepStraightSCurve::initiate_tool(){
	MGSelectState::initiate_tool();

	// 最初のメッセージ
	SetStatusMessage(IDS_PROMPT_SWEEP_CURVE);
	return false;
}

bool MGSurfSweepStraightSCurve::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).
){
	VERIFY(state_owner()->check_current());

	// 次はスイープ方向 state
	set_sibling_next_command(new MGSurfSweepStraightIPoint(state_owner()));
	return false;
}

// MGSurfSweepStraightIPoint

MGSurfSweepStraightIPoint::MGSurfSweepStraightIPoint(MGSurfSweepExtrudeTool* owner)
: MGLocateState(owner, UNLOCK_SNAP_ATTRIB, NO_RUBBER, POINT_IPDRAW){
}

bool MGSurfSweepStraightIPoint::OnKeyDown(
	fugenView* pView,
	UINT nChar,
	UINT nRepCnt,
	UINT nFlags
){
	if(nChar == 'b' || nChar == 'B'){
		bool& b = state_owner()->m_bBoth;
		b = !b;
	}
	return MGLocateState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGSurfSweepStraightIPoint::OnLocated(const MGLocateInfo& info){
	// 補正作業
	if(!adjust(info.window(), info.point_screen())){
		return false;
	}

	// 計算
	if(!state_owner()->calculate()){
		return OnCommandCanceled(1);
	}
	// コマンド終了
	return OnCommandEnd(1);
}

void MGSurfSweepStraightIPoint::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	CPoint point=pView->mousemove_new_point();
	if(!adjust(pView, point)){
		return;
	}
	MGSurfSweepExtrudeTool* owner=state_owner();
	MGPosition& dest=owner->m_dest;
	MGPosition& org=owner->m_org;
	auto& draw=owner->m_draw;
	MGVector dir(dest - org);
	MGColor::get_instance(MGColor::SpringGreen).exec(sgl);
	
	size_t nobj=draw.size();
	std::vector<UniqueObject> tempObjs(nobj);
	for(size_t i=0; i<nobj; i++)
		tempObjs[i].reset(draw[i]->clone());

	if(owner->m_bBoth){
		for(size_t i=0; i<nobj; i++)
			tempObjs[i]->transform(dir);
		std::for_each(tempObjs.begin(), tempObjs.end(), mgcalc::CreateDrawFunctor(sgl));
		for(size_t i=0; i<nobj; i++)
			tempObjs[i]->transform(dir*-2.);
		std::for_each(tempObjs.begin(), tempObjs.end(), mgcalc::CreateDrawFunctor(sgl));
		sgl.Begin(GL_LINES);
		sgl.Vertex3dv(org.data());
		sgl.Vertex3dv(dest.data());
		sgl.End();
		sgl.drawPoint(org);
	}else{
		for(size_t i=0; i<nobj; i++)
			tempObjs[i]->transform(dir);
		std::for_each(tempObjs.begin(), tempObjs.end(), mgcalc::CreateDrawFunctor(sgl));
	}
	sgl.Begin(GL_LINES);
	sgl.Vertex3dv(org.data());
	sgl.Vertex3dv(dest.data());
	sgl.End();
	sgl.drawPoint(org);
	sgl.drawPoint(dest);
}

bool MGSurfSweepStraightIPoint::adjust(fugenView* pView, CPoint point){
	ASSERT(!state_owner()->m_org.is_null());
	const MGPosition& posRef = state_owner()->m_org;
	MGPosition pos(cursor()); // 補正後の XYZ

	if(pView->is_perspective()){
		const MGPlane& cpl = pView->cplane().plane();
		MGStraight baseline(MGSTRAIGHT_UNLIMIT, cpl.normal(), posRef);
		MGStraight ray;
		pView->unproject_to_sl(point, ray);
		pos = baseline.eval(baseline.closest(ray)[0]);
	}else{
		// 平面ビューの場合は depth 方向の座標成分を一致するように補正する。
		switch(pView->is_standard_view()){
		case 2: // xy
			pos(2) = posRef[2];
			break;
		case 3: // yz
			pos(0) = posRef[0];
			break;
		case 4: // xz
			pos(1) = posRef[1];
			break;
		}
	}

	if(pos == posRef){
		return false;
	}

	state_owner()->m_dest = pos;
	return true;
}

void MGSurfSweepStraightIPoint::prompt_message()const{
	CString strYesNo;
	strYesNo.LoadString(state_owner()->m_bBoth ?IDS_YES : IDS_NO);
	SetStatusMessage(IDS_PROMPT_SURFACE_EXTRUDE, strYesNo);
}
