/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmTypes.h"
#include "glmHierarchicalBone.h"
#include "glmDistanceConversions.h"

#include <glmGolaemCharacterDeclarations.h> // GolaemCharacter declarations

//base name for skeleton override in attributes (simulation) or in simData (layout)
#define GLM_SKELETON_OVERRIDE_DEFAULT_BASENAME "glmSkeletonOverride"

namespace glm
{
    struct HierarchicalSkeletonDescriptionV0;

    //*************************************************************************************
    /**
	* \ingroup visualization
	* \brief Describe a hierarchical skeleton. This method is intended to be specialized to load different hierarchical skeleton representation (xml, bvh, ...)
	* \note inherited classes should set the _rootBone pointer with the root of the skeleton, and fill the _bones vector with all the bones of the skeleton (including the root bone)
	* Bones in the _bones vector should be the bones of a valid biped skeleton reprensentation:
	*	- no unparented bones except the root
	*	- no loop in the bones
	*	- 2 isomorphic arms
	*	- 2 isomorphic legs
	*	- a bust node, whith 3 children (one ending in the neck, and 2 ending in arms)
	*	- arms and legs are constitued of at least 2 bones
	*
	*
	* \date 11-04-2009
	*
	* \author dev@golaem.com
	*
	*
	* \bug 
	*
	*/
    //**********************************************************************************
    class GLM_CORE_API HierarchicalSkeletonDescriptionV1
    {
        GLM_VIRTUAL_RTTI("32d789e2-1104-4c74-8d8d-302ccdf26e58", glm::HierarchicalSkeletonDescriptionV1, addAttributes)

    public: //structures
        //-----------------------------------------------------------------------------
        struct BoneNamePredicate
        {
            bool operator()(const HierarchicalBoneV1* bone0, const HierarchicalBoneV1* bone1)
            {
                return bone0->getName().compare(bone1->getName()) < 0;
            }
        };

        /**
        * \brief Overrides for all the skeleton bone's properties, exprimed as an absolute new value that replace the skeleton's one
        */
        struct GLM_CORE_API Override
        {
            GLM_CLASS_STAT_ID(glm::MemoryStatIds::GolaemShared)

            Override()
            : _rootHeightUnscaled(1.f)
            , _boneslocalPos(getClassMemoryStatId())
            , _boneslocalOri(getClassMemoryStatId())
            , _bonesSkinningScale(getClassMemoryStatId())
            { }

            float _rootHeightUnscaled;
            Array <Vector3> _boneslocalPos;
            Array <Quaternion> _boneslocalOri;
            Array <Vector3> _bonesSkinningScale;
        };

        /**
        * \brief Overrides for some of the skeleton bone's properties, exprimed as a scale/offset from the skeleton's current values
        */
        struct GLM_CORE_API OverrideScales
        {
            GLM_CLASS_STAT_ID(glm::MemoryStatIds::GolaemShared)

            OverrideScales()
            { }

            inline bool empty() const { return (_bonesLocalPosScalesIndices.empty() && _bonesLocalOriOffsetsIndices.empty() && _bonesAdditionalSkinningScalesIndices.empty());  }

            PODArray<unsigned int> _bonesLocalPosScalesIndices;
            Array<Vector3> _bonesLocalPosScales;

            PODArray<unsigned int> _bonesLocalOriOffsetsIndices;
            Array<Quaternion> _bonesLocalOriOffsets;
            
            PODArray<unsigned int> _bonesAdditionalSkinningScalesIndices;
            Array<Vector3> _bonesAdditionalSkinningScales;
        };

    public:
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation)

        //************************************************************
        //! @name Constructors and destructor
        //************************************************************
        //@{
        //------------------------------------------------------------
        //! Default constructor
        //------------------------------------------------------------
        HierarchicalSkeletonDescriptionV1(const char* name = "DefaultHierarchicalSkeletonDescriptionName");

        //------------------------------------------------------------
        //! Copy constructor
        //------------------------------------------------------------
        HierarchicalSkeletonDescriptionV1(const HierarchicalSkeletonDescriptionV1& source);

        //------------------------------------------------------------
        //! Destructor
        //------------------------------------------------------------
        virtual ~HierarchicalSkeletonDescriptionV1();

        //------------------------------------------------------------
        //! Operator =
        //------------------------------------------------------------
        HierarchicalSkeletonDescriptionV1& operator=(const HierarchicalSkeletonDescriptionV1& source);

        //------------------------------------------------------------
        //! Retro-compatibility
        //------------------------------------------------------------
        void createFrom(const HierarchicalSkeletonDescriptionV0& source);

        /** 
		* \brief Clear the description and destruct bones
		*/
        virtual void clear();
        //@}

        /**
		* \brief Operator ==
		* \param otherSkeleton 
		* \return true if the name, the number of bones, and the root bones are ==
		* \note == for the root bone means the bone are identical, and all of the bone's children are identical too (which mean the complete skeleton is identical)
		*/
        bool operator==(const HierarchicalSkeletonDescriptionV1& otherSkeleton) const;
        bool equals(const HierarchicalSkeletonDescriptionV1& otherSkeleton, bool compareNames, bool compareBoneCount, bool compareBones, bool compareBoneOrder) const;

        //************************************************************
        //! @name Accessors
        //************************************************************
        //@{
        /** 
		* \brief Get the name of the skeleton
		* \return 
		*/
        inline glm::GlmString getName() const
        {
            return _name;
        }

        /**
		* \brief Set the name of the skeleton
		* \param name new skelton name
		*/
        inline void setName(const char* name)
        {
            _name = name;
        }

        /**
		* \brief Get the root bone of the skeleton
		* \return 
		*/
        inline const HierarchicalBoneV1* getRootBone() const
        {
            return _rootBone;
        }
        /**
		* \brief Set the root bone of the skeleton
		* \return 
		*/
        inline void setRootBone(HierarchicalBoneV1* rootBone)
        {
            _rootBone = rootBone;
        }

        /**
		* \brief Get the bones of the skeleton
		* \return 
		*/
        inline const PODArray<HierarchicalBoneV1*>& getBones() const
        {
            return _bones;
        }
        /**
		* \brief Touch the bones of the skeleton
		* \return 
		*/
        inline PODArray<HierarchicalBoneV1*>& touchBones()
        {
            return _bones;
        }
        //@}

        //************************************************************
        //! @name Integrity check
        //************************************************************
        //@{
        /**
		* \brief Tells if the current skeleton description is valid
		* \param logErrors true to log error on logger
		* \return true if the skeleton description is valid
		* \note a skeleton description is considered as 'valid' if all bone's index are consistant (one different bone index per bone, no bone index greater than the number of bones)
		* and if all bones are correctly set directly or indirectly udner the 'root' bone
		* and if the root bone has no parent
		*/
        bool isSkeletonDescriptionValid(const bool logErrors = true) const;
        //@}

        // index of bones, width first, will build an array of boneIds arranged width first, then alphabetically
        void initSortedBones();

        // FBX/Cache Bone To GolaemCharacter/GCG Bone index (not renamed, used almost everywhere, wait for a complete task for that)
        const glm::PODArray<size_t>& getSortedBones() const
        {
            return _cacheToSpecificSortedBones;
        }

        // GolaemCharacter/GCG Bone index to FBX/Cache bone index
        const glm::PODArray<size_t>& getSpecificToCacheBoneIndices() const
        {
            return _specificToCacheBoneIndices;
        }

        float getMaxBonesHierarchyLength() const;

        void bakeSkeletonOverride(const Override& skeletonOverride);

    protected:
        //************************************************************
        //! @name Serialization
        //************************************************************
        //@{
        /**
		* \brief register the inspectable attributes to the class RTTI
		*/
        static void addAttributes();
        //@}

    protected:
        //************************************************************
        //! @name Datas
        //************************************************************
        //@{
        glm::GlmString _name;
        HierarchicalBoneV1* _rootBone;        //!< root bone of the skeleton
        PODArray<HierarchicalBoneV1*> _bones; //!< list of all bones of the skeleton (including the root bone)

        glm::PODArray<size_t> _cacheToSpecificSortedBones; ///< runtime data not serialized, but rebuilt if needed. former "sortedBones"
        glm::PODArray<size_t> _specificToCacheBoneIndices; ///< runtime data not serialized, but rebuilt if needed. former "inverseSortedBones"

        //@}
    };

    GLM_CORE_API void skeletonOverrideFromOverrideScales(HierarchicalSkeletonDescriptionV1::Override& outSkeletonOverride, const HierarchicalSkeletonDescriptionV1::OverrideScales& skeletonOverrideScales, const GenericSpecificConverterMapping& converterMapping);
    GLM_CORE_API void skeletonOverrideToOverrideScales(HierarchicalSkeletonDescriptionV1::OverrideScales& outSkeletonOverrideScales, const HierarchicalSkeletonDescriptionV1::Override& skeletonOverride, const GenericSpecificConverterMapping& converterMapping);

} // namespace glm
