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

#pragma once

#include "glmSDKAPI.h"
#include "glmAnimationInterface.h"
#include "glmMotionDefinitions.h"
#include "glmBlindData.h"

#include <glmGolaemMotionDeclarations.h> // GolaemMotion declarations

namespace glm
{
    struct GenericAnimationV5;
    struct GenericAnimationV7;
    struct GenericAnimationV8;
    struct GenericAnimationV9;
    struct GenericAnimationV10;
    struct GenericAnimationV11;

    /**
	* \brief Represent a generic normalized animation
	*
	* \author dev@golaem.com
	*
	*/
    class GLM_SDK_API GenericAnimationV12 : public AnimationInterface
    {
        friend class GenericSpecificConverter;

        GLM_ABSTRACT_RTTI("25097ab7-21f8-4c5f-bac0-deab055c31e8", glm::GenericAnimationV12, addAttributes)

    public: //static methods
        static void computeNextLoopOrigin(Vector3& loopOriginNormalizedPosition, Quaternion& loopOriginOrientation, const GenericAnimationV12& animation);
        static void computeLastLoopOrigin(Vector3& loopOriginNormalizedPosition, Quaternion& loopOriginOrientation, const GenericAnimationV12& animation);

        static void editMotion(GenericAnimationV12& editedMotion, const GenericAnimationV12& originalMotion, const EditMotionParameters& parameters);
        static void computeSummedSquareDistances(const GenericAnimationV12& genericAnimation, int referenceFrame, glm::Array<float>& outDistances, float& outMaxDistance, int startFrame = -1, int endFrame = -1);

        static AnimationInterface* genericAnimationLoadCallback (const char* animationFilePath, int, const char*);

    public: //methods
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)


        GenericAnimationV12();
        GenericAnimationV12(const GenericAnimationV12& genericAnimation);
        ~GenericAnimationV12();

        void createFromV5(GenericAnimationV5& genericAnimationV5);
        void createFromV7(GenericAnimationV7& genericAnimationV7, int motionFileVersionNumber);
        void createFromV8(GenericAnimationV8& genericAnimationV8);
        void createFromV9(GenericAnimationV9& genericAnimationV9);
        void createFromV10(GenericAnimationV10& genericAnimationV10);
        void createFromV11(GenericAnimationV11& genericAnimationV11);

        /**
		* \brief Deep copy
		*/
        GenericAnimationV12& operator=(const GenericAnimationV12& source);

        //************************************************************
        //! @name Initialization & construction
        //************************************************************
        //@{
        /**
		* \brief The init method should be called whenever posture are feed to the animation and channel info should be computed
		*/
        void init();

        /**
		* \brief Finish the GeneralizedAnimation (remove all postures and channels)
		*/
        void finish();

        /**
		* \brief Build an animation with default postures only
		*/
        void buildNullAnimation(const GenericSkeleton* genericSkeleton, Time deltaTime, int nbPostures);

        /**
		* \brief Build an animation from a single posture
		*/
        void buildAnimationFromPosture(const GenericPosture& genericPosture);

        LoadMotionErrorCode::Value loadMotion(const char* filePath, int* retFileVersionNumber = NULL);
        SaveMotionErrorCode::Value saveMotion(const char* filePath) const;
        LoadMotionErrorCode::Value loadMotionXML(const char* filePath, int* retFileVersionNumber = NULL);
        SaveMotionErrorCode::Value saveMotionXML(const char* filePath) const;
        //@}

        //************************************************************
        //! @name Compute and access statistics on the animation
        //************************************************************
        //@{
        void getTotalDeltaPosition(Vector3& result) const;
        Vector3 getTotalNormalizedDeltaPosition() const;
        void getTotalNormalizedDeltaPosition(Vector3& result) const;
        void getTotalDeltaOrientation(Quaternion& result) const;
        glm::Angle computeTotalDeltaHeading() const;
        Vector3 computeAverageMovingDirection() const;
        void computeAverageMovingDirection(Vector3& result) const;
        Vector3 getMovingDirectionAtFrame(unsigned int frameIndex) const;
        void computeFootPrintsFullAutomatic(const glm::PODArray<bool>& limbs);
        void computeFootPrints(const glm::PODArray<bool>& limbs, float heightThreshold, float speedTheshold);

        /**
		* \brief returns true is there is at least one ground sequence in this animation
		*/
        bool groundSequenceAvailable() const;

        const Array<GroundSupportSequence>& getConstraintSequences() const { return _animationInterfaceGroundSequence; }
        bool hasGroundNormal() const override { return false; }
        Vector3 getGroundNormalAtFrame(unsigned int /*frameIndex*/, unsigned int /*constraintSequenceIndex*/) const override { return Vector3(0, 0, 1); }

        const Array<BlindData>& getBlindDataInfo() const { return _blindDataInfo; }
        const PODArray<float>& getBlindData(int frameIndex) const;
        //@}

        //************************************************************
        //! @name
        //************************************************************
        //@{
        /**
		* \brief Compute the _movingDirection for each posture by using the animation total delta position
		*/
        void computePosturesMovingDirection();
        //@}

        void getSpineBoneName(glm::GlmString& name, size_t spineIdx) const;
        void setSpineBoneName(size_t spineIdx, const glm::GlmString& name);

        void getLimbBoneName(glm::GlmString& name, size_t limbIdx) const;
        void setLimbBoneName(size_t limbIdx, const glm::GlmString& name);

        int getLimbBoneIndex(const glm::GlmString name) const;

        void getEffectorBoneName(glm::GlmString& name, size_t effectorIdx) const;
        void setEffectorBoneName(size_t effectorIdx, const glm::GlmString& name);
        void getEffectorBoneName(glm::GlmString& name, size_t channelIdx, size_t effectorInChannelIdx) const;
        void setEffectorBoneName(size_t channelIdx, size_t effectorInChannelIdx, const glm::GlmString& name);

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

        inline int getPosturesCount() const { return static_cast<int>(_posturesArray.size()); }
        inline Time getDeltaTime() const { return _animationDeltaTime; }
        inline int getLimbsConfigurationsCount() const { return static_cast<int>(_limbsConfigurationsArray.size()); }

    protected:  //methods
        /**
		* \brief register the inspectable attributes to the class RTTI
		*/
        static void addAttributes();

        const glm::Array<glm::GlmString>& getEffectorBoneNames() const;
        void setEffectorBoneNames(const glm::Array<glm::GlmString>&);

        bool getInitPostLoad() const;
        void setInitPostLoad(bool);

        void initPostLoad();
        void checkPosturesSkeletonOverrides();

    public: //data
        //************************************************************
        // Data
        //************************************************************
        //@{
        Time _animationDeltaTime;
        glm::Array<GenericPostureV5> _posturesArray;
        glm::Array<LimbConfigurationV0> _limbsConfigurationsArray;
        //@}

        //************************************************************
        //! @name Channels
        //************************************************************
        //@{
        int _channelCount;
        ChannelInfo* _channelsInfo;
        //@}

        glm::Array<glm::GlmString> _spineBoneNames;
        glm::Array<glm::GlmString> _limbBoneNames;
        glm::Array<glm::GlmString> _effectorBoneNames;

        glm::Array<BlindDataV1> _blindDataInfo;

        static char _currentVersionNumber;

    private: //data
        Array<GroundSupportSequenceV0> _animationInterfaceGroundSequence;

    };

} // namespace glm
