/****************************************************************************************
 
   Copyright (C) 2012 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.
 
****************************************************************************************/

//! \file fbxgeometry.h
#ifndef _FBXSDK_SCENE_GEOMETRY_H_
#define _FBXSDK_SCENE_GEOMETRY_H_

#include <fbxsdk/fbxsdk_def.h>

#include <fbxsdk/scene/geometry/fbxgeometrybase.h>
#include <fbxsdk/scene/geometry/fbxdeformer.h>
#include <fbxsdk/scene/geometry/fbxshape.h>
#include <fbxsdk/scene/geometry/fbxblendshape.h>
#include <fbxsdk/scene/geometry/fbxblendshapechannel.h>

#include <fbxsdk/fbxsdk_nsbegin.h>

class FbxGeometryWeightedMap;

/** The base class of geometric objects that support control point deformations (e.g. FbxMesh, FbxNurbs, 
* and FbxPatch). The FbxGeometry provides support for the following kinds of deformations.
*
* \li Skin deformation deformers
* \li Vertex cache deformers 
* \li Geometry weighted maps
* \li Shapes
* 
* Most of the methods of FbxGeometry are wrappers to simplify the access/manipulation of the connections 
* to the deformers. For example, calling the GetDeformerCount() method is the same 
* thing as calling: 
*
* \code
* geometry.GetSrcObjectCount(FbxDeformer::ClassId)
* \endcode
*/
class FBXSDK_DLL FbxGeometry : public FbxGeometryBase
{
    FBXSDK_OBJECT_DECLARE(FbxGeometry,FbxGeometryBase);

public:
    /** Returns the node attribute type.
      * This method is derived in the more high level classes (FbxMesh, FbxNurbs, etc...) and returns the
      * actual type of the geometry object. 
      *
      * \return \e eUnknown
      */
    virtual FbxNodeAttribute::EType GetAttributeType() const;

    /**
      * \name Deformer Management
      */
    //@{

    /** Adds a deformer to this geometry (as mentioned in the description of this class, adding a deformer is a synonym
      * for "connect a deformer").
      * \param pDeformer     Pointer to the deformer to be added.
      * \return              Index of the added deformer.
      */
    int AddDeformer(FbxDeformer* pDeformer);

	/** Remove a deformer.
	* \param pIndex Index of deformer to remove.
	* \return Pointer to the removed deformer (or \c NULL if pIndex is out of range). If pIndex is out of range
	*         FbxGeometry::GetLastErrorID() returns eIndexOutOfRange.
	*/
	FbxDeformer* RemoveDeformer(int pIndex);

    /** Returns the number of deformers.
      * \return     The number of deformers that are connected to this geometry.
      */
    int GetDeformerCount() const;

    /** Returns the deformer at the specified index.
      * \param pIndex     The specified deformer index.
      * \return           Pointer to the deformer (or \c NULL if pIndex is out of range). If pIndex is out of range,
      *                   FbxGeometry::GetLastErrorID() returns eIndexOutOfRange.
      */
    FbxDeformer* GetDeformer(int pIndex) const;

    /** Returns the number of deformers of a specified type.
      * \param pType     The specified deformer type.
      * \return          The number of deformers of the specified type.
      */
    int GetDeformerCount(FbxDeformer::EDeformerType pType) const;

    /** Returns the deformer of a specified type at the specified index.
      * \param pIndex     The specified deformer index.
      * \param pType      The specified deformer type.
      * \return           Pointer to the deformer (or \c NULL if pIndex is out of range). If pIndex is out of range,
      *                   FbxGeometry::GetLastErrorID() returns eIndexOutOfRange.
      */
    FbxDeformer* GetDeformer(int pIndex, FbxDeformer::EDeformerType pType) const;

    //@}

    /**
      * \name Geometry Weighted Maps Management
      */
    //@{

    /** Returns the source geometry weighted map that is connected to this geometry.
      * \return     Pointer to the source geometry weighted map that is connected to this geometry if any.
      */
    FbxGeometryWeightedMap* GetSourceGeometryWeightedMap();

    /** Returns the number of destination geometry weighted map(s) that are connected to this geometry.
      * \return     The number of destination geometry weighted map(s) that are connected to this geometry.
      */
    int GetDestinationGeometryWeightedMapCount() const;

    /** Returns the destination geometry weighted map at a specified index.
      * \param pIndex     The specified index.
      * \return           Pointer to the destination geometry weighted map at the specified index (if any).
      */
    FbxGeometryWeightedMap* GetDestinationGeometryWeightedMap(int pIndex);

    //@}

	/**
	* \name Shape Management
	*/
	//@{

	/** Add a shape to the specified blend shape deformer and blend shape channel of this geometry.
	* \param pBlendShapeIndex              The blend shape deformer index.
	* \param pBlendShapeChannelIndex       The blend shape channel index.
	* \param pShape         Pointer to the shape object to be added.
	* \param pPercent       The full deform percentage of this shape.
	* \return               \c true if success, \c false otherwise.
	*/
	bool AddShape(int pBlendShapeIndex, int pBlendShapeChannelIndex, FbxShape* pShape, double pPercent = 100);

	/** Removes all the shapes without destroying them.
	* If shapes aren't explicitly destroyed before calling this function, they will be
	* destroyed along with the SDK manager that created them.
	*/
	void ClearShape();

	/** Returns the number of shapes.
	* \return     The number of shapes that have been added to this geometry.
	*/
	int GetShapeCount() const;

	/** Returns the number of shapes.
	* \param pBlendShapeIndex              The blend shape deformer index.
	* \param pBlendShapeChannelIndex       The blend shape channel index.
	* \return     The number of shapes that have been added to this blend shape channel of this blend shape deformer.
	*/
	int GetShapeCount(int pBlendShapeIndex, int pBlendShapeChannelIndex) const;

	/** Returns the shape found at the specified index on a blend shape channel of a blend shape deformer.
	* \param pBlendShapeIndex              The blend shape deformer index.
	* \param pBlendShapeChannelIndex       The blend shape channel index.
	* \param pShapeIndex                   The specified shape index.
	* \return                              Pointer to the shape (or \c NULL if pIndex is out of range). If pIndex is out of range,
	*                                      FbxGeometry::GetLastErrorID() returns eIndexOutOfRange.
	*/
	FbxShape* GetShape(int pBlendShapeIndex, int pBlendShapeChannelIndex, int pShapeIndex);

	/** Returns the shape found at the specified index on a blend shape channel of a blend shape deformer.
	* \param pBlendShapeIndex              The blend shape deformer index.
	* \param pBlendShapeChannelIndex       The blend shape channel index.
	* \param pShapeIndex                   The specified shape index.
	* \return                              Pointer to the shape (or \c NULL if pIndex is out of range). If pIndex is out of range,
	*                                      FbxGeometry::GetLastErrorID() returns eIndexOutOfRange.
	*/
	FbxShape const* GetShape(int pBlendShapeIndex, int pBlendShapeChannelIndex, int pShapeIndex) const;

	/** Get the shape animation curve.
	* The shape channel is an animatable property with a value range from 0 to 100 (with 100 being full shape deformation).
	* The default value is 0.
	* \param pBlendShapeIndex              The blend shape deformer index.
	* \param pBlendShapeChannelIndex       The blend shape channel index.
	* \param pCreateAsNeeded If \c true, creates the animation curve if it is not already present.
	* \param pLayer          The animation layer from which we want to get the requested animation curve.
	* \return                Animation curve (or NULL if an error occurred). If an error occurs,
	*                        FbxGeometry::GetLastErrorID() returns one of the following:
	*                             - eIndexOutOfRange: Shape index is out of range.
	*                             - eShapeNoCurveFound: Shape curve could not be found.
	* \remarks If pLayer is left at NULL, the method will use the first layer of the Animation stack.
	*/
	FbxAnimCurve* GetShapeChannel(int pBlendShapeIndex, int pBlendShapeChannelIndex, FbxAnimLayer* pLayer, bool pCreateAsNeeded = false);
	//@}

    /** NURBS and Patches surface modes.
      * This information is never directly used inside the FBX SDK. Applications can use these values if they wish to 
	  * carry the "rendering" details of the NURBS and Patches. The FBX SDK guarantee that the value (member of the FbxNurbs,
	  * FbxNurbsSurface and FbxPatch classes) is stored to FBX files and retrieved from them.
	  * \remarks The enum has been defined in this class to avoid symbols duplication.
      */
    enum ESurfaceMode
    {
        eRaw,			//! Raw.
        eLowNoNormals,	//! Low and no normals.
        eLow,			//! Low.
		eHighNoNormals,	//! High and no normals.
        eHigh			//! High.
    };

    /**
      * \name Pivot Management
      * The geometry pivot is used to specify additional translation, rotation,
      * and scaling information applied to all control points when the model is
      * exported.
      */
    //@{

    /** Returns the pivot matrix.
      * \param pXMatrix     Placeholder for the returned matrix.
      * \return             Reference to the passed argument.
      */
    FbxAMatrix& GetPivot(FbxAMatrix& pXMatrix) const;

    /** Sets the pivot matrix.
      * \param pXMatrix     The transformation matrix that is assigned to the pivot matrix.
      */
    void SetPivot(FbxAMatrix& pXMatrix);

    /** Applies the pivot matrix to all vertices/normals of the geometry.
      */
    void ApplyPivot();

    //@}

	/**
	* \name Default Animation Values
	* These functions provides direct access to default animation values that are specific to a geometry.
	* These functions only work if the geometry has been associated with a node.
	*/
	//@{

	/** Sets the default deformation for a specified shape.
	* The default shape property has a value range from 0 to 100 (with 100 being full shape deformation).
	* The default value is 0.
	* \param pBlendShapeIndex              The blend shape deformer index.
	* \param pBlendShapeChannelIndex       The blend shape channel index.
	* \param pPercent                      Deformation percentage (on a scale ranging from 0 to 100).
	* \remarks                             This function has no effect if pIndex is out of range.
	*/
	void SetDefaultShape(int pBlendShapeIndex, int pBlendShapeChannelIndex, double pPercent);

	/** Sets the default deformation for a specified shape.
	* The default shape property has a value range from 0 to 100 (with 100 being full shape deformation).
	* The default value is 0.
	* \param pBlendShape              The blend shape deformer.
	* \param pBlendShapeChannel       The blend shape channel.
	* \param pPercent                 Deformation percentage (on a scale ranging from 0 to 100).
	* \remarks                        This function has no effect if pShapeName is invalid.
	*/
	void SetDefaultShape(FbxBlendShapeChannel* pBlendShapeChannel,  double pPercent);

	/** Returns the default deformation value for the specified shape.
	* The default shape property has a value range from 0 to 100 (with 100 being full shape deformation).
	* The default value is 0.
	* \param pBlendShapeIndex              The blend shape deformer index.
	* \param pBlendShapeChannelIndex       The blend shape channel index.
	* \return           The deformation value for the specified shape, or 0 if pIndex is out of range.
	*/
	double GetDefaultShape(int pBlendShapeIndex, int pBlendShapeChannelIndex) const;

	/** Returns the default deformation value for the specified shape.
	* The default shape property has a value range from 0 to 100 (with 100 being full shape deformation).
	* The default value is 0.
	* \param pBlendShape              The blend shape deformer.
	* \param pBlendShapeChannel       The blend shape channel.
	* \return                         The deformation value for the specified shape, or 0 if pShapeName is invalid.
	*/
	double GetDefaultShape(FbxBlendShapeChannel* pBlendShapeChannel) const;

	//@}

    /**
      * \name Error Management
      */
    //@{

    /** Retrieves the error object.
     *  \return Reference to the error object.
     */
    FbxError& GetError ();

    /** \enum EErrorCode Error identifiers.
      */
    enum EErrorCode
    {
        eIndexOutOfRange,			/*!< The index used to access an item is out of range. */
        eNullParameter,				/*!< Requested pointer to shape object is \c NULL. */
        eShapeAlreadyAdded,			/*!< The specified shape has already been added to this object. */
        eShapeInvalidName,			/*!< The provided name argument is empty. */
        eShapeNameClash,				/*!< The provided name is already used by another shape. */
        eShapeNoCurveFound,			/*!< The shape's animation curve could not be found. */
        eUnknownError,					/*!< Generic error message. */
        eErrorCount
    };

    /** Returns the last error code.
     *  \return     The last error code.
     */
    EErrorCode GetLastErrorID() const;

    /** Returns the last error string.
     *  \return     Text description of the last error.
     */
    const char* GetLastErrorString() const;

    //@}

/*****************************************************************************************************************************
** WARNING! Anything beyond these lines is for internal use, may not be documented and is subject to change without notice! **
*****************************************************************************************************************************/
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    virtual FbxObject& Copy(const FbxObject& pObject);

    void CleanShapeChannels(FbxAnimLayer* pAnimLayer);
    void CleanShapeChannel(FbxAnimLayer* pAnimLayer, int pShapeIndex);
	void CreateShapeChannelProperties(FbxString& pShapeName);
    void ConvertShapeNamesToV5Format(FbxString pTakeNodeName);
    void ConvertShapeNamesToV5Format(FbxString pTakeNodeName, int pShapeIndex);
    void RevertShapeNamesToV6Format(FbxString pTakeNodeName);
    void RevertShapeNamesToV6Format(FbxString pTakeNodeName, int pShapeIndex);
    void ClearTemporaryShapeNames();

protected:
	virtual void Construct(const FbxGeometry* pFrom);
    virtual void Destruct(bool pRecursive);
    virtual void SetDocument(FbxDocument* pDocument);

    FbxString CreateShapeChannelName(int pShapeIndex);
    FbxString CreateShapeChannelName(FbxString pShapeName);

    void CopyDeformers(FbxGeometry const* pGeometry);
    void CopyPivot(FbxGeometry const* pSource);

    // Used during FBX v5 file store
    FbxArray<FbxString*> mShapeNameArrayV6;
    FbxArray<FbxString*> mShapeNameArrayV5;
    FbxArray<FbxString*> mShapeChannelNameArrayV5;

    FbxAMatrix* mPivot;

    mutable FbxError mError;
#endif /* !DOXYGEN_SHOULD_SKIP_THIS *****************************************************************************************/
};

#include <fbxsdk/fbxsdk_nsend.h>

#endif /* _FBXSDK_SCENE_GEOMETRY_H_ */
