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

#pragma once

#include "glmSDKAPI.h"
#include "glmMotionDefinitions.h"
#include "glmAbstractMotionBehavior.h"
#include "glmMassSpringBN.h"

namespace glm
{
    // MassSpring on a limb to add automatic animation from spring & damping
    class GLM_SDK_API MassSpringBehavior : public AbstractMotionBehavior
    {
    public:
        class MassSpringBehaviorData
        {
        public:
            PODArray<MassSpringRequest*> _massSpringRequest;
            float _coeff;

            Vector3 _previousPosition;
            Vector3 _previousForce;

            float _damping;
            float _spring;
            float _weight;
            float _boneLenghSmoothingFactor = 1.f;

            //int _frameDelay;
        public:
            GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

            MassSpringBehaviorData();
            ~MassSpringBehaviorData();
        };

        int _maxIterationsCount;
        float _distanceThreshold;

    private:
        MassSpringBN* _massSpringBlendNode;

        glm::Array<MassSpringBehaviorData> _massSpringBehaviorData;

        PODArray<int> _defaultBoneIndex;
        int _defaultChainBonesCount;
        bool _isPostIK;

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

        MassSpringBehavior();
        virtual ~MassSpringBehavior();

        // Set the bone that should aim at the target
        inline void setMassSpringEndBones(PODArray<int>& boneIndices);

        //// Set the maximal bone chain length
        inline void setBonesChainCount(int massSpringChainBonesCount);
        inline void setIsPostIK(bool isPostIK);

        inline void setSpring(int64_t index, float springCoef);
        inline void setDamping(int64_t index, float dampingCoef);
        inline void setWeight(int64_t index, float weight);
        //inline void setFrameDelay(int64_t index, int frameDelay);
        inline void setBoneLenghSmoothingFactor(int64_t index, float boneLenghSmoothingFactor);

        // type checking - value derived from this type
        virtual bool isThisType(const AbstractBehavior* value) const override;

    protected:
        // cloning
        friend class CloneFactory;
        inline virtual MassSpringBehavior* create() const override;

        virtual void doAllocate(BehaviorGraph& graph) override;
        virtual void doDeallocate() override;
        virtual void doResize(BehaviorGraph& graph) override;
        virtual void doInit(BehaviorGraph& graph) override;
        virtual void doFinish(BehaviorGraph& graph) override;
        virtual void doInit(int64_t index, BehaviorGraph& graph) override;
        virtual void doFinish(int64_t index, BehaviorGraph& graph) override;
        virtual void doStart(int64_t index, BehaviorGraph& graph) override;
        virtual void doUpdate(int64_t index, BehaviorGraph& graph, Time dt) override;
        virtual AbstractBehavior::ResultStatus::Value doStop(int64_t index, BehaviorGraph& graph, Time dt) override;

    private:
        void computeMassSpring(int64_t index, BehaviorGraph& graph, Time dt);

        void clearRequests(int64_t index);
    };

    //-----------------------------------------------------------------------------
    inline void MassSpringBehavior::setMassSpringEndBones(PODArray<int>& boneIndices)
    {
        _defaultBoneIndex = boneIndices;
    }

    //-----------------------------------------------------------------------------
    inline void MassSpringBehavior::setBonesChainCount(int chainBonesCount)
    {
        _defaultChainBonesCount = chainBonesCount;
    }

    //-----------------------------------------------------------------------------
    inline void MassSpringBehavior::setIsPostIK(bool isPostIK)
    {
        _isPostIK = isPostIK;
    }

    //-----------------------------------------------------------------------------
    inline void MassSpringBehavior::setSpring(int64_t index, float springCoef)
    {
        _massSpringBehaviorData[index]._spring = springCoef;
    }

    //-----------------------------------------------------------------------------
    inline void MassSpringBehavior::setDamping(int64_t index, float dampingCoef)
    {
        _massSpringBehaviorData[index]._damping = dampingCoef;
    }

    //-----------------------------------------------------------------------------
    inline void MassSpringBehavior::setWeight(int64_t index, float weight)
    {
        _massSpringBehaviorData[index]._weight = weight;
    }

    //-----------------------------------------------------------------------------
    inline void MassSpringBehavior::setBoneLenghSmoothingFactor(int64_t index, float boneLenghSmoothingFactor)
    {
        _massSpringBehaviorData[index]._boneLenghSmoothingFactor = boneLenghSmoothingFactor;
    }

    //-------------------------------------------------------------------------
    inline MassSpringBehavior* MassSpringBehavior::create() const
    {
        MassSpringBehavior* myNewMassSpringBehavior = GLM_NEW(MassSpringBehavior, myNewMassSpringBehavior, getClassMemoryStatId());
        return myNewMassSpringBehavior;
    }
} // namespace glm
