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

#pragma once

#include "glmPathFollowerConfiguration.h"
#include "glmNavigationBehavior.h"
#include "glmNavigationData.h"
#include "glmStaticTargetProvider.h"

#include <glmAbstractUserBehavior.h>
#include <glmSDKAPI.h>
#include <glmNavigationData.h>
#include <glmVector3.h>

namespace glm
{
    class PathEntityComponent;
    class AbstractPathFollower;
    class AbstractReachTargetNavigator;
    class FaceNavigator;
    class PerceptionContext;
    class PathContext;

    // Goto behavior, provides a navigation pipeline to an entity
    class GLM_SDK_API GotoBehavior : public glm::AbstractUserBehavior
    {
    public:
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation)
        class GotoBehaviorData
        {
        public:
            // runtime data. Can we share pathFollower ?
            glm::Array<TargetData> _targetList;
            float _timeSinceLastTargetUpdate;
            AbstractPathFollower* _pathFollower;
            bool _forceTargetUpdate;
            PathFollowerConfiguration _pathFollowerConfiguration;
            AbstractTargetProvider::SP _targetProvider; // if null(default) uses the master

            float _pathLateralOffset; // lateral offset from current path
            float _pathSpreadRatio;

            bool _stopOnTargetReached;

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

            GotoBehaviorData();
            ~GotoBehaviorData();
        };

        PathFollowerConfiguration _pathFollowerConfigurationMaster;
        float _targetUpdatePeriod;

    private:
        AbstractTargetProvider::SP _targetProviderMaster;
        glm::Array<GotoBehaviorData> _gotoData;
        PathContext* _pathContext = NULL;

    public:
        /**
		* \brief Builds a GotoBehavior having its own underlying NavigationBehavior.
		* \param targetProvider The target provider of the goto.
		* \param PathEntityComponent The Path component on which the behavior works.
		* \param configuration The configuration of the "path follower" performing the goto.
		*/
        GotoBehavior(
            AbstractTargetProvider* targetProvider = new StaticTargetProvider(),
            const PathFollowerConfiguration& configuration = PathFollowerConfiguration::defaultConfiguration());

        virtual ~GotoBehavior();

        void setTargetProvider(AbstractTargetProvider* targetProvider); // master version
        inline AbstractTargetProvider* getTargetProvider() const;

        inline GotoBehaviorData& getData(int64_t index);

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

    protected:
        // cloning
        friend class CloneFactory;
        inline virtual GotoBehavior* 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 ResultStatus::Value doStop(int64_t index, BehaviorGraph& graph, Time dt) override;

    private:
        void clearNavigationData(int64_t index, BehaviorGraph& graph);
        bool updateTarget(int64_t index, BehaviorGraph& graph, NavigationData& navigationData, const Vector3& entityPosition, float lateralClearance, Time dt);

        bool computeTarget(int64_t index, BehaviorGraph& graph, const Vector3& entityPosition, float lateralClearance, NavigationData& navigationData, const glm::GlmString& errorMessagePrefix, bool force = false);
    };

    //-------------------------------------------------------------------------
    inline GotoBehavior::GotoBehaviorData& GotoBehavior::getData(int64_t index)
    {
        return _gotoData[index];
    }

    //-------------------------------------------------------------------------
    inline AbstractTargetProvider* GotoBehavior::getTargetProvider() const
    {
        return _targetProviderMaster.getImpl();
    }

    //-------------------------------------------------------------------------
    inline void GotoBehavior::setTargetProvider(AbstractTargetProvider* targetProvider)
    {
        _targetProviderMaster = targetProvider;
        _targetProviderMaster->_userBehavior = this;

        size_t entityCount = _gotoData.size();
        for (size_t i = 0; i < entityCount; ++i)
        {
            GotoBehaviorData& data = _gotoData[i];
            data._forceTargetUpdate = true;
            data._targetProvider = _targetProviderMaster;
        }
    }

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