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

#pragma once

#include "glmSDKAPI.h"
#include "glmSimuEntityHandle.h"
#include <glmPODArray.h>
#include <glmSet.h>

#include <glmMutex.h>
#include "glmPathDefinitions.h"
#include "glmFastBoundingBox.h"
#include "glmVoxelField.h"

namespace glm
{
    class PerceptionContext;
    class PerceptionEntityComponent;
    class NavigationMesh3;
    struct GLM_SDK_API PerceptionZoneEntity
    {
		GLM_CLASS_STAT_ID(glm::MemoryStatIds::GolaemShared)

        PerceptionContext* _perceptionContext = NULL;
        SimuEntityHandle _entityHandle;
        bool _isValid = false;

        PerceptionEntityComponent* _perceptionComponent = NULL; // optimisation data
    };

    struct GLM_SDK_API PerceptionZoneObstacle
    {
        // former PerceptionZoneObstacleData, attributes reordered for alignment and padding optimization
        Vector3 _obstacleNavmeshLocalCenter;
        float _obstacleLength;

        NavigationMeshEdgeId _edgeId;

        inline bool operator==(const PerceptionZoneObstacle& other) const;
    };

    class GLM_SDK_API PerceptionZone
    {
    public:
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

    protected:
        glm::PODArray<PerceptionZoneEntity*> _entities;
        glm::PODArray<NavigationMesh3*> _navMeshes;
        glm::Array<glm::Array<PerceptionZoneObstacle>*> _obstacles; // per spatialEnvironment/navMesh, pointers to avoid reallocation when adding a navMesh

    public:
        PerceptionZone();
        ~PerceptionZone();

        inline size_t getPerceptionEntityCount() const;
        inline const PerceptionZoneEntity* getPerceptionEntity(size_t index) const;
        inline PerceptionZoneEntity* touchPerceptionEntity(size_t index);
        bool addPerceptionEntity(PerceptionContext* perceptionContext, SimuEntityHandle entityHandle);
        bool removePerceptionEntity(PerceptionContext* perceptionContext, SimuEntityHandle entityHandle);
        bool findPerceptionEntity(PerceptionContext* perceptionContext, SimuEntityHandle entityHandle, size_t& foundEntityIdx);

        inline size_t getNavMeshCount() const;
        inline NavigationMesh3* getNavMesh(int navMeshIdx) const;
        bool addNavMeshObstacles(int navMeshIdx, NavigationMesh3* navMesh);
        bool removeNavMeshObstacles(int navMeshIdx);

        inline size_t getPerceptionObstacleCount(int navMeshIdx) const;
        inline const PerceptionZoneObstacle& getPerceptionObstacle(int navMeshIdx, size_t index) const;

        void clear();

    private:
        mutable RWSpinLock _modificationMutex;
    };

    //-----------------------------------------------------------------------------
    inline size_t PerceptionZone::getPerceptionEntityCount() const
    {
        return _entities.size();
    }

    //-----------------------------------------------------------------------------
    inline const PerceptionZoneEntity* PerceptionZone::getPerceptionEntity(size_t index) const
    {
        return _entities[index];
    }

    //-----------------------------------------------------------------------------
    inline PerceptionZoneEntity* PerceptionZone::touchPerceptionEntity(size_t index)
    {
        return _entities[index];
    }

    //-----------------------------------------------------------------------------
    inline bool PerceptionZoneObstacle::operator==(const PerceptionZoneObstacle& other) const
    {
        return _edgeId == other._edgeId;
    }

    //-----------------------------------------------------------------------------
    inline size_t PerceptionZone::getNavMeshCount() const
    {
        return _obstacles.size();
    }

    //-----------------------------------------------------------------------------
    inline NavigationMesh3* PerceptionZone::getNavMesh(int navMeshIdx) const
    {
        return (navMeshIdx >= _navMeshes.sizeInt()) ? NULL : _navMeshes[navMeshIdx];
    }

    //-----------------------------------------------------------------------------
    inline size_t PerceptionZone::getPerceptionObstacleCount(int navMeshIdx) const
    {
        return (navMeshIdx >= _obstacles.sizeInt() || _obstacles[navMeshIdx] == NULL) ? 0 : (*_obstacles[navMeshIdx]).size();
    }

    //-----------------------------------------------------------------------------
    inline const PerceptionZoneObstacle& PerceptionZone::getPerceptionObstacle(int navMeshIdx, size_t index) const
    {
        return (*_obstacles[navMeshIdx])[index];
    }
} // namespace glm
