/////////////////////////////////////////////////////////////
// CINEMA 4D SDK                                           //
/////////////////////////////////////////////////////////////
// (c) MAXON Computer GmbH, all rights reserved            //
/////////////////////////////////////////////////////////////

#ifndef __C4D_SNAPDATA_H_
#define __C4D_SNAPDATA_H_

#include "c4d_basedata.h"
#include "dmodeling.h"

class String;
class BaseList2D;
class BaseDocument;
class BaseDraw;
class ViewportSelect;
class AtomArray;
class BaseThread;
class BaseDrawHelp;
class BaseBitmap;
class BaseContainer;
class EditorWindow;
class BaseObject;

#define		ID_SNAPCORE_LIB								440000112

#define PLUGINFLAG_SNAP_INFERRED_POINT			(1<<27)		/// Inferred points can be generated by the results of this snap mode (used in registration)
#define PLUGINFLAG_SNAP_INFERRED_AXIS				(1<<26)		/// Inferred axis can be generated by the results of this snap mode (used in registration)
#define PLUGINFLAG_SNAP_MIDPOINT						(1<<25)		/// This snap mode is used by the midpoint snap, use the parent id to set which snap it belongs to

//////////////////////////////////////////////////////////////////////////
//! Snap Priority
//! A higher priority snap will take precedence over a lower priority one when both are within snap range
enum SNAPPRIORITY
{
	SNAPPRIORITY_0							= 0,					/// The lowest priority
	SNAPPRIORITY_PLANE					=	1000,				/// Planar snapping e.g. to the workplane or a polygon surface
	SNAPPRIORITY_GUIDE					=	2000,				/// Guide snapping lower then edge to allow correct drawing for dynamic guides
	SNAPPRIORITY_EDGE						=	3000,				/// Edge snapping is a slightly higher priority and will override planar snapping
	SNAPPRIORITY_POINT					=	4000				/// Point snapping is the highest (default) snapping priority
}; // > and < needed so that needs to be left out ENUM_END_LIST(SNAPPRIORITY);

//////////////////////////////////////////////////////////////////////////
//! Snap flags, sent during the snap process itself to limit snapping
//! 
enum SNAPFLAGS
{
	SNAPFLAGS_0									= 0,					/// No flags
	SNAPFLAGS_IGNORE_SELECTED		=	(1<<0),			/// Don't snap to the selected objects or components
	SNAPFLAGS_IGNORE_AXIS				=	(1<<1),			/// Don't snap to the active axis
	SNAPFLAGS_POINTSONLY				= (1<<2),			/// Ignore modes with lower priority than points
	SNAPFLAGS_SURFACEONLY				= (1<<3)			/// Ignore modes with higher priority than planes
} ENUM_END_FLAGS(SNAPFLAGS);

//////////////////////////////////////////////////////////////////////////
//! Inferred guide types
enum INFERREDGUIDETYPE
{
	INFERRED_GUIDE_POINT				=	0,					/// An inferred point or "axis", this will create guides along all perpendicular lines to the point in x, y and z
	INFERRED_GUIDE_LINE					=	1,					/// Inferred guide line, this will act just like a normal guide line, e.g. along an edge
	INFERRED_GUIDE_PLANE				= 2						/// Inferred plane, this will act just like a normal guide plane
} ENUM_END_LIST(INFERREDGUIDETYPE);

//////////////////////////////////////////////////////////////////////////
//! Structure for holding and returning information about the result of a snap
//! and the point that's been snapped to
struct SnapPoint
{
public:
	SnapPoint():target(NULL),component(NOTOK) { }
	~SnapPoint() {}

	Matrix			mat;				///		The result position of the snap
	BaseList2D	*target;		///		The target object being snapped to
	LONG				component;	///		The component ID being snapped to (e.g. polygon, edge, point, line point)
};

//////////////////////////////////////////////////////////////////////////
//! The final result that the user gets when calling Snap::Snap or Snap::Intesect
//! user owns the struct
struct SnapResult : public SnapPoint
{
public:
	SnapResult():SnapPoint(), snapmode(NOTOK), initial_snap(Vector(0.0)) { }
	~SnapResult() {}

	LONG	snapmode;					///		The snap mode used
	Vector initial_snap;		///		The snap porition before 2d processing
	Vector delta;						///		The delta between new and old point positions
};

//////////////////////////////////////////////////////////////////////////
//! Structure containing relevant context information for the snap system
struct SnapStruct
{
public:
	SnapStruct():doc(NULL), bd(NULL), snap_radius(10.0), excluded_objects(NULL), object_list(NULL), bt(NULL), flags(SNAPFLAGS_0) {}
	~SnapStruct() {}

	BaseDocument		*doc;								///	The current document
	BaseDraw				*bd;								/// The active BaseDraw that snapping will be taking place in
	Real						snap_radius;				/// The radius to snap within
	AtomArray				*excluded_objects;	/// A list of objects to exclude
	AtomArray				*object_list;				/// A list of objects in view 
	BaseThread			*bt;								/// The current BaseThread
	SNAPFLAGS				flags;							/// Flags sent by user calling snap function
};

//////////////////////////////////////////////////////////////////////////
//! The base virtual class for Snap mode plugins
//! For example Polygon Snap, Edge Snap, Spline Snap, Vertex Snap and so on
class SnapData : public BaseData
{
public:
	//////////////////////////////////////////////////////////////////////////
	//! Initialize the snap plugin here
	//! \return TRUE for success of initializing the plugin
	virtual Bool Init(void) { return TRUE; }

	//////////////////////////////////////////////////////////////////////////
	//! Free the snap plugin instance here
	virtual void Free(void) { }

	//////////////////////////////////////////////////////////////////////////
	//! Prepare for snapping to begin
	/*!
		\param ss The passed Snap context structure
		\return TRUE on Success of initializing all snap structures needed
	*/
	virtual Bool InitSnap(const SnapStruct &ss) { return TRUE; }

	//////////////////////////////////////////////////////////////////////////
	//! 3D Snap detection routine
	/*!
		\param p The position being snapped to in world coordinates
		\param ss The passed Snap context structure
		\param result Contains data about the resulting snap point if one is generated
		\return TRUE if a snap occurred, FALSE if not
	*/
	virtual Bool Snap(const Vector &p, const SnapStruct &ss, SnapPoint &result) { return FALSE; }

	//////////////////////////////////////////////////////////////////////////
	//! Guide intersection routine
	/*!
		\param p The point being snapped to in world coordinates
		\param n The guide normal in world coordinates
		\param plane TRUE if the guide is a plane, FALSE if it's a ray/line
		\param ss The passed Snap context structure
		\param result Contains data about the resulting intersection points if they're generated
		\return TRUE if an intersection occurred, FALSE if not
	*/
	virtual Bool Intersect(const Vector &p, const Vector &n, Bool plane, const SnapStruct &ss, SnapPoint &result) { return FALSE; }

	//////////////////////////////////////////////////////////////////////////
	//! Free any stored data after snapping has occurred
	/*!
		\param ss The passed Snap context structure
	*/
	virtual void FreeSnap(const SnapStruct &ss) { }

	//////////////////////////////////////////////////////////////////////////
	//! Viewport drawing for the snap
	/*!
	 \return TRUE success of drawing
	*/
	virtual Bool Draw(const SnapStruct &ss, BaseDocument *doc, BaseDraw *bd, BaseDrawHelp *bh, BaseThread *bt) { return TRUE; }

	//////////////////////////////////////////////////////////////////////////
	//! Interaction for the snap
	/*!
	 \return TRUE if intercepting the cursor info purely for the snap (and overriding any active tools), false to pass through
	*/
	virtual Bool GetCursorInfo(const SnapStruct &ss, BaseDocument *doc, BaseDraw *bd, Real x, Real y, BaseContainer &result) { return FALSE; }
	virtual Bool MouseInput(const SnapStruct &ss, BaseDocument *doc, BaseDraw *bd, EditorWindow *win, const BaseContainer &msg) { return FALSE; }
	virtual Bool KeyboardInput(const SnapStruct &ss, BaseDocument *doc, BaseDraw *bd, EditorWindow *win, const BaseContainer &msg) { return FALSE; }
};

//////////////////////////////////////////////////////////////////////////
//! The Snap Interface, must be allocated either with AutoAlloc or Alloc and freed after use with Free
class SnapCore
{
private:
	SnapCore(void) {}
public:
	//////////////////////////////////////////////////////////////////////////
	//! Snap Initialization routine
	/*!
		\param doc The current document
		\param exclude An array of objects to exclude from being snapped to
		\return TRUE on success of initializing the snap
	*/
	Bool Init(BaseDocument *doc, BaseDraw *bd,  AtomArray *exclude = NULL);

	//////////////////////////////////////////////////////////////////////////
	//! Updates the snaps to use the current settings
	//! \return TRUE on success of updating
	Bool Update(void);

	//////////////////////////////////////////////////////////////////////////
	//! Snap Initialization routine
	/*!
		\param p The position to snap to in global space
		\param result The result of the successful snap, user owns this
		\return TRUE on success of finding a snap point, FALSE if no point was found in range to snap to
	*/
	Bool Snap(const Vector &p, SnapResult &result, SNAPFLAGS flags = SNAPFLAGS_0);

	//////////////////////////////////////////////////////////////////////////
	//! Snap Initialization routine
	/*!
		\param p The position to snap to in global space
		\param n The normal of the guide being intersected with in global space
		\param plane TRUE if the intersection should be with a plane, FALSE if with a ray/line
		\param result The result of the successful intersection, user owns this
		\return TRUE on success of finding an intersection, FALSE if no intersection was found in range
	*/
	Bool Intersect(const Vector &p, const Vector &n, Bool plane, SnapResult &result, SNAPFLAGS flags = SNAPFLAGS_0);

	//////////////////////////////////////////////////////////////////////////
	//! Adds an inferred guide point, line or plane to the scene for snapping to
	/*!
		\param mg The matrix for the point (axis), line, or plane, z points along the line or as the normal for the plane
		\param type The type of inferred guide to add
		\return TRUE on success of adding the guide, FALSE for a memory error attempting to do so
	*/
	BaseObject* AddInferred(BaseDocument *doc, const Matrix &mat, INFERREDGUIDETYPE type);

	//////////////////////////////////////////////////////////////////////////
	//! Flush Inferred System all guides and inferred elements will be deleted, should be called at mouse release
	/*!
		\return TRUE on success of removing all inferred guides
	 */
	Bool FlushInferred(void);
	
	//////////////////////////////////////////////////////////////////////////
	//! recive the active object and pass it to SnapHook if is created from current tool important for tool guide 
	/*
	 \param op the new Object created in the tool
	 */
	void SetToolObject(BaseObject* op);
	
	//////////////////////////////////////////////////////////////////////////
	//!Standard allocation routine
	//! \return Instance of iSnapCore
	static SnapCore* Alloc(void);
	//!Standard freeing routine
	static void Free(SnapCore *&p);
};

//////////////////////////////////////////////////////////////////////////
//! Test if a snap mode is enabled or not
/*!
	\param mode The snapmode's ID to test for
	\param doc The document you wish to test within
	\return TRUE if the snap is enabled, FALSE if not
*/
Bool IsSnapEnabled(BaseDocument *doc, LONG mode = NOTOK);

//////////////////////////////////////////////////////////////////////////
//! Set the snap enabled status for a particular mode
/*!
	\param mode The snapmode to set the enabled state for
	\param state The state to use TRUE for enabled FALSE for disabled
	\param doc The document to set the snapmode state for
 */
void EnableSnap(Bool state, BaseDocument *doc, LONG mode = NOTOK);

//////////////////////////////////////////////////////////////////////////
//! Routine to get the snap settings for the current document
/*!
	\param doc The document who's snap settings you wish to retrieve
	\param snapmode Optionally pick a specific snap mode to get or modify it's specific settings
	\return A copy of the settings in a BaseContainer form
*/
BaseContainer SnapSettings(BaseDocument *doc, LONG snapmode = NOTOK);

//////////////////////////////////////////////////////////////////////////
//! Routine to set the snap settings for the current mode/document
/*!
	\param doc The document you wish to set the snap settings for
	\param bc The BaseContainer with the settings for this document and/or snapmode
	\param snapmode Optionally specify which snapmode you wish to set the settings for instead of the global settings
*/
void SnapSettings(BaseDocument *doc, const BaseContainer &bc, LONG snapmode = NOTOK);

//////////////////////////////////////////////////////////////////////////
//! Test if quantizing is enabled or not
/*!
	\param doc The document you wish to test within
	\return TRUE if quantizing is enabled, FALSE if not
*/
Bool IsQuantizeEnabled(BaseDocument *doc);

//////////////////////////////////////////////////////////////////////////
//! Retrieve the quantize step values from QUANTIZE_MOVE, QUANTIZE_SCALE etc
/*!
	\param doc The document you wish to retrieve parameters for
	\param bd The viewport that you want to retrieve the correct QUANTIZE_MOVE value for, not relevant for other quantize modes
	\param quantize_mode The mode you want to retrieve the value for, e.g. QUANTIZE_ROTATE, QUANTIZE_MOVE or QUANTIZE_SCALE
	\return The real step value
*/
Real QuantizeStep(BaseDocument *doc, BaseDraw *bd, LONG quantize_mode);

//////////////////////////////////////////////////////////////////////////
//! Sets the quantize step values for QUANTIZE_MOVE, QUANTIZE_SCALE etc
/*!
	\param doc The document you wish to set parameters for
	\param bd The viewport that you want to set the correct QUANTIZE_MOVE value for, not relevant for other quantize modes
	\param quantize_mode The mode you want to set the value for, e.g. QUANTIZE_ROTATE, QUANTIZE_MOVE or QUANTIZE_SCALE
	\param val The value that you want to set the quantizing step size to
*/
void QuantizeStep(BaseDocument *doc, BaseDraw *bd, LONG quantize_mode, Real val);

//////////////////////////////////////////////////////////////////////////
//! Retrieve the viewport workplane and orientation
/*!
	\param doc The current document
	\param bd The viewport you want to get the workplane's matrix from
	\param mg A passed Matrix used to retrieve the correct workplane matrix, caller owns, pass NULL to skip
	\param scale A passed Vector used to retrieve the correct workplane scale, called owns, pass NULL to skip
	\param op Will be filled with the workplane object itself, called owns, pass NULL to skip
	\return TRUE on success of retrieving the workplane and it's data
*/
Bool GetConstructionPlane(BaseDraw *bd, Matrix *mg, Vector *scale, BaseObject **op);

//////////////////////////////////////////////////////////////////////////
//! Retrieve the workplane object for the document
/*!
	\param doc The document to retrieve the workplane from
	\return the workplane object
*/
BaseObject* GetWorkplaneObject(BaseDocument *doc);

//////////////////////////////////////////////////////////////////////////
//! Get the current workplane locked status
/*!
	\param doc the BaseDocument that the workplane belongs to
	\return TRUE if the workplane is locked
*/
Bool WorkplaneLock(BaseDocument *doc);

//////////////////////////////////////////////////////////////////////////
//! Set the current workplane locked status
/*!
	\param bd the Viewport that to lock the workplane within
	\param locked Whether to lock or unlock the workplane
*/
void WorkplaneLock(BaseDraw *bd, Bool locked);

//////////////////////////////////////////////////////////////////////////
//! Get the current workplane Matrix 
/*!
 \param doc the BaseDocument that the workplane belongs to
\param bd The viewport you want to get the workplane's matrix from, if NULL the locked matrix is returned indipendently form view
 \return the workplane matrix
 */
Matrix GetWorkplaneMatrix(BaseDocument *doc, BaseDraw *bd);
//////////////////////////////////////////////////////////////////////////
//! Snap registration routines

//////////////////////////////////////////////////////////////////////////
//! Snap allocator type, used purely in RegisterSnapPlugin
typedef SnapData *SnapDataAllocator(void);

//////////////////////////////////////////////////////////////////////////
//!Snap registration
/*!
	\param id The unique identifier for this plugin, should be registered on plugincafe.com
	\param str The name for the snap mode, normally for localization you should use GeLoadString(ID) using c4d_symbols and the strings files in your resource folder
	\param help A small block of information that will appear in the interface to explain how the snap mode operates
	\param info Standard registration flags, this includes PLUGINFLAG_SNAP_INFERRED_POINT and PLUGINFLAG_SNAP_INFERRED_AXIS
	\param npalloc The allocator, create a static SnapPlugin::Alloc() { return gNew MySnapMode; } method in your class, then set this to MySnapMode::Alloc
	\param icon A bitmap icon for the snap mode
	\param priority The priority of the snap mode, a higher priority will take precedent over other snaps within the radius
	\param parent_mode An ID for a parent snap mode, this mode then inherits that modes enabled/disabled state
	\return TRUE on success of registering the new snap mode plugin.
*/
Bool RegisterSnapPlugin(LONG id, const String &str, const String &help, LONG snapinfo, SnapDataAllocator *npalloc, BaseBitmap *icon, SNAPPRIORITY priority = SNAPPRIORITY_EDGE, LONG parent_mode = NOTOK);

#endif