
//**************************************************************************/
// Copyright (c) 2008 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: August 2009
//**************************************************************************/

#ifndef __MUDBOXSDK_BRUSH_H__
#define __MUDBOXSDK_BRUSH_H__

#include <QtCore/QHash>
#include <QtCore/QSharedPointer>

namespace mudbox {

/// This class can be used to define configurations for different brush behaviours of the same class
struct MBDLL_DECL BrushConfiguration
{
	/// Constructor
	BrushConfiguration( const ClassDesc *pClass, unsigned int iID, QString sName, QString sToolTip, QString sCategory );
	/// Returns the unique id of the configuration
	unsigned int ID( void ) const;
	/// Returns the name of the configuration.
	virtual QString Name( void ) const;
	/// Returns the tooltip for the configuration.
	virtual QString ToolTip( void ) const;
	/// Returns the operation class belonging to the configuration
	virtual const ClassDesc *OperationClass( void ) const;
	/// Returns the category title that groups configurations together
	virtual QString Category() const;
	/// Serializes the configuration to the given stream.
	virtual void Serialize( Stream& s );

private:
	const ClassDesc *m_pClass;
	unsigned int m_iID;
	QString m_sName, m_sToolTip, m_sCategory;
};

/// Possible values for the brush mirror attribute. @see MirrorConfiguration
enum MirrorMode
{
	/// No mirror
	eMirrorNone,
	/// Mirror on global X axis
	eMirrorX,
	/// Mirror on global Y axis
	eMirrorY,
	/// Mirror on global Z axis
	eMirrorZ,
	/// Mirror on local X axis
	eMirrorLocalX,
	/// Mirror on local Y axis
	eMirrorLocalY,
	/// Mirror on local Z axis
	eMirrorLocalZ,
	/// Mirror using topology symmmetry
	eMirrorTangent
};

/// The structure contains information about mirroring of the operation
struct MBDLL_DECL MirrorConfiguration
{
	/// Constructor
	MirrorConfiguration( void );
	/// Returns if the mirror is on an axis or tangent
	enum MirrorMode MirrorMode( void ) const;
	/// Sets the mirroring planes
	void SetMirrorMode( enum MirrorMode mode );
	/// Returns the mirror plane normals
	const Store<mudbox::Vector> &MirrorPlaneNormals( void ) const { return m_aMirrorPlaneNormals; };
	/// Returns the mirror plane positions
	const Store<mudbox::Vector> &MirrorPlanePositions( void ) const { return m_aMirrorPlanePositions; };
	/// Returns if the mirroring is local
	bool IsMirrorLocal( void ) const { return m_bMirrorLocal; };
	/// Returns the number of mirroring planes
	int MirrorPlanes( void ) const { return m_iMirrorPlanes; };
	/// \brief Transforms a vector using the current mirror parameters in object space
	Vector Transform( 
		const Vector &cSource, ///<[in] Vector in object space which should be transformed
		float fW, ///<[in] Fourth dimension of the vector. This is usually 0 when tranforming directions (to skip translations), and 1 otherwise
		const SurfacePoint *pTargetLocation ///<[in] Location of a point on the surface which indicates where the result is expected. 
		/// This parameter can be NULL, but in some cases (like for tangent space mirroring) the result will only be correct if this
		/// parameter is specified.
		) const;

	inline bool operator ==( const MirrorConfiguration & ) const throw() { return false; };
	inline bool operator !=( const MirrorConfiguration &v ) const throw() { return !operator ==( v ); };

private:
	bool m_bMirrorLocal;
	int m_iMirrorPlanes;
	Store<mudbox::Vector> m_aMirrorPlaneNormals;
	Store<mudbox::Vector> m_aMirrorPlanePositions;

	friend class BrushOperation;
};

typedef AttributeInstance<MirrorConfiguration> amirrorcfg;

MBDLL_DECL AttributeWidget *CreateNewMirrorConfigurationWidget( QWidget *pParent, int iWidth, amirrorcfg *pAttribute );

template <> inline
AttributeWidget *amirrorcfg::CreateEditorWidget( QWidget *pParent, int iWidth ) { return CreateNewMirrorConfigurationWidget( pParent, iWidth, this ); };

template <> inline
Attribute::AttributeType amirrorcfg::Type( void ) const { return typeUnknown; };


/// Container class for stamp related properties and functions
struct MBDLL_DECL StampConfiguration
{
	StampConfiguration( void )
	{
		m_bActive = false;
		m_bOrientToStroke = true;
		m_bStretch = false;
		m_bFlipX = m_bFlipY = m_bRandomize = m_bRandomizeFlipX = m_bRandomizeFlipY = false;
		m_fRotation = m_fRandomizeScale = m_fRandomizeStrength = 0.0f;
		m_fRandomizeRotation = 0.35f;
		m_fRandomizePositionX = m_fRandomizePositionY = 0.4f;
	}

	void Serialize( Stream &s );

	inline bool operator ==( const StampConfiguration & ) const throw() { return false; };
	inline bool operator !=( const StampConfiguration &v ) const throw() { return !operator ==( v ); };

	QString m_sFileName;
	bool m_bActive;
	bool m_bOrientToStroke, m_bStretch;
	bool m_bFlipX, m_bFlipY;
	float m_fRotation;
	bool m_bRandomize, m_bRandomizeFlipX, m_bRandomizeFlipY;
	float m_fRandomizeScale, m_fRandomizeRotation, m_fRandomizePositionX, m_fRandomizePositionY, m_fRandomizeStrength;
};

typedef AttributeInstance<StampConfiguration> astampcfg;

MBDLL_DECL AttributeWidget *CreateNewStampConfigurationWidget( QWidget *pParent, int iWidth, astampcfg *pAttribute );

template <> inline
AttributeWidget *astampcfg::CreateEditorWidget( QWidget *pParent, int iWidth ) { return CreateNewStampConfigurationWidget( pParent, iWidth, this ); };

template <> inline
Attribute::AttributeType astampcfg::Type( void ) const { return typeUnknown; };


/// \brief The BrushOperation class is the base class for brush functionality.
///
/// A plugin can implement its own operation class derived from BrushOperation, which will be available for the users in the tools tray.
/// Note that if there is any change in the available operations (like a new plugin is installed with such functions)
/// the settings directory has to be deleted. The implementations of this class must use the DECLARE_CLASS and
/// IMPLEMENT_CLASS macros.
class MBDLL_DECL BrushOperation : public Operation
{
	DECLARE_CLASS;

	/// Values for the brush direction property. @see Direction()
	enum Direction
	{
		eDirNormal,
		eDirAveragedNormal,
		eDirVertexNormal,
		eDirForward,
		eDirRight,
		eDirX,
		eDirY,
		eDirZ,
		eDirCamera,
		eDirScreen,
		eDirXY,
		eDirXZ,
		eDirYZ
	};

	/// Values for the brush effect modifier
	enum Modifier
	{
		eModNormal,
		eModAdditive,
		eModInverted,
		eModNoEffect
	};

	/// This value represents how the user triggered the brush
	enum TriggerType
	{
		eTriggerPrimary,	// primary button was used (by default left mouse button)
		eTriggerSecondary,	// secondary button was used (by default middle mouse button)
		eTriggerTertiary	// tertiary button was used (by default left+middle mouse button)
	};

	/// Values for mask types that can be add to the brush effect as a multiplier
	enum MaskType
	{
		eMaskAny = -1,
		eMaskStamp,
		eMaskStencil,
		eMaskTypeCount
	};

	/// Constructor, do not use it directly
	BrushOperation( void );

	/// Returns the name of the current brush operation
	virtual QString Name( const ClassDesc *pClass = 0 ) const;

	/// Overriding the node event
	virtual void OnNodeEvent( const Attribute &cAttribute, NodeEventType eType );

	/// Returns if the brush is in screen space
	virtual bool IsScreenSpace( void ) const;
	/// Returns the size of the active brush
	virtual float BrushSize( void ) const;
	/// Returns the strength of the active brush
	virtual float BrushStrength( void ) const;
	/// Sets the size of the active brush
	virtual void SetBrushSize( float fSize );
	/// Sets the strength of the active brush
	virtual void SetBrushStrength( float fStrength );
	/// Returns the size of the active brush including the scaling parameter
	virtual float EffectiveBrushSize( void ) const;
	/// Returns the strength of the active brush including the scaling parameter
	virtual float EffectiveBrushStrength( void ) const;
	/// Returns the size bias percentage
	virtual float BrushSizeBias( void ) const;
	/// Returns the strength bias percentage
	virtual float BrushStrengthBias( void ) const;
	/// Returns the maximal brush size
	virtual float MaxBrushSize( void ) const;
	/// Returns the maximal brush strength
	virtual float MaxBrushStrength( void ) const;

	/// Returns true if the brush wants to use drawing curve
	virtual bool UsesStrokeSmoothing( void ) const;
	/// Returns the distance parameter for smoothing if the brush uses drawing curve
	virtual float StrokeSmoothingDistance( void ) const;
	/// Convenience function for setting Stroke smoothing parameters
	virtual void SetStrokeSmoothing( bool bEnable, float fDistance );
	/// Returns true if the brush wants to draw the cursor on it's own
	virtual bool HasCustomCursor( void );

	/// Returns true if the brush wants to use drawing curve
	virtual bool UsesSteadyStroke( void ) const;
	/// Returns the distance parameter for smoothing if the brush uses drawing curve
	virtual float SteadyStrokeDistance( void ) const;
	/// Convenience function for setting Stroke smoothing parameters
	virtual void SetSteadyStroke( bool bEnable, float fDistance );

	/// Returns the falloff curve
	virtual const NURBSCurve *Falloff( void ) const;
	/// Sets the falloff curve
	virtual void SetFalloff( NURBSCurve *pCurve );
	/// Returns the current falloff table.
	virtual const Store<float> &FalloffData( void ) const;
	/// Returns the falloff value in the specified curve position
	inline float FalloffValue( float fValue ) const
	{
		return FalloffData()[int(fValue*(FalloffData().ItemCount()-1))];
	};

	/// Returns true if the mask is turned on for the brush.
	virtual bool IsMaskEnabled( MaskType type = eMaskAny ) const;
	/// Returns the current mask image.
	virtual const Image *MaskImage( MaskType type = eMaskAny ) const;
	/// Returns the current matrix of the mask.
	virtual Matrix MaskMatrix( MaskType type = eMaskAny ) const;
	/// Returns whether the value should be inverted or not (inversion means 1-x)
	virtual bool IsMaskInverted( MaskType type = eMaskAny ) const;
	/// Returns the factor value, which should be multiplied to the mask value.
	virtual float MaskFactor( MaskType type = eMaskAny ) const;
	/// Returns true if the mask shall tile, and false if all the values outside the mask should be treated as zero.
	virtual bool IsMaskTiled( MaskType type = eMaskAny ) const;
	/// Returns a string identifier which is unique for this mask.
	virtual QString MaskName( MaskType type = eMaskAny ) const;
	/// Returns true if the mask should be included in buildup or not.
	virtual bool MaskBuildUp( MaskType type = eMaskAny ) const;
	/// Returns the effective area of the mask as an image. Use this to restrict the mask effect to certain pixels
	virtual const Image *MaskOpacityImage( MaskType type = eMaskAny ) const;
	/// Called by the kernel when the active stamp or stencil is changed on the UI. Override if you want to be notified when this happens.
	virtual void OnMaskChanged( MaskType type = eMaskAny );

	/// Returns the current build up rate (between 0 and 1).
	virtual float BuildUpRate( void ) const;
	/// Returns if the patchs should be applied at the end of stroke
	virtual bool PatchesAtRelease( void ) const;
	/// Returns if the pressure affects brush size
	virtual bool PressureControlsBrushSize( void ) const;
	/// Returns if the pressure affects brush strength
	virtual bool PressureControlsBrushStrength( void ) const;
	/// Returns the mode if inverted or not
	virtual bool IsInverted( void ) const;
	/// Returns if the brush is aligned to the surface
	virtual bool IsOrientedToSurface( void ) const;

	/// Returns the direction type
	Direction Direction( void ) const;
	/// Returns the direction which whould be used for the brush. If the returned value is a zero length vector, then the brush has to calculate its own averaged normal direction.
	Vector DirectionVector( unsigned int iVertexIndex ) const;
	/// When the current direction is calculated properly, the operation may want to call this routine to use the direction for the stamp transformation.
	virtual void SetDirectionVector( const Vector &vDirection ) const;
	/// Return the local position of the previous patch in this stroke.
	Vector PreviousPatchPosition( void ) const;

	/// Called when a new stroke is started.
	virtual void BeginStroke( Mesh *pMesh, Modifier eModifier, TriggerType eTriggerType );
	/// Called when a stroke is finished.
	virtual void EndStroke( void );
	/// Called when the mouse is moved during a stroke. The default implementation calls AddPatch(). Positions are in range [0,1]
	virtual void MouseMove( float fXPosition, float fYPosition, float fXDelta, float fYDelta, AxisAlignedBoundingBox &cDirtyWorldArea );
	/// Called when a new surface point has to be added to the stroke.
	virtual void AddPatch( const SurfacePoint *pPoint, const Vector &vPatchCenter, float fSize, float fPressure, float fStrength, AxisAlignedBoundingBox &cDirtyWorldArea );
	/// Flood a mesh with the effect of the brush.
	virtual void Flood( Mesh *pTarget, float fStrength = 1.0f, bool bFromCamera = false );

	/// Changes to the configuration
	virtual void SetActiveConfiguration( BrushConfiguration *config );
	/// Changes to the configuration
	virtual void SetActiveConfiguration( const QString &sName );
	/// Returns the active configuration
	virtual BrushConfiguration *ActiveConfiguration( void ) const;

	/// Add a new configuration
	static void AddConfiguration( BrushConfiguration *config );
	/// Returns the number of configurations
	static unsigned int ConfigurationCount( void );
	/// Returns a configuration
	static BrushConfiguration *Configuration( unsigned int iIndex );

	/// Returns the structure with stamp properties
	const StampConfiguration &StampConfig( void ) const;
	/// Sets stamp configuration
	void SetStampConfig( StampConfiguration &newConfig );
	/// Convenience method for enabling stamp and setting stamp image
	void SetStamp( bool bEnable, const QString &sFileName = "" );

	/// Set the scale amount of size according to the brush configuration
	void SetBrushSizeScale( float fScale );
	/// Set the scale amount of strength according to the brush configuration
	void SetBrushStrengthScale( float fScale );

	/// Returns if the brush uses the global size
	bool UsesGlobalBrushSize( void ) const;

	/// Returns the mirror configuration
	const MirrorConfiguration &MirrorConfig( void ) const;
	/// Sets mirror configuration
	void SetMirrorConfig( MirrorConfiguration newConfig );

	/// Returns if we already initialized the configuration with the default values.
	/// Can be used to check if the config is set for the first time so it needs some
	/// config-dependent initialization.
	/// The parameter is the configuration index defined in the config declaration.
	virtual bool IsConfigurationInited( BrushConfiguration *config ) const;
	/// Saves the configuration state (the attribute values) into memory
	/// The parameter is the configuration index defined in the config declaration.
	virtual void StoreConfiguration( BrushConfiguration *config );
	/// Reloads the configuration state from the saved buffer so it restores the same state
	/// when it was active last time.
	/// The parameter is the configuration index defined in the config declaration.
	virtual void RestoreConfiguration( BrushConfiguration *config );
	/// Saves/loads the current states of the brushes to/from the given stream
	static void SerializeConfigurationStates( Stream &s );

	/// Returns the file name to be displayed as an overlay icon above the cursor
	virtual QString OverlayIcon( void );
	/// Returns an (x, y) offset relative to the mouse cursor where the icon should be displayed
	virtual QPointF OverlayOffset( void );

	/// For storing and restoring undo/redo data
	virtual bool ExecuteAndInvert( void );
	/// Serializing
	virtual void Serialize( Stream &s );
	/// Value assignment operator
	virtual BrushOperation &operator =( const BrushOperation &op );

	/// Returns if using the brush will lock the active layer to the current subdivision level so it 
	/// cannot be modified on other levels later
	virtual bool CanLockLayer( void ) const;


	/// Internal use only
	virtual void SetFalloffIndex( int iIndex );

	/// Internal use only
	virtual int FalloffIndex( void ) const;

protected:
	/// ATTRIBUTES:
	abool m_bScreenSpace;
	abool m_bInverted;
	abool m_bOrientToSurface;
	afloatr m_fNormalBrushSize, m_fNormalBrushStrength;
	afloatr m_fScreenBrushSize, m_fScreenBrushStrength;
	afloatr m_fMaxBrushSize, m_fMaxBrushStrength;
	afloatr m_fBrushSizeBias, m_fBrushStrengthBias;
	acheckablefloat m_fStrokeSmoothing;
	acheckablefloat m_fSteadyStroke;
	abool m_bRememberSize;
	aenum m_iDirection;
	astampcfg m_cStampCfg;
	amirrorcfg m_cMirrorCfg;
	aevent m_cFlood;
	avoid m_vPressure;
	acurveptr m_cFalloff;
	aint m_iFalloffIndex; 

	Modifier m_eModifier;
	TriggerType m_eTriggerType;
	float m_fBrushSizeScale;
	float m_fBrushStrengthScale;
	BrushConfiguration *m_pActiveConfiguration;

private:
	static QHash< BrushConfiguration *, QSharedPointer<mudbox::BrushOperation> > s_aConfigurationStates;
	static Store<BrushConfiguration *> s_aConfigurations;
};


#define MB_ADD_CONFIG( classname, id, name, tooltip ) \
	mudbox::BrushOperation::AddConfiguration( new BrushConfiguration( classname::StaticClass(), id, name, tooltip ) );

#define MB_ADD_CONFIG2( classname, id, name, tooltip, category ) \
	mudbox::BrushOperation::AddConfiguration( new BrushConfiguration( classname::StaticClass(), id, name, tooltip, category ) );

}; // end of namespace mudbox

#endif
