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

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

#include "stdafx.h"
#include "mg/Straight.h"
#include "Calc/mgcalc.h"
#include "Calc/mgfunctor.h"
#include "fugenView.h"
#include "GLInputRealDlg.h"
#include "TransCmd/TransScale.h"

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

namespace{
	double scalingFactor=2.;
}
// MGTransScale1DTool

MGTransScale1DTool::MGTransScale1DTool(fugenDoc* pDoc)
: MGCommandStateOwner(pDoc, ID_TRANSFORM_SCALE),
m_numDimension(3), m_scalingFactor(scalingFactor){
}

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

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

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

bool MGTransScale1DTool::makeModel(const MGTransf& tr){
	const MGPickObjects& curobj = current_objects();
	if(curobj.empty()){
		return false;
	}

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

	replace(curobj,gelps);
	return true;
}

MGTransf MGTransScale1DTool::calc_transform(const MGPosition& P2){
	const LInfoVec& linfos=locates();
	const MGPosition& P0 = linfos[0]->point_world();
	const MGPosition& P1 = linfos[1]->point_world();
	const MGVector P0P1(P1 - P0);
	const MGVector P0P2(P2 - P0);
	const double dScale = sqrt((P0P2%P0P2) / (P0P1%P0P1));
	return calc_transform(dScale);
}

MGTransf MGTransScale1DTool::calc_transform(double factor){
	const LInfoVec& linfos=locates();
	const MGPosition& P0 = linfos[0]->point_world();
	// 最終的な transform
	MGTransf tr(1.0, 1.0, 1.0);
	tr -= P0;

	// WCS -> UCS
	MGMatrix ucs;
	if(m_numDimension==1 || m_numDimension==2){
		const MGPosition& P1 = linfos[1]->point_world();
		MGUnit_vector ucsx(P1-P0);
		ucs.set_xy_axis(ucsx, linfos[0]->window()->cplane().plane().normal() * ucsx);
		tr *= ucs;
	}

	// scaling
	MGTransf scalar;
	switch(m_numDimension){
	case 1:	scalar=MGTransf(factor, 1.0, 1.0);break;
	case 2:	scalar=MGTransf(factor, factor, 1.0);break;
	default:scalar=MGTransf(factor, factor, factor);break;
	}
	tr *= scalar;

	// UCS -> WCS
	if(m_numDimension==1 || m_numDimension==2)
		tr *= ucs.transpose();
	tr += P0;
	return tr;
}

// MGTransScale1DSObj

MGTransScale1DSObj::MGTransScale1DSObj(MGTransScale1DTool* owner)
: MGSelectState(owner, MGSelectState::MULTIPLE_SELECT){
}

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

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

// MGTransScale1DIPoint

MGTransScale1DIPoint::MGTransScale1DIPoint(MGTransScale1DTool* owner)
: MGLocateState(owner, UNLOCK_SNAP_ATTRIB, LINE_RUBBER, POINT_IPDRAW){
}

bool MGTransScale1DIPoint::OnKeyDown(fugenView* window, UINT nChar,UINT nRepCnt, UINT nFlags){
	MGTransScale1DTool* owner = state_owner();
	const LInfoVec& linfos=locates();
	size_t np = linfos.size();

	int& dim= owner->m_numDimension;
	switch(nChar){
		case('1'): dim=1; break;
		case('2'): dim=2; break;
		case('3'): dim=3; break;
		case('k'):
		case('K'):
		if(np>=1){
			// Input scaling factor from daialogue.
			CString title;title.LoadString(IDS_SCALING_FACTOR);
			CGLInputRealDlg dlg(title);
			dlg.SetValue(owner->m_scalingFactor);
			if(dlg.DoModal()==IDOK){
				double factor = dlg.GetValue();
				if(factor>0.){
					owner->m_scalingFactor =scalingFactor= factor;
					disable_AETPmode();
					// スケーリング計算
					MGTransf tr=owner->calc_transform(factor);
					if(!owner->makeModel(tr)){
						return OnCommandEnd(3);
					}

					// コマンド終了
					return OnCommandEnd(1);
				}
			}
		}
		default:;
	}
	return MGLocateState::OnKeyDown(window, nChar, nRepCnt, nFlags);
}

bool MGTransScale1DIPoint::OnLocated(const MGLocateInfo& info){
	MGTransScale1DTool* owner = state_owner();
	const LInfoVec& linfos=locates();
	size_t np = linfos.size();
	switch(np){
	case 0:
		break;
	case 1:
		disable_AETPmode();
		break;
	case 2:
		// スケーリング原点 or 一つ目の参照点が決定
		// メッセージを更新
		{
		const MGPosition& P0=linfos[0]->point_world();
		const MGPosition& P1=linfos[1]->point_world();
		setTabRestrictionToProhibitUpdate(P0,P1);
		break;
		}

	default:
		disable_AETPmode();
		// スケーリング計算
		MGTransf tr=owner->calc_transform(linfos.back()->point_world());
		if(!owner->makeModel(tr)){
			return OnCommandEnd(3);
		}

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

void MGTransScale1DIPoint::do_make_temporary_display(mgSysGL& sgl,fugenView* pView){
	const LInfoVec& linfos=locates();
	size_t np=linfos.size();
	if(!np){
		return;
	}

	const MGPosition& org=linfos[0]->point_world();
	const MGPosition& ref1 = (np == 1) ? cursor() : linfos[1]->point_world();

	// 線を引く
	MGColor::get_instance(MGColor::White).exec(sgl);
	sgl.Begin(GL_LINES);
	sgl.Vertex3dv(org.data()); // start position
	sgl.Vertex3dv(ref1.data()); // end position
	sgl.End();
	if(np<=1)
		return;

	const MGPosition& ref2 = cursor();
	sgl.Begin(GL_LINES);
	sgl.Vertex3dv(org.data());
	sgl.Vertex3dv(ref2.data());
	sgl.End();

	// スケーリング中のオブジェクトも描く
	MGOpenGLView& glv=*pView;
	const MGDrawParam& para=glv.draw_param();
	int density=para.line_desity_wire_face();

	MGTransScale1DTool* owner = state_owner();
	MGTransf tr=owner->calc_transform(ref2);

	const MGPickObjects& curobj = current_objects();
	MGColor::get_instance(MGColor::SpringGreen).exec(sgl);
	MGPickObjects::const_iterator cur = curobj.begin(), last = curobj.end();
	for(; cur != last; ++cur){
		std::unique_ptr<MGObject> obj((*cur)->top_object()->clone());
		obj->transform(tr);
		obj->drawWire(sgl,density);
	}
}

void MGTransScale1DIPoint::prompt_message()const{
	const LInfoVec& linfos=locates();
	size_t np=linfos.size();

	const MGTransScale1DTool* owner = state_owner();
	int nd=owner->m_numDimension;
	if(np==0){
		if(nd<=0 || nd>3)
			nd=3;
		int nd2=nd%3+1;
		int nd3=nd2%3+1;
		SetStatusMessage(IDS_PROMPT_ORIGIN,nd,nd2,nd2,nd3,nd3);
	}else if(np==1 && (nd==1 || nd==2)){
		SetStatusMessage(IDS_PROMPT_SCALE_DIR);
	}else
		SetStatusMessage(IDS_PROMPT_REFPOINT_NTH, np);
}
