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

#pragma once

#include "glmSDKAPI.h"

//golaem core includes
#include <glmMathBase.h>
#include <glmLog.h>
#include <glmTypes.h>
#include <glmSimuEntityHandle.h>
#include <glmSimulation.h>
#include <glmSegment3.h>
#include <glmTriangle3.h>

#define GLM_ROADMAP_ERASE_SUPPORT_ENABLE	0

/**	
* \mainpage Golaem Path
* 
* \image html "GolaemPathLogo.png"

* Golaem Path is a real-time 3D navigation engine for autonomous entities relying on a dedicated, automatically computed, 
* 3D spatial environment. 
*
* Copyright (C) Golaem S.A.  All Rights Reserved.
*
* \section main_sources Sources of information
* The following lines list the different sources of information:
* \li Golaem Path User Manual, describes the concepts behind Golaem Path and aims at providing a good understanding on 
* how it works, one should start by reading this document;
* \li Golaem Path Doxygen, the present document, features the code documentation and usage examples for most classes 
* (including code examples);
* \li http://www.golaem.com/path.php, home page for Path, look here for new releases, contact, and other information.
*
* \section main_softOrganization Software organization
* Golaem Path features are shared between two main libraries and a third one, dedicated to visualization helpers for Golaem
* own <a href="http://www.ogre.org">Ogre</a> based 3D visualization, each has its own C++ namespace:
* \li \ref Environment "glmPathEnvironment" uses namespace \ref glm::environment, it contains the 
* classes related to the spatial environment;
* \li \ref Navigation "glmPathNavigation" uses namespace  \ref glm::navigation, it containes the 
* classes related to the online navigation.
* \li \ref Visualization "glmPathVisualization" uses namespace \ref glm::visualization, it contains utility
* classes used to display Path debug graphic infos using Golaem visualization over OpenGL
*
* \section main_conventions Conventions
* \subsection main_conventions_coordinates 3D Coordinate system
* Path (as well as the other Golaem products) uses a right handed, Z-Up, Cartesian coordinate system; Most visualizations system, 
* OpenGL, Direct3D use a right-handed, Y-Up, Cartesian coordinate system.
*
* The following coordinate transformation should be kept in mind:
* \li xGolaem = -xOgre
* \li yGolaem = -zOgre
* \li zGolaem = yOgre
*
* \image html "CoordinateSystem.png" The right handed Z-up cartesian coordinate system used in Path
* \subsection main_conventions_units Units
* \li \e Lengths (and by extension coordinates) are measured in meters.
* \li \e Time is measured in seconds.
* \li The class used for \e angles \c glm::Angle stores angles measured in radians; an instance can be built from a degree value, 
* it will be converted. In the XY plane, an object whose orientation is 0 is aligned with the x axis.
*/

namespace glm
{
	/**
	* \defgroup Environment Environment
	*
	* \brief Spatial data structures allowing the navigation of autonomous agents in 3D.
	* 
	* \ingroup Path
	*
	* \author Golaem <dev@golaem.com>
	*
	* To allow the navigation of autonomous agents in a 3D environment, Golaem Path relies on several specialized spatial data 
	* structures. The basic framework is made of 3 classes :
	* \li \ref INavigationMesh, interface for navigation meshes, represents the navigable space as a 3D triangulated surface;
	* \li \ref IRoadmap, interface for roadmaps, defines the graph of routes used to compute paths through the navigable space;
	* \li \ref SpatialEnvironment, is the main entry point and is able to translate locations from the navigation space to the scene ground.
	*
	* \dot 
	* digraph framework_architecture {
	* node [shape = "record"]
	* SpatialEnvironment [label = "SpatialEnvironment" URL="\ref glm::SpatialEnvironment"]
	* IRoadmap [label = "IRoadmap" URL="\ref glm::IRoadmap"]
	* INavigationMesh [label = "INavigationMesh" URL="\ref glm::INavigationMesh"]
	* edge [arrowhead = "none" headlabel = "1"
	*		arrowtail = "odiamond" taillabel = "1"]
	* SpatialEnvironment -> IRoadmap
	* SpatialEnvironment -> INavigationMesh
	* }
	* \enddot
	*
	* On top of this framework, various \ref Queries queries can be performed.
	*/

	/**
	* \brief Gse compute error codes
	*/
	struct GLM_SDK_API GseComputationErrorCode
	{
		enum Value
		{
			SUCCESS,
			INVALID_INPUT, // can also mean file not found for file read
			INVALID_OUTPUT, // can also mean file not writable for file write
			FAILURE, // can also mean ivalid file version
			INTERRUPTED,
			END
		} ;
	} ;

	/** 
	* \brief Enumeration describing the different navmesh Edges Types
	*/
	class GLM_SDK_API NavMeshEdgeType
	{
	public:
		enum Value 
		{
			FREE,
			OBSTACLE,
			SHARED, 
			STEP,
			END
		};
	};

	/**
	* \defgroup SemanticEnvironment Semantic Environment
	* \ingroup Environment
	* \brief Semantic environment use in the spatial environment.
	*/
	/** 
	* \brief SemanticObjectType
	*
	* \ingroup SemanticEnvironment
	*
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_SDK_API SemanticObjectType 
	{			
	public:
		/**
		* \brief The enumeration containing the different semantic objects types
		*/
		enum Value
		{
			FREE_BORDER = 0,
			NAVIGABLE_SLOPE,
			NAVIGABLE_SURFACE,
			NON_NAVIGABLE_SURFACE,
			OBSTACLE_BORDER,
			SHARED_BORDER,
			STEP_BORDER,
			END
		};
	};

	class GLM_SDK_API PerceivedObjectType
	{
	public:
		enum Value
		{
			BEGIN = 0,
			ENTITY,
			GROUP,
			STATIC_OBSTACLE,
			USER_OBSTACLE,
			END
		};
	};

	typedef uint32_t SemanticObjectTypeValue;

	/** 
	* \class glm::RoadmapNodeId
	*
	* \brief Unique identifier for a node (ie a waypoint) of a roadmap.
	*
	* \ingroup Roadmap
	*
	* \author Golaem <dev@golaem.com> 
	*/
	typedef int32_t RoadmapNodeId;

	/** 
	* \class glm::RoadmapArcId
	*
	* \brief Unique identifier for an arc of a roadmap.
	*
	* \ingroup Roadmap
	*
	* \author Golaem <dev@golaem.com> 
	*/
	typedef int32_t RoadmapArcId;

	/** 
	* \class glm::NavigationMeshZoneId
	*
	* \brief Unique identifier used for a NavigationMeshZone.
	*
	* \ingroup TopologicalQueries
	*
	* \author Golaem <dev@golaem.com>
	*
	* \see \ref NavigationMeshZone
	*/
	typedef int32_t NavigationMeshZoneId;

	/** 
	* \class glm::NavigationMeshCellId
	*
	* \brief Unique identifier for navigation meshes <tt>Cells</tt>.
	*
	* \ingroup NavMesh
	*
	* \author Golaem <dev@golaem.com>
	*/
	typedef int32_t NavigationMeshCellId;

	/** 
	* \class glm::NavigationMeshEdgeId
	*
	* \brief Unique identifier for navigation meshes <tt>Edges</tt>.
	*
	* \ingroup NavMesh
	*
	* \author Golaem <dev@golaem.com>
	*/
	typedef int32_t NavigationMeshEdgeId;

    struct IDDistancePairPOD
	{
		int32_t		id;
		float	distance;

        void setInvalid()
        {
            id = -1;
            distance = -1;
        }
	};

	struct GLM_SDK_API PopulationSlot
	{
        GLM_CLASS_STAT_ID(glm::MemoryStatIds::Simulation);

    protected:
        Vector3 _shapeExtents = Vector3(0.1f);
        Vector3 _shapeOffset = Vector3(0.f);
        Vector3 _safeDistance = Vector3(0.f);

        Vector3 _position = Vector3(0.f);
        Vector3 _normal = Vector3(0, 0, 1);
        Vector3 _forward = Vector3(1, 0, 0);
        Quaternion _orientation = Quaternion(0, 0, 0, 1);
        
        mutable Vector3 _offsetPosition = Vector3(0.f);

        mutable Segment3 _slotCollisionGeoSegments[4];
        mutable Triangle3 _slotCollisionGeoTriangles[2];

    public:
        bool _decimated = false;
        unsigned int _entityTypeId = 0;
        int _renderingTypeIdx = 0;
        float _scale = 1.f;
        unsigned int _index = 0;
        bool _overridePosition = false;
        bool _overrideForwards = false;
        int _groupId = -1;
        int _groupEntityTypeIdx = -1;
        Vector3 _groupBarycenter = Vector3(0, 0, 0);

    protected:
        float _cachedClearance2d = 0.f;
        float _navMeshClearance = 0.2f;
        mutable bool _offsetPositionDirty = true;
        mutable bool _geoDirty = true;

    public:
        inline const Vector3& getPosition() const;
        inline void setPosition(const Vector3& position);

        inline const Vector3& getNormal() const;
        inline void setNormal(const Vector3& normal);

        inline const Vector3& getForward() const;
        inline void setForward(const Vector3& forward);

        inline const Quaternion& getOrientation() const;
        inline void setOrientation(const Quaternion& orientation);

        inline const Vector3& getShapeExtents() const;
        inline void setShapeExtents(const Vector3& extents);

        inline const Vector3& getShapeOffset() const;
        inline void setShapeOffset(const Vector3& offset);

        inline const Vector3& getSafeDistance() const;
        inline void setSafeDistance(const Vector3& offset);

        inline float getClearance2d() const;

        bool collidesWith(const PopulationSlot* other) const;

        inline const Vector3& getOffsetPostion() const;

        inline const Segment3* getSlotCollisionGeoSegments() const;
        inline const Triangle3* getSlotCollisionGeoTriangles() const;

        inline float getNavMeshClearance() const;

    protected:
        void computeCollisionGeo() const;
        void computeOffsetPosition() const;
        void computeClearance();
    };

    //-----------------------------------------------------------------------------
    inline const Vector3& PopulationSlot::getPosition() const
    {
        return _position;
    }

    //-----------------------------------------------------------------------------
    inline void PopulationSlot::setPosition(const Vector3& position)
    {
        _position = position;
        _geoDirty = true;
        _offsetPositionDirty = true;
    }

    //-----------------------------------------------------------------------------
    inline const Vector3& PopulationSlot::getNormal() const
    {
        return _normal;
    }

    //-----------------------------------------------------------------------------
    inline void PopulationSlot::setNormal(const Vector3& normal)
    {
        _normal = normal;
        _geoDirty = true;
        _offsetPositionDirty = true;
    }

    //-----------------------------------------------------------------------------
    inline const Vector3& PopulationSlot::getForward() const
    {
        return _forward;
    }

    //-----------------------------------------------------------------------------
    inline void PopulationSlot::setForward(const Vector3& forward)
    {
        _forward = forward;
        _geoDirty = true;
        _offsetPositionDirty = true;
    }

    //-----------------------------------------------------------------------------
    inline const Quaternion& PopulationSlot::getOrientation() const
    {
        return _orientation;
    }

    //-----------------------------------------------------------------------------
    void PopulationSlot::setOrientation(const Quaternion& orientation)
    {
        _orientation = orientation;
        _geoDirty = true;
        _offsetPositionDirty = true;
    }

    //-----------------------------------------------------------------------------
    inline const Vector3& PopulationSlot::getShapeExtents() const
    {
        return _shapeExtents;
    }

    //-----------------------------------------------------------------------------
    inline void PopulationSlot::setShapeExtents(const Vector3& extents)
    {
        _shapeExtents = extents;
        computeClearance();
    }

    //-----------------------------------------------------------------------------
    inline const Vector3& PopulationSlot::getShapeOffset() const
    {
        return _shapeOffset;
    }

    //-----------------------------------------------------------------------------
    inline void PopulationSlot::setShapeOffset(const Vector3& offset)
    {
        _shapeOffset = offset;
        _geoDirty = true;
        _offsetPositionDirty = true;
    }

    //-----------------------------------------------------------------------------
    inline const Vector3& PopulationSlot::getSafeDistance() const
    {
        return _safeDistance;
    }

    //-----------------------------------------------------------------------------
    inline void PopulationSlot::setSafeDistance(const Vector3& distance)
    {
        _safeDistance = distance;
        computeClearance();
    }

    //-----------------------------------------------------------------------------
    inline float PopulationSlot::getClearance2d() const
    {
        return _cachedClearance2d;
    }

    //-----------------------------------------------------------------------------
    inline const Vector3& PopulationSlot::getOffsetPostion() const
    {
        if (_offsetPositionDirty)
        {
            computeOffsetPosition();
            _offsetPositionDirty = false;
        }
        return _offsetPosition;
    }

    //-----------------------------------------------------------------------------
    inline const Segment3* PopulationSlot::getSlotCollisionGeoSegments() const
    {
        if (_geoDirty)
        {
            computeCollisionGeo();
            _geoDirty = false;
        }
        return _slotCollisionGeoSegments;
    }

	//-----------------------------------------------------------------------------
    inline const Triangle3* PopulationSlot::getSlotCollisionGeoTriangles() const
    {
        if (_geoDirty)
        {
            computeCollisionGeo();
            _geoDirty = false;
        }
        return _slotCollisionGeoTriangles;
    }

    //-----------------------------------------------------------------------------
    inline float PopulationSlot::getNavMeshClearance() const
    {
        return _navMeshClearance;
    }

    //-----------------------------------------------------------------------------
    inline void PopulationSlot::computeClearance()
    {
        _cachedClearance2d = (_shapeExtents + _safeDistance).projectZ().norm() * 0.5f;
        _navMeshClearance = _cachedClearance2d * 1.1f;
        _geoDirty = true;
    }

} // namespace glm
