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

/**
 * @file LocateState.h
 * @brief クラス MGLocateState の宣言。
 */
#ifndef _MGLocateState_HH_
#define _MGLocateState_HH_

#include "mgGL/SnapPositions.h"
#include "mgGL/SnapAttrib.h"
#include "fugen.h"
#include "Common/CursorRestriction.h"
#include "Common/LocateInfo.h"
#include "Common/GridCursor.h"
#include "Common/CommandBase.h"
#include "Common/CommandStateOwner.h"

class MGCurve;
class MGObject;
class fugenDoc;
class MGGridCursor;

/// @class MGLocateState LocateState.h "Common/LocateState.h"
/// MGLocateState is a class to get point data (locate).
///
/// 色々なコマンドツールで、ユーザーが動かすマウスのカーソル位置を
/// 捕捉するためのクラス。
///
/// マウス位置を作業平面のグリッド交点にスナップしたり、
/// オブジェクトの特徴的な点にスナップしたりする処理を行う。
class MGLocateState:public MGCommandBase{

	friend class  MGLocateOnObjects;

public:

///defines m_lock_snap_attrib data.
enum SNAP_ATTRIB_LOCKKIND{
	UNLOCK_SNAP_ATTRIB=0,///<allow for the operator to update the snap attrib.
	LOCK_SNAP_ATTRIB=1	///<prohibit for the operator to update the snap attrib.
};

//////////Constructor//////////

/// Standard constructor. This state belongs to owner.
MGLocateState(
	MGCommandStateOwner* owner,///<Owner of this MGLocateState.
	SNAP_ATTRIB_LOCKKIND lock_snap_attrib,///<indicates if lock the snap attrib.
	RUBBER_KIND rubberband=NO_RUBBER,
		///<Specify what type of rubberband is necessary.
	IPOINT_DRAWER_KIND input_drawer=NO_IPDRAW,
		///<specify what type of input points drawer be used.
	bool docSnapObjectives=true///<Indicates if this doc's MGObjects be
		///set as the snap objecties, true:set, false:not set.
);

/// constructor that does not have an owner.
MGLocateState(
	fugenDoc*  pDoc,		///<document.
	UINT	command_id,	///<command id
	RUBBER_KIND rubberband=NO_RUBBER,
		///<Specify what type of rubberband is necessary.
	IPOINT_DRAWER_KIND input_drawer=NO_IPDRAW,
		///<specify what type of input points drawer be used.
	bool docSnapObjectives=true///<Indicates if this doc's MGObjects be
		///set as the snap objecties, true:set, false:not set.
);

///Virtual Destructor
virtual ~MGLocateState();

//////////Member Function//////////
		
//set command drawer.
void setDrawerPointLine(
	IPOINT_DRAWER_KIND input_drawer//specify what type of input points drawer be used.
		//0:no drawer, 1:points only, 2:polyline only, 3:points and polyline.
);
		
//set command drawer.
void setDrawerRubber(
	RUBBER_KIND rubberband//Specify what type of rubberband is necessary.
		//0: no rubber band, 1:line rubber band, 2:rect rubber band.
);

void setDrawerColorPoint(const MGColor& PColor);
void setDrawerColorLine(const MGColor& LColor);
void setDrawerColorRubber(const MGColor& RColor);
void setDrawerLWidthPLine(float width);///<line width of Line
void setDrawerLWidthRubber(float width);///<line width of rubber

///Initiate the class.
///MGLocateState always return false.
virtual bool initiate_tool();

/// Prohibit the consecutive same point input.
/// When prohibit=true is input, MGLocateState does not allow to input the same point
/// as the last point, and neglect it, does not invoke OnLocated.
///prohibit=false allows to input it.
void prohibitSamePointInput(bool prohibit=true){m_prohibitSamePointInput=prohibit;}

SNAP_ATTRIB_LOCKKIND snap_lock_kind() const{ return m_lock_snap_attrib; }
void lock_snap_attrib(){m_lock_snap_attrib=LOCK_SNAP_ATTRIB;};
void unlock_snap_attrib(){m_lock_snap_attrib=UNLOCK_SNAP_ATTRIB;};

///Locate a point from the screen coordinate point_in to world coordinate point.
///Function's return value is snap_kind of the point located:
///nopos: no points located.
///endpos: end point located.
///knotpos: knot point located.
///vertexpos: vertex point located.
///nearpos: near point located.
virtual MGSnapPositions::snap_kind locate_object_snap_point(
	fugenView* window,///<The fugenView pointer where point_in  belongs to.
	const CPoint& point_in,	///<Window's point coordinates.
	MGPosition& point,	///<located point will be output
	const MGObject*& obj,//When function's return value is nearpos, end, knot(Curve),
				//vertex(FSurface), or center(Curve&FSurface),
				//the point's parameter value of the object be returned.
	MGPosition& param	
);

///pick a curve and locate the near point data.
///pick_near_position workd only when m_near is turned on.
///function's return value is 0 if no positions are located.
///>0 if a position is located, in this case, crvo and t will have output.
int pick_near_position(
	fugenView* window,///<The fugenView pointer where (sx,sy) belongs to.
	const float center[2],///<screen coordinates whose origin is (left, bottom).
	const MGCurve*& crvo,///<the curve and the (sx,sy)'s parameter value of the curve
						///<will be returned.
	double& t			
);
int pick_near_position(
	fugenView* window,//The fugenView pointer where point_in belongs to.
	CPoint point_in,	//Window's point coordinates.
	const MGCurve*& crvo,//the curve and the (sx,sy)'s parameter value of the curve
						//will be returned.
	double& t			
);

bool snap_end()const{return m_snap_attrib.getEnd();};
bool snap_knot()const{return m_snap_attrib.getKnot();};
bool snap_vertex()const{return m_snap_attrib.getVertex();};
bool snap_near()const{return m_snap_attrib.getNear();};
bool snap_center()const{return m_snap_attrib.getCenter();};

///Terminate the class.
///Overrided terminate must invoke MGLocateTool::terminate_tool last.
///The function's return value is true, if the tool termination is accepted.
///False, if the termination is refuses.
virtual bool terminate_tool(bool cancel=false);

///Turn on the end flag. If data extraction was not done
///after the MGLocateState construction, turn_on_extract_end must be used.
//@{
void turn_on_end(){m_snap_attrib.setEnd(true);};
void turn_on_extract_end();
void turn_off_end(){m_snap_attrib.setEnd(false);};
//@}

///The same as end.
//@{
void turn_on_knot(){m_snap_attrib.setKnot(true);};
void turn_on_extract_knot();
void turn_off_knot(){m_snap_attrib.setKnot(false);};
//@}

///The same as end.
//@{
void turn_on_vertex(){m_snap_attrib.setVertex(true);};
void turn_on_extract_vertex();
void turn_off_vertex(){m_snap_attrib.setVertex(false);};
//@}

///The same as end.
//@{
void turn_on_near(){m_snap_attrib.setNear(true);};
void turn_on_extract_near();
void turn_off_near(){m_snap_attrib.setNear(false);};
//@}

///The same as end.
//@{
void turn_on_center(){m_snap_attrib.setCenter(true);};
void turn_on_extract_center();
void turn_off_center(){m_snap_attrib.setCenter(false);};
//@}

const float* pick_aperture()const{return m_snap_attrib.getSnapAperture();};

///Set pick aperture
void set_pick_aperture(const float ap[2]){m_snap_attrib.setSnapAperture(ap);}

///////////*** m_CursorRestriction handling ***//////////

/// Prohibit ANGLE/ELEVATION/TAB mode. The mode is not changed.
void prohibitAETPmodeUpdate();

///Turn off ANGLE/TAB/ELEVATION restriction to be FREE.
///This also turn off AETP-prohibit-mode().
void disable_AETPmode();

/// 外部からの操作
///Turn on the ANGLE restriction mode on, and set the value.
void setAngleRestriction(double degree);

///Set MGCursorRestriction mode to ELEVATION.
void setElevationRestriction(
	const MGPosition& origin,	//Elebvation origin
	const MGVector& normal		//Elevation normal diretion
);

///Set MGCursorRestriction mode to ELEVATION.
///This also prohibits the update of the cursorRestriction(prohibitAETPmodeUpdate).
///That is, disable_AETPmode() must be invoked() if desired.
void setElevationRestrictionToProhibitUpdate(
	const MGPosition& origin,	//Elebvation origin
	const MGVector& normal		//Elevation normal diretion
);

///Set elevation restriction by the MGLocateInfo.
///The normal is the info.window's cplane normal, and
///the base position of the normal is info.point_world.
///This also prohibits the update of the cursorRestriction(prohibitAETPmodeUpdate).
void setElevationRestrictionToProhibitUpdate(const MGLocateInfo& info); 

///Set MGCursorRestriction mode to TAB
void setTabRestriction(
	const MGPosition& P0,	//Tab's straight's start point.
	const MGPosition& P1	//Tab's straight's end point.
);

///Set MGCursorRestriction mode to TAB
///This also prohibits the update of the cursorRestriction(prohibitAETPmodeUpdate).
void setTabRestrictionToProhibitUpdate(
	const MGPosition& P0,	//Tab's straight's start point.
	const MGPosition& P1	//Tab's straight's end point.
);

///Set MGCursorRestriction mode to PLANAR on the basepoint input..
void setPlanarRestrictionPoint(
	const MGVector& normal, //normal of the plane.
	const MGPosition& basepoint
);

///Set MGCursorRestriction mode to PLANAR on the basepoint input..
void setPlanarRestrictionPointToProhibitUpdate(
	const MGVector& normal, //normal of the plane.
	const MGPosition& basepoint
);

///Turn on the DISTANCE restriction mode on, and set the value.
void setDistanceRestriction(double dist);

///update snap objectives.

///update snap objectives. update_snap_objectives() invoke
///initiate_snap_data() after set_snap_objectives().
void update_snap_objectives(const MGGroup& objectives);
void update_snap_objectives(const std::list<const MGGel*>* objectives=0);
void update_snap_objectives(const MGGel* objective);

///update snap mode by input sattrib.
///update this locate's snap modes, which do no include grid snap.
virtual void updateSnapMode(const MGSnapAttrib& sattrib);


///Get the mgTexture instance which is a static varialbe.
mgTexture* MGLocateState::getCursorTexture();

virtual bool canF8Key(){return true;};
virtual bool ProcessF8Key(){F8Locate();return false;};

protected:

//Append the locate info to the back of locates().
//MGObject* data and the parameter are refered from
//this m_located_object and m_located_param.
MGLocateInfo& append_locate_info(
	const MGPosition& pointW,	//position data in world coordinates.
	fugenView* window=0,		//Window the point is input.
	const CPoint* pointS=0		//postion data in screen coordinates.
);

// Extract each enable locate-points from the argument group.
void extract_locate_points();

//initiate the snap data.
virtual void initiate_snap_data();

// Set the rubberband kind.
void set_rubberband_kind(RUBBER_KIND rk){m_drawer.m_rubberKind = rk;}

///When locate on a curve, return the curve pointer, else nullptr.
MGCurve* snappedCurve();
const MGCurve* snappedCurve() const;

///valid only when snappedCurve()!=nullptr, returns the parameter value
///of the curve.
double snappedCurveParam() const;

///When locate on a MGObject, return the MGObject pointer, else nullptr.
MGObject* snappedObject(){return m_located_object;};
const MGObject* snappedObject() const{return m_located_object;};

///valid only when snappedObject()!=nullptr, returns the parameter value
///of the snappedObject.
const MGPosition& snappedParam()const{return m_located_param;};

// Import the current CursorRestriction status from the Status bar.
//void importCursorRestrictionFromStatusBar();

void extract_near_locate_points(
	const std::list<const MGGel*>& objectives
);

///Process F8 KB input data.
///Function's return value is 
///0 if some data is input.
///!=0 otherwise.
///		return 2;	//OK was not input(canceled or dialogue was closed)
///		return 3;	//No strings were input.
///		return 5;	//distance limitation was processed.
///		return 6;	//angle limitation was processed.
///		return 7;	//This is assumed not to happen. Maybe some bugs are included.
int process_F8_input(
	fugenView* window,///<The fugenView pointer where this event took place.
	MGPosition& ipoint	///<Input point will be output when same data is input.
);

// This method is invoked by only OnMouseMove.
virtual MGSnapPositions::snap_kind snap(
	fugenView* window, UINT nFlags, const CPoint& point
);

MGCursorRestriction& cursorRestriction(){return m_CursorRestriction;};
const MGCursorRestriction& cursorRestriction()const{return m_CursorRestriction;};

/////////////////////////////////////////////////////////////////

//OnLocated will be invoked when a point is input.
virtual bool OnLocated(
	///When linfo.snap_kind() is not nopos(i.e. nearpos, end, knot, vertex,
	/// near, center, or ON_SURFACE), the object pointer
	///and the point's parameter value of the object is set.
	///object is curve when end, knot, or near.
	///object is fsurface when vertex, or ON_SURFACE.
	///object is curve or fsurface when center.
	const MGLocateInfo& linfo
)=0;

virtual bool OnKeyDown(
	fugenView* window,//The fugenView pointer where this event took place.
	UINT nChar,	UINT nRepCnt, UINT nFlags
		//These parameters are of CWnd::OnKeyDown. See the document.
);

//When a point is located, the data will be output to documnet's cursor().
virtual bool OnLButtonDown(
	fugenView* window,//The fugenView pointer where this event took place.
	UINT nFlags, CPoint point
		//These parameters are of CWnd::OnLButtonDown. See the document.
);

virtual bool OnMouseMove(
	fugenView* window,//The fugenView pointer where this event took place.
	UINT nFlags, CPoint point
		//These parameters are of CWnd::OnMouseMove. See the document.
);

virtual bool OnSetCursor(
	fugenView* pView,
	CWnd* pWnd,
	UINT nHitTest,
	UINT message
);

MGSnapPositions::snap_kind m_snapKind;//m_located_object's snap kind.
MGObject* m_located_object;//Object if locate point is on a object.
MGPosition m_located_param;//Object parameter value if m_located_object !=0.

virtual void ModifyContextMenu(CMenu& popup);
virtual bool CanHandle(UINT nID) const;
virtual bool HandleMessage(UINT nID);
virtual bool HandleUpdateUI(UINT nID, CCmdUI* pCmdUI);

protected:
	/// @return true when position data is input, false when some restriction data is input and
	/// position data is not input.
	bool F8Locate(fugenView* pView = 0, const MGObject* snapObj=0);

	/// @return コマンド終了 or 状態遷移するならば true
	bool UndoLocate(fugenView* pView = 0);

private:

/// Prohibit the consecutive same point input.
/// When prohibit=true is input, MGLocate does not allow to input the same point
/// as the last point, and neglect it, does not invoke OnLocated.
/// prohibit=false allows to input it.
bool m_prohibitSamePointInput;

	//doc's snap attrib is copied on construction.
	MGSnapAttrib m_snap_attrib;

	//Status bar's attrib is referenced on construction.
	MGCursorRestriction& m_CursorRestriction;
			//Free, Angle, Elevation, Tab mode, Planar mode.
			//This mode ca usually be modified from the status bar. However this
			//can be prohibitted by prohibitAETPmodeUpdate(). AETP stands for angle,
			//elevation, tab and planar. When prohibitted, the following operations
			//do not affect, do not update m_CursorRestriction status.
			//(1) LButtonDown on the status bar ANGLE mode area,
			//(2) ctrl+LButtonDown on the fugenView area
			//(3) tab key input.

	std::list<const MGGel*> m_objectives;//object pointer for the locate snap objects.
	SNAP_ATTRIB_LOCKKIND m_lock_snap_attrib;
				//indicates if this MGLocateState allows to update
				//the snap attrib m_snap_attrib after the object construction.
				//if m_lock_snap_attrib ==LOCK_SNAP_ARRTIB, prohibit to update.
				//If m_lock_snap_attrib ==UNLOCK_SNAP_ARRTIB, allow to update.

	MGSnapPositions m_end_data;//All the end position data will be stored.
	MGSnapPositions m_knot_data;//All the knot position data will be stored.
	MGSnapPositions m_vertex_data;
					//All the vertices position data of topology will be stored.
	MGSnapPositions m_center_data;

	mgVBO m_nearDlistName;///<Display list name of m_near_data to pick.
	std::vector<const MGCurve*> m_near_data;
					///curve pointers which need near process will be stored.

	HCURSOR m_hCursor; ///< カーソル
	MGGridCursor m_drawer;///<Specify what kind of input points drawer be used.


	void add_snap_objectives(const std::list<const MGGel*>& objectives);

	//Pick the position data from MGSnapPositions data points.
	//Function's return value is 0 if no objects are picked, >0 if an object is picked.
	int pick_position(
		fugenView* window,//The fugenView pointer where (sx,sy) belongs to.
		const float center[2],	//screen coordinates whose origin is (left, bottom).
		const MGSnapPositions& points,
		MGPosition& point,	//point data will be output when function's return value !=0.
		const MGObject*& obj,//Picked object will be returned.
		MGPosition& param	//When points's get_snap_kind() value is end, knot(Curve),
				//vertex(FSurface), or center(Curve&FSurface),
				//the point's parameter value of the object be returned in param.
	)const;
	int pick_position(
		fugenView* window,//The fugenView pointer where point_in belongs to.
		const CPoint& point_in,	//Window's point coordinates.
		const MGSnapPositions& points,
		MGPosition& point,	//point data will be output when function's return value !=0.
		const MGObject*& obj,//Picked object will be returned.
		MGPosition& param	//When points's get_snap_kind() value is end, knot(Curve),
				//vertex(FSurface), or center(Curve&FSurface),
				//the point's parameter value of the object be returned in param.
	)const;

	//Delete snap objective display lists.
	void delete_snap_display_list();

	void set_drawer(
			IPOINT_DRAWER_KIND input_drawer,//specify what type of input points drawer be used.
				//0:no drawer, 1:points only, 2:polyline only, 3:points and polyline.
			RUBBER_KIND rubberband//Specify what type of rubberband is necessary.
				//0: no rubber band, 1:line rubber band, 2:rect rubber band.
		);


	//Set object snap objectives. After set_snap_objectives(), 
	//initiate_snap_data()(or initiate_tool() must be invoked.
	void set_snap_objectives(const std::list<const MGGel*>* objectives=0);
	void set_snap_objectives(const MGGroup& objectives);
	
bool processLButtonDown(
	fugenView* window,//The fugenView pointer where this event took place.
	UINT nFlags,CPoint point,//These parameters are of CWnd::OnLButtonDown. See the document.
	bool onlyOnObject//true if only on object by MGLocateOnObjects
);

};

#endif
