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

// TransRotate.cpp: 

#include "stdafx.h"
#include "Calc/mgcalc.h"
#include "Calc/mgfunctor.h"
#include "fugenView.h"
#include "GLInputRealDlg.h"
#include "ViewCmd/CPlaneUtil.h"
#include "TransCmd/TransRotate.h"

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

// MGTransRotateTool

MGTransRotateTool::MGTransRotateTool(fugenDoc* pDoc)
: MGCommandStateOwner(pDoc, ID_TRANSFORM_ROTATE){
}

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

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

	if(current_objects().empty()){
		// モデル選択からスタート
		set_child_current_command(new MGTransRotateSObj(this));
	}
	else{
		// スケーリング操作からスタート
		set_child_current_command(new MGTransRotateIPoint(this));
	}
	return false;
}

// MGTransRotateSObj

MGTransRotateSObj::MGTransRotateSObj(MGTransRotateTool* owner)
: MGSelectState(owner, MGSelectState::MULTIPLE_SELECT){
}

bool MGTransRotateSObj::initiate_tool(){
	MGSelectState::initiate_tool();
	SetStatusMessage(IDS_PROMPT_OBJECT);
	return false;
}

bool MGTransRotateSObj::OnKeyDown(fugenView* window, UINT nChar,UINT nRepCnt, UINT nFlags){
	if(nChar == VK_RETURN){
		if(current_objects().empty()){
			// キャンセルとみなす
			return get_owner_command()->OnCommandEnd(3);
		}else{
			// 次の state へ進む
			set_sibling_next_command(new MGTransRotateIPoint(state_owner()));
			return false;
		}
	}else{
		return MGSelectState::OnKeyDown(window, nChar, nRepCnt, nFlags);
	}
}

// MGTransRotateIPoint

MGTransRotateIPoint::MGTransRotateIPoint(MGTransRotateTool* owner)
: MGLocateState(owner, UNLOCK_SNAP_ATTRIB, LINE_RUBBER, POINT_IPDRAW)
, m_bCopy(false),m_rotation_axis(2){
	m_xyzchar[0]='U';m_xyzchar[1]='V';m_xyzchar[2]='N';
}

bool MGTransRotateIPoint::OnKeyDown(
	fugenView* pView,
	UINT           nChar,
	UINT           nRepCnt,
	UINT           nFlags
){
	static double angle=0.;

	size_t np=locates().size();
	if((nChar=='c' || nChar=='C') && np<=1){
		toggle_copy_mode();
		return false;
	}

	if((nChar=='k' || nChar=='K') && np>=1){
		// 角度入力の、数値入力ダイアログ出現
		double angleR;
		if(InputAngleFromDialogue(angle,pView,angleR)){
			calculate(angleR);
			// コマンド終了
			return OnCommandEnd(1);
		}
	}
	if(np==1){
		switch(nChar){
		case 'u':
		case 'U':
			m_rotation_axis=0;// Rotate around u axis.
			break;
		case 'v':
		case 'V':
			m_rotation_axis=1;// Rotate around v axis.
			break;
		case 'n':
		case 'N':
			m_rotation_axis=2;// Rotate around n axis.
			break;
		default:;
		}
		const MGPosition& org=locates().front()->point_world();
		setPlanarRestrictionPointToProhibitUpdate(m_axes[m_rotation_axis],org);
	}
	return MGLocateState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGTransRotateIPoint::OnLocated(const MGLocateInfo& info){
	const LInfoVec& linfos=locates();
	size_t np=linfos.size();
	if(np>=3){
		// 計算
		const MGLocateInfo& info0=*linfos[0];
		const MGPosition& org = info0.point_world();
		const MGPosition& P1 = linfos[1]->point_world();
		const MGPosition& P2=linfos[2]->point_world();
		const MGVector& normal = m_axes[m_rotation_axis];
		double angle = MGCL::angle(org, P1, P2, normal);
		calculate(angle);

		// コマンド終了
		disable_AETPmode();
		return OnCommandEnd(1);
	}

	if(np==1){
		setDrawerRubber(LINE_RUBBER);
		const MGPlane& pl=linfos[0]->window()->cplane().plane();
		m_axes[0]=pl.u_deriv();m_axes[1]=pl.v_deriv();m_axes[2]=pl.normal();	
		const MGPosition& org=linfos.front()->point_world();
		setPlanarRestrictionPointToProhibitUpdate(m_axes[m_rotation_axis],org);
	}else{
		setDrawerRubber(NO_RUBBER);//We draw arc instead of line rubber.
	}
	if(np==0)
		disable_AETPmode();
	return false;
}

void MGTransRotateIPoint::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	const LInfoVec& linfos=locates();
	if(linfos.size()<=1){
		return;
	}

	const MGLocateInfo& info0=*linfos[0];
	const MGPosition& org=info0.point_world(); // 原点
	const MGPosition& P1=linfos[1]->point_world(); //  X 軸方向参照点 to measure angle
	const MGPosition& P2=cursor(); // 偏角をなす線分の端点

	sgl.LineWidth(2.f);

	// 角度計測用線分を描画
	MGColor::get_instance(MGColor::White).exec(sgl);
	sgl.drawOpenPolyline(&P2,&org,&P1,nullptr);
	
	//Arc from the base line to the angle measuring line segment.
	MGVector Xaxis=P1-org;
	double r1=Xaxis.len(), r2=(P2-org).len();
	double arcRadius= r1<=r2 ? r1:r2; arcRadius*=.666;
	const MGVector& normal = m_axes[m_rotation_axis];
	double angle = MGCL::angle(org, P1, P2, normal);
	if(angle>mgPAI)
		angle-=mgDBLPAI;
	MGEllipse arc(org,org+Xaxis.normalize()*arcRadius, angle, normal);
	MGColor::get_instance(MGColor::White).exec(sgl);
	arc.drawWire(sgl);

	// 回転変換後の形状を描画
	fugenView& view=*pView;
	const MGDrawParam& para=view.draw_param();
	int density=para.line_desity_wire_face();
	MGColor::get_instance(MGColor::SpringGreen).exec(sgl);

	const MGPickObjects& curobj = current_objects();
	MGPickObjects::const_iterator cur = curobj.begin(), last = curobj.end();
	MGTransf transf; transf.set_rotate_3D(normal, angle, org);
	for(; cur != last; ++cur){
		std::unique_ptr<MGObject> obj((*cur)->top_object()->clone());
		obj->transform(transf);
		obj->drawWire(sgl,density);
	}
}

void MGTransRotateIPoint::calculate(double angle){
	const LInfoVec& linfos=locates();
	const MGLocateInfo& info0=*linfos[0];
	const MGPosition& org = info0.point_world();
	const MGVector& normal = m_axes[m_rotation_axis];
	MGTransf transf; transf.set_rotate_3D(normal, angle, org);

	MGGelPositions gelps;
	MGPickObjects curobj(current_objects());
	MGPickObjects::const_iterator first = curobj.begin(), last = curobj.end();
	for(; first != last; ++first){
		MGObject* newobj = (*first)->top_object()->clone();
		newobj->transform(transf);
		gelps.push_back(MGGelPosition((*first)->bottom_group(),newobj));
	}

	if(m_bCopy){
		add_object_to_document(gelps);
	}else{
		replace(curobj,gelps);
	}
}

void MGTransRotateIPoint::prompt_message() const{
	CString strYesNo;
	strYesNo.LoadString(m_bCopy ? IDS_YES : IDS_NO);

	int angleBase=(m_rotation_axis+1)%3;
	char baseC=m_xyzchar[angleBase];
	char axisC=m_xyzchar[m_rotation_axis];
	char nexBaseC=m_xyzchar[(angleBase+1)%3];

	size_t np=locates().size();
	switch(np){
	case 0:
		SetStatusMessage(IDS_PROMPT_ROTATE_ORIGIN, strYesNo);
		break;

	case 1:
		SetStatusMessage(IDS_PROMPT_ROTATE_BASELINE,strYesNo,axisC,baseC,baseC,nexBaseC,nexBaseC);
		break;

	case 2:
		SetStatusMessage(IDS_PROMPT_ROTATE_ANGLE,axisC,nexBaseC);
		break;
	}
}
