/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
#if !defined( __CURSORRESTRICTION_H__)
#define __CURSORRESTRICTION_H__

class MGPosition;
class MGVector;
class MGConstructionPlane;
class fugenView;

#include "mg/Default.h"
#include "mg/Straight.h"
#include "Common/LocateInfo.h"

/// Declaration for class MGCursorRestriction used by MGLocateState.
/// The purpose of this class is to restrict the cursor track within specified space
/// such as a sphere, a straight line, etc.
class MGCursorRestriction{
public:

	/// Restriction mode.
	/// MGCursorRestriction has two kinds of restriction, one is distance and the other
	/// is CURSOR_RESTRICTION_MODE(free, elevation, tabmode, angle).
	/// CURSOR_RESTRICTION_MODE restriction and distance restriction can be specified
	/// simultaneously.
	/// Both ELEVATION and TABMODE mode are tamporary mode until
	/// the next input position is specified. At the next input, the mode is set FREE.
	enum CURSOR_RESTRICTION_MODE{
		FREE=0,  ///< means that dose not aply restrictions, exceptdistance.
		ANGLE,	 ///< Angel restriction. Input points are urged to make the specified angle(s)
				 ///< against the u(X) direction line of the input view's construction plane.
		ELEVATION,///< normal straight line is set to m_straight_to_map and
				 ///< the cursor is mapped onto the line.
		TABMODE,  //< A straight line is set to m_straight_to_map and
				 ///< the cursor is mapped onto the line.
		PLANAR,	///< Planar mode
		ANGLE_PLANAR///< Bpth Angle and Planar modes are effective.
		};

public:	
	/// The default constructor.
	MGCursorRestriction();

	/////////// operations ////////////

	///Set restriction mode as input.
	void SetRestrictionMode(CURSOR_RESTRICTION_MODE mode);

	///This method is invoked in moving the cursor(update_state=false),
	///or to input adjusted data(update_state=ture).
	///Adjust input cursr point data by snap attributes.
	///Function's return value is true when a point is input, false if not.
	///False will be returned when mode change is done and cursr data is not valid.
	void adjust_cursor(
		fugenView* window,  ///<active view
		UINT           nFlags,  ///<whether shift, tab, control key be pressed or not
		const CPoint&  point,   ///<screen position of the cursor
		const LInfoVec&    ipos,	///<locates points.
		MGPosition&    cursr	///<non-adjusted world position data of the cursor is input,
								///<and the adjusted data will be output.
	);

	/// Prohibit update modes.
	void prohibitAETmodeUpdate(){m_prohibit_AETmodeUpdate=true;};
	void prohibitPmodeUpdate(){m_prohibit_PmodeUpdate=true;};
	void prohibitAETPmodeUpdate();

	///Allow update modes.
	void allowAETmodeUpdate(){m_prohibit_AETmodeUpdate=false;};
	void allowPmodeUpdate(){m_prohibit_PmodeUpdate=false;};
	void allowAETPmodeUpdate();

	bool isAETUpdateProhibitted(){return m_prohibit_AETmodeUpdate;};
	bool isPUpdateProhibitted(){return m_prohibit_PmodeUpdate;};
	bool isAETPUpdateProhibitted(){return m_prohibit_AETmodeUpdate||m_prohibit_PmodeUpdate;};

	/// Returns the mode.
	CURSOR_RESTRICTION_MODE mode() const{ return m_restriction_mode;}

//Angle mode functions.
	bool isAngleMode()const{
		return m_restriction_mode==ANGLE || m_restriction_mode==ANGLE_PLANAR;};
	double get_angle()const{return m_angle;};
	void freeAngleMode();
	void addAngleMode();

	///Set the angle data of angle restriction, does not update the mode.
	void set_angle(double degree);
	
	// Restrict track of the cursor on radial lines which pass through pivot.
	// The first radial lines are lines that pass through pivot and
	// make the angle of multiples of m_angle against the X(U)-axis of cplane.
	void restrict_angle(
		MGPosition&                cursor,
		const MGPosition&          pivot,
		const MGConstructionPlane& cplane
	);

//Planar mode functions.
	void freePlanarMode();

	///Set the mode as PLANAR on the current view cplane.
	///Restriction plane is on the cplane of the located view
	/// and previous is the last point of locates().
	void addPlanarToPreviousMode();
	bool isPlanarMode()const{
		return m_restriction_mode==PLANAR || m_restriction_mode==ANGLE_PLANAR;};

	///Set the mode as PLANAR.
	///normal is the normal of the plane to restrict, and basePoint is the origin of the plane.
	void set_planarToPoint(const MGVector& normal, const MGPosition& basePoint);

	// Restrict track of the cursor as planar to previous point or specified point
	// as the root point as m_straight_to_map.
	void restrict_planar(
		const CPoint& point,
		const fugenView& view,
		const LInfoVec& ipos,	//located position.
		MGPosition& cursor///Non restricted cursor point is input, and
			///adjusted cursor is output.
	);

//Elevation or Tab mode fucntions.
	bool isMapToStraightMode()const{
	return m_restriction_mode==ELEVATION || m_restriction_mode==TABMODE;};

	///Set straight to map on in ELEVATION or TAB mode.
	void set_straight_to_map(
		const MGStraight& sl
	);
	
	///Set straight to map on in ELEVATION or TAB mode.
	void set_straight_to_map(
		const MGPosition& P0,
		const MGPosition& P1
	);

	// Restrict track of the cursor on the straight line m_straight_to_map.
	void restrict_to_straight(
		MGPosition& cursor //The position to restrict is input, updated cursor is output.
	);

//Distance mode functions.
	/// Set the maximum distance from a position to the cursor.
	/// This turns on the distance restriction on, but does not change
	/// the other mode.
	/// When dist<=0., the distance restriction is disabled.
	void set_distance(double dist);

	/// Update the distance mode. When onoff=false, turns off the mode.
	void set_distanceMode_ON(bool onoff=true);
	const std::pair<bool, double>& get_distanceAttrib()const{return m_distance;};
	std::pair<bool, double>& get_distanceAttrib(){return m_distance;};

	// Restrict track of the cursor within the sphere 
	// whose radius is m_distance.second and the center is pivot.
	void restrict_distance(MGPosition& cursor, const MGPosition& pivot);

private:
	
	// pair.first indicates whether its restriction is working or not.
	// pair.second is the quantity of restrition.
	std::pair<bool, double> m_distance;

	bool m_prohibit_AETmodeUpdate;
	bool m_prohibit_PmodeUpdate;
	CURSOR_RESTRICTION_MODE m_restriction_mode;//ELEVATION, TABMODE, or FREE mode.
		//when m_restriction_mode=ELEVATION, cursor is on the normal vector of the construction plane
		//from the point m_posbade on the construction plane.
		//when m_restriction_mode=TABMODE, cursor is on the straight line whose that passes m_posbase and 
		//input_position's back data.

	double  m_angle;/// Degree angle. Valid only when m_restriction_mode=ANGLE, and specifies the angle
		///to the u-deriv() line of the input view's construction plane.

	// The straight to map on in elevation or Tab-mode.
	// CursorRestriction project input cursor position onto this straight.
	MGStraight m_straight_to_map;

	// 
	bool m_planarOnPrevious;///<true if planar mode's plane is on the previous
			/// point of locates().back, or the point on the plane is provided
			/// as the root point of m_straight_to_map's root point.

};

extern const MGCursorRestriction mgNoCursorRestriction;

#endif //__CURSORRESTRICTION_H__
