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

#pragma once

#include "glmPathFollowerConfiguration.h"
#include "glmNavigationPath.h"
#include "glmNavigationMesh3.h"
#include "glmNavigationMeshLocation.h"
#include "glmPathContext.h"

namespace glm
{
    class NavigationData;

    /** 
    *
    * \ingroup PathFollower
    *
    * \author Golaem <dev@golaem.com>
    */
    class GLM_SDK_API AbstractPathFollower
    {

        friend class NavigationData;

    public:
        GLM_ABSTRACTCLASS_STAT_ID( glm::MemoryStatIds::Simulation)

        AbstractPathFollower(/*SimuEntityHandle entityHandle, */ const PathFollowerConfiguration& configuration = PathFollowerConfiguration::defaultConfiguration());

        virtual ~AbstractPathFollower();

        virtual bool init(PathContext* pathContext, RandomNumberGenerator& rng);
        bool update(PathContext* pathContext, const NavigationData& navigationData, float radius, Time time);
        const NavigationPath& getPathInLocalRef() const; // used in debug display only, does not include worldMatrix (must add it when used)
        float getEntityCurvilinearAbscissa() const;
        float getLocalTargetCurvilinearAbscissa() const;
        bool hasTarget() const;
        NavigationMeshLocation getTarget() const;
        Vector3 getTargetPosition(const NavigationMesh3& navMesh) const;
        bool setTarget(PathContext* pathContext, const NavigationData& navigationData, float radius, const glm::Array<NavigationMeshLocation>& waypoints, glm::PODArray<size_t>& discardedWaypoints);
        void clearTarget();
        bool isTargetReached() const;
        float getDistance2ToTarget(const NavigationMeshLocation& navigationLocation) const;
        Vector3 getLocalTargetPositionWorld(const NavigationMesh3& navMesh) const;                                                                // includes worldMatrix transform
        Vector3 getLocalTargetFlattenAtEntityPosWorld(const NavigationMesh3& navMesh, const PathEntityComponentLocation& pathCompLocation) const; // includes worldMatrix transform
        //Quaternion getRotationFromPathStartToEntityCurrent(const NavigationMesh3& navMesh, const NavigationMeshCellId& firstPathCellId, const NavigationMeshCellId& entityCurrentCellId) const;

        void useVectorFieldDirection(Vector3 direction);
        void noVectorFieldDirection(const NavigationData& pathComponent);

        const PathFollowerConfiguration* _configuration;
        bool _obstacleDistanceIn3D;

        // filled by the Goto behavior
        float _pathLateralOffset;
        float _pathSpreadRatio;
        unsigned int _firstMainVertex;
        bool _stopOnTargetReached;

        bool _resetAbscissas;

    protected:
        bool checkTargetReached(const NavigationMeshLocation& navigationLocation) const;

        /**
	    * \brief Updates the local target, must be defined by specializations.
	    */
        virtual bool updateLocalTarget(PathContext* pathContext, const NavigationData& pathComponent, float radius) = 0;

        /**
	    * \brief Initialize the local target (called when a new target is set).
	    */
        virtual bool initLocalTarget(PathContext* pathContext, const NavigationData& pathComponent, float radius) = 0;

        /**
	    * \brief Plan the path between the entity and the given location.
	    */
        virtual bool planPath(PathContext* pathContext, const NavigationData& navigationData, const glm::Array<NavigationMeshLocation>& waypoints, glm::PODArray<size_t>& discardedWaypoints);

        NavigationPath _path;

        //SimuEntityHandle						_entityHandle;
        //const NavigationData*	_pathEntityComponent;
        //const ISpatialEnvironmentLocation*	_navigationLocation;
        float _entityCurvilinearAbscissa;
        unsigned int _entityPreviousVertexIndex;

        NavigationMeshLocation _localTarget;
        float _localTargetCurvilinearAbscissa;
        unsigned int _localTargetPreviousVertexIndex;

        // vector field
        bool _useVectorField;
        Vector3 _vectorFieldDirection;

    private:
        /**
	    * \brief Updates the entity current curvilign abscissa on the path.
	    */
        bool updateEntityCurvilinearAbscissa(const NavigationData& navigationData);

        GLM_DISALLOW_COPY_AND_ASSIGN(AbstractPathFollower)

        /**
	    * \brief The time since the last time the local target was updated.
	    */
        Time _timeSinceTargetUpdate;

        /**
	    * \brief Tells if the current target is reached.
	    */
        bool _targetReached;
    };

    //*****************************************************************************
    // AbstractPathFollower inline definitions
    //*****************************************************************************

    //-----------------------------------------------------------------------------
    inline float AbstractPathFollower::getEntityCurvilinearAbscissa() const
    {
        return _entityCurvilinearAbscissa;
    }

    //-----------------------------------------------------------------------------
    inline float AbstractPathFollower::getLocalTargetCurvilinearAbscissa() const
    {
        return _localTargetCurvilinearAbscissa;
    }

    //-----------------------------------------------------------------------------
    inline const NavigationPath& AbstractPathFollower::getPathInLocalRef() const
    {
        return _path;
    }

    //-----------------------------------------------------------------------------
    inline bool AbstractPathFollower::isTargetReached() const
    {
        return _targetReached;
    }

    //-----------------------------------------------------------------------------
    inline void AbstractPathFollower::useVectorFieldDirection(Vector3 direction)
    {
        _useVectorField = true;
        _vectorFieldDirection = direction;
    }

} // namespace glm
