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

#pragma once

//golaem motion includes
#include "glmSDKAPI.h"
#include "glmMotionDefinitions.h"
#include <glmAbstractSpecificAnimation.h>

//golaem core includes
#include <glmHierarchicalSkeletonDescription.h>

namespace glm
{
    class SpecificPosture;

    /**
	 * \brief Represent a generic normalized animation
	 *
	 * \author dev@golaem.com
	 *
	 */
    class GLM_SDK_API BvhAnimation : public AbstractSpecificAnimation
    {
    public: //static methods
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)
        static AnimationInterface* BvhAnimationLoadCallback(const char* animationFilePath, int, const char*);

        static bool readHIERARCHY(HierarchicalSkeletonDescription& outSkeletonDescription, glm::GlmMap<const HierarchicalBone*, int>& outNumberOfChannelsPerBone, glm::GlmMap<const HierarchicalBone*, glm::Array<glm::GlmString>>& outBonesChannels, std::istream& dataBuffer, bool removeSkeletonEndSites, const distanceUnit::Value fileUnit = distanceUnit::METER);
        static bool readFrame(SpecificPosture& outSpecificPosture, Array<Quaternion>& tmpRestRelativeOrientations, const HierarchicalSkeletonDescription& skeletonDescription, const glm::GlmMap<const HierarchicalBone*, int>& numberOfChannelsPerBone, const glm::GlmMap<const HierarchicalBone*, glm::Array<glm::GlmString>>& bonesChannels, std::istream& motionDataBuffer, const distanceUnit::Value fileUnit = distanceUnit::METER);

    public: //methods
        //************************************************************
        //! @name Constructors and destructor
        //************************************************************
        //@{
        /**
		 * \brief Default constructor
		 */
        BvhAnimation();

        /**
		 * \brief Destructor
		 */
        ~BvhAnimation();
        //@}

        //************************************************************
        //! @name
        //************************************************************
        //@{
        /**
		 * \brief Load the bvh animation file
		 * \param animationPath complete path the the .bvh file
		 */
        bool initMotion(const char* animationPath, bool removeSkeletonEndSites = true, const distanceUnit::Value fileUnit = distanceUnit::METER);
        /**
		 * \brief Load the bvh animation file
		 * \param animationPath complete path the the .bvh file
		 */
        bool initMotion(std::istream& motionDataBuffer, bool removeSkeletonEndSites = true, const distanceUnit::Value fileUnit = distanceUnit::METER);

        /**
		 * \brief Unload the animation
		 */
        void finish();

        /**
		 * \brief Get the animation delta time between each posture
		 */
        inline Time getDeltaTime() const override;

        /**
		 * \brief Get the skeleton description from the BVH file
		 */
        inline const HierarchicalSkeletonDescription* getSkeletonDescription() const override;

        /**
		 * \brief Get the size of the specific postures array
		 */
        inline int getPosturesCount() const override;

        /**
		 * \brief Get the array of the specific postures representing the animation
		 */
        inline const SpecificPosture* getPostures() const override;

        bool cropMotion(int newFirstFrame, int newLastFrame) override;
        bool editMotion(const EditMotionParameters& parameters) override;

        const Array<BlindData>& getBlindDataInfo() const override { return _emptyBlindDataInfo; }
        const PODArray<float>& getBlindData(int) const override { return _emptyFrameBlindData; }
        bool hasGroundNormal() const override { return false; }
        Vector3 getGroundNormalAtFrame(unsigned int /*frameIndex*/, unsigned int /*constraintSequenceIndex*/) const override { return Vector3(0,0,1); }
        //@}

    protected:  //methods
        virtual SpecificPosture* touchPostures() override;
        virtual void setSkeletonDescription(const HierarchicalSkeletonDescription& newSkeletonDescription) override;

        bool readHIERARCHY(std::istream& dataBuffer, bool removeSkeletonEndSites, const distanceUnit::Value fileUnit);
        bool readMOTION(std::istream& dataBuffer, const distanceUnit::Value fileUnit);

    public: //data
        //************************************************************
        //! @name Datas
        //************************************************************
        //@{
        HierarchicalSkeletonDescription _skeletonDescription;
        Time _animationDeltaTime = 0.033;
        Array<SpecificPosture> _animationPostures;

        Array<BlindData> _emptyBlindDataInfo;
        PODArray<float> _emptyFrameBlindData;

        glm::GlmMap<const HierarchicalBone*, int> _numberOfChannelsPerBone;              //TODO: make a simple array here
        glm::GlmMap<const HierarchicalBone*, glm::Array<glm::GlmString>> _bonesChannels; //TODO: make a simple array here
        //@}
    };

    //------------------------------------------------------------
    inline Time
    BvhAnimation::getDeltaTime() const
    {
        return _animationDeltaTime;
    }

    //------------------------------------------------------------
    inline const HierarchicalSkeletonDescription*
    BvhAnimation::getSkeletonDescription() const
    {
        return &_skeletonDescription;
    }

    //------------------------------------------------------------
    inline int
    BvhAnimation::getPosturesCount() const
    {
        return _animationPostures.sizeInt();
    }

    //------------------------------------------------------------
    inline const SpecificPosture*
    BvhAnimation::getPostures() const
    {
        return _animationPostures.begin();
    }

    //------------------------------------------------------------
    inline SpecificPosture* BvhAnimation::touchPostures()
    {
        return _animationPostures.begin();
    }
    
    //------------------------------------------------------------
    inline void BvhAnimation::setSkeletonDescription(const HierarchicalSkeletonDescription& newSkeletonDescription)
    {
        _skeletonDescription = newSkeletonDescription;
    }
} // namespace glm
