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

/**
 * @file LineBisect.cpp
 * @brief MGLineBisectTool クラスのインプリメンテーション
 */
#include "stdafx.h"
#include "mg/Straight.h"
#include "Calc/line.h"
#include "Calc/mgcalc.h"
#include "fugenView.h"
#include "Common/CommandStateOwner.h"
#include "Misc/UserPreference.h"
#include "CurveCmd/LineBisect.h"

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

// MGLineBisectTool

MGLineBisectTool::MGLineBisectTool(fugenDoc* pDoc)
: MGLocateState(pDoc,ID_CURVE_LINE_BISECT,LINE_RUBBER,POINT_IPDRAW),
m_nIDS(1), m_bBoth(false){
}

MGLineBisectTool::MGLineBisectTool(MGCommandStateOwner* owner)
: MGLocateState(owner, UNLOCK_SNAP_ATTRIB, LINE_RUBBER, POINT_IPDRAW),
m_nIDS(1), m_bBoth(false){
}

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

bool MGLineBisectTool::initiate_tool(){
	MGLocateState::initiate_tool();
	const UserPreference& pref = UserPreference::getInstance();
	m_bBoth = pref.GetBoolValue(upv_Curve_LineBisect_Both);
	return false;
}

bool MGLineBisectTool::terminate_tool(bool cancel){
	UserPreference& pref = UserPreference::getInstance();
	pref.SetBoolValue(upv_Curve_LineBisect_Both, m_bBoth);

	return MGLocateState::terminate_tool(cancel);
}

bool MGLineBisectTool::calculate(fugenView* pView){
	const LInfoVec& linfos=locates();
	size_t np = linfos.size();
	if(np>=3){	
		const MGPosition& org=linfos[0]->point_world();
		const MGPosition& P1=linfos[1]->point_world();
		const MGPosition& P2=linfos[2]->point_world();
		MGUnit_vector v1(P1 - org);
		MGUnit_vector v2(P2 - org);
		MGVector v = (v1 + v2) * .5; // bisector

		MGPosition P3=linfos[3]->point_world();
		P3 = org + (P3-org).project(v);
		std::unique_ptr<MGCurve> line(
			std::unique_ptr<MGStraight>(new MGStraight(P3,(m_bBoth ? (2*org - P3) : org))));
		if(!line.get()){
			// failed
			m_nIDS = IDS_FAIL_GENERATE_CURVE;
			return false;
		}

		// ドキュメントを変更
		add_object_to_current_group(line.release());
		return true;
	}else
		return false;
}

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

	std::unique_ptr<MGCurve> line;
	const MGPosition& org =linfos[0]->point_world();
	const MGPosition& P1=linfos[1]->point_world();
	if(linfos.size()==3){
		const MGPosition& P2=linfos[2]->point_world();
		MGUnit_vector v1(P1-org);
		MGUnit_vector v2(P2-org);
		MGVector v = (v1+v2)*.5; // bisector

		MGVector cur = cursor();
		cur -= org;
		MGPosition pos = org + cur.project(v);
		line = mgcalc::create_line(org, pos, m_bBoth);
	}

	sgl.Begin(GL_LINES);
		MGColor::get_instance(MGColor::White).exec(sgl);
		sgl.Vertex3dv(org.data());
		sgl.Vertex3dv(P1.data());
		switch(linfos.size()){
		case 2:
			sgl.Vertex3dv(org.data());
			sgl.Vertex3dv(cursor().data());
			break;
		case 3:
			sgl.Vertex3dv(org.data());
			sgl.Vertex3dv(linfos[2]->point_world().data());
			break;
		}
	sgl.End();

	if(line.get()){
		MGColor::get_instance(MGColor::SpringGreen).exec(sgl);
		line->drawWire(sgl);
	}
}

bool MGLineBisectTool::OnKeyDown(fugenView* pView, UINT nChar, UINT nRepCnt, UINT nFlags){
	switch(nChar){
	case 'b':
	case 'B':
		m_bBoth = !m_bBoth;
		break;
	default:;
	}
	return MGLocateState::OnKeyDown(pView, nChar, nRepCnt, nFlags);
}

bool MGLineBisectTool::OnLocated(const MGLocateInfo& info){
	const LInfoVec& linfos=locates();
	size_t np = linfos.size();
	if(np>=2){
		setDrawerPointLine(POINT_IPDRAW);
		const MGPosition& P0=linfos[0]->point_world();
		const MGPosition& P1=linfos[1]->point_world();
		
		if(np>=3){
			const MGPosition& P2=linfos[2]->point_world();
			if(P2==P0)
				cancel_last_locate();
		}
	}

	np = linfos.size();
	if(np==1)
		setDrawerRubber(LINE_RUBBER);
	else if(np>=2)
		setDrawerRubber(NO_RUBBER);

	if(np>=4){
		// 計算して終了
		if(!calculate(info.window())){
			// failed
			return OnCommandEnd(m_nIDS);
		}
		return OnCommandEnd(1); // コマンド終了
	}
	return false;
}

void MGLineBisectTool::prompt_message() const{
	switch(locates().size()){
	case 0:
		SetStatusMessage(IDS_PROMPT_LINE_BISECT_START);
		break;
	case 1:
		SetStatusMessage(IDS_PROMPT_LINE_ANGLE_START);
		break;
	case 2:
		{
			CString strYesNo;
			strYesNo.LoadString(m_bBoth ? IDS_YES : IDS_NO);
			SetStatusMessage(IDS_PROMPT_LINE_ANGLE_END, strYesNo);
		}
		break;
	case 3:
		{
			CString strYesNo;
			strYesNo.LoadString(m_bBoth ? IDS_YES : IDS_NO);
			SetStatusMessage(IDS_PROMPT_LINE_BISECT_END, strYesNo);
		}
		break;
	}
}
