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

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

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

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

// MGTransShearTool

MGTransShearTool::MGTransShearTool(fugenDoc* pDoc)
:MGCommandStateOwner(pDoc, ID_TRANSFORM_SHEAR){
}

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

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

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


// MGTransShearSObj

MGTransShearSObj::MGTransShearSObj(MGTransShearTool* owner)
: MGSelectState(owner, MGSelectState::MULTIPLE_SELECT){
}

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

bool MGTransShearSObj::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 MGTransShearIPoint(state_owner()));
			return false;
		}
	}else{
		return MGSelectState::OnKeyDown(window, nChar, nRepCnt, nFlags);
	}
}

// MGTransShearIPoint

MGTransShearIPoint::MGTransShearIPoint(MGTransShearTool* owner)
: MGLocateState(owner, UNLOCK_SNAP_ATTRIB, LINE_RUBBER, POINT_IPDRAW)
,m_bCopy(false){
}

bool MGTransShearIPoint::calculate(){
	const MGPickObjects& curobj = current_objects();
	if(curobj.empty() || m_transf.is_null())
		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(m_transf);
		gelps.push_back(MGGelPosition((*cur)->bottom_group(),new_obj));
	}

	if(IsCopyMode()){
		add_object_to_document(gelps);
	}else{
		replace(curobj, gelps);
	}
	return true;
}

void MGTransShearIPoint::calc_transform(const MGPosition& P2){
	const LInfoVec& linfos=locates();
	size_t np = linfos.size();
	assert(np>=2);
	const MGVector& N = linfos[0]->window()->cplane().plane().normal();
	const MGPosition& P0 = linfos[0]->point_world();
	const MGPosition& P1 = linfos[1]->point_world();

	// shearing angle を求める。
	// P0P1-P0P2 のなす角の tangent を係数とする。

	MGVector P0P1(P1 - P0);
	MGVector P0P2(P2 - P0);
	double cossq = P0P1 % P0P2;
	cossq *= cossq;

	double aa = P0P1 % P0P1;
	double bb = P0P2 % P0P2;
	bb *= aa;
	if(MGAZero(bb)){
		m_transf.set_null();
		return;
	}
	cossq /= bb;
	double fac = sqrt(1. / cossq - 1.);
	if(P0P1 * P0P2 % N < 0){
		fac = -fac;
	}

	// 最終的な transform
	m_transf=MGTransf(1.0, 1.0, 1.0);

	// WCS -> UCS
	MGMatrix ucs;
	MGUnit_vector ucsx=P0P1.normalize();
	ucs.set_xy_axis(ucsx, N * ucsx);

	m_transf -= P0;
	m_transf *= ucs;

	double elements[9] = {
		1.0, 0.0, 0.0,
		fac, 1.0, 0.0,
		0.0, 0.0, 1.0};
	MGMatrix shear(3, elements);

	m_transf *= MGTransf(shear, MGVector(0., 0., 0.));

	// UCS -> WCS
	m_transf *= ucs.transpose();
	m_transf += P0;
}

bool MGTransShearIPoint::OnKeyDown(fugenView* window, UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'c':
	case 'C':
		ToggleCopyMode();
		break;
	default:;
	}
	return MGLocateState::OnKeyDown(window, nChar, nRepCnt, nFlags);
}

bool MGTransShearIPoint::OnLocated(const MGLocateInfo& info){
	const LInfoVec& linfos=locates();
	size_t np=linfos.size();
	if(np>=3){
		// スケーリング計算
		calc_transform(linfos.back()->point_world());
		if(!calculate()){
			return OnCommandEnd(3);
		}

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

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

	const MGPosition& P0=linfos[0]->point_world();
	const MGPosition& P1 = cursor();
	calc_transform(P1);

	MGColor::get_instance(MGColor::White).exec(sgl);
	if(np >= 2){
		const MGPosition& P2 = P1;
		sgl.Begin(GL_LINES);
		sgl.Vertex3dv(P0.data());
		sgl.Vertex3dv(P2.data());
		sgl.End();
	}

	const MGPickObjects& curobj = current_objects();
	if(np>=2 && !curobj.empty()){
		// 変換中のオブジェクトも描く
		MGOpenGLView& glv=*pView;
		const MGDrawParam& para=glv.draw_param();
		int density=para.line_desity_wire_face();
		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(m_transf);
			obj->drawWire(sgl,density);
		}
	}
}

void MGTransShearIPoint::prompt_message() const{
	CString strYesNo;
	strYesNo.LoadString(IsCopyMode() ? IDS_YES : IDS_NO);

	switch(locates().size()){
	case 0:
		SetStatusMessage(IDS_PROMPT_SHEAR_ORIGIN);
		break;
	case 1:
		SetStatusMessage(IDS_PROMPT_SHEAR_XDIR, strYesNo);
		break;
	case 2:
		SetStatusMessage(IDS_PROMPT_SHEAR_ANGLE, strYesNo);
		break;
	}
}
