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

#pragma once

#include "glmSDKAPI.h"
#include "glmPathDefinitions.h"

namespace glm
{
	class Vector3;
	class Triangle3;
	class Triangle2;
	class Segment3;
	class Segment2;

	/**
	* \defgroup NavMesh Navigation Meshes
	*
	* \ingroup Environment
	*
	* \author Golaem <dev@golaem.com>
	*
	* \brief Navigation meshes interfaces, classes, creators...
	*/

	/**
	* \brief Partial implementation of the INavigationMesh interface providing implementation for some methods.
	*
	* \ingroup NavMesh
	*
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_SDK_API AbstractNavigationMesh
	{
	public:

		GLM_ABSTRACTCLASS_STAT_ID( glm::MemoryStatIds::Simulation);

		AbstractNavigationMesh();
		virtual ~AbstractNavigationMesh();

		/**
		* \brief returns the edgeType of a given Edge (by ID)
		*/
		NavMeshEdgeType::Value getEdgeType(const NavigationMeshEdgeId & edgeId) const;

		/**
		* \brief Update Edges Types cache (resize to current id count, and refresh data)
		*/
		void updateEdgeTypes();

		/**
		* \brief Locate the given position in the navigation mesh.
		*
		* This method uses two parameter both as input is output. It proceed as follows :
		* \li The given location (if the identifier is not -1) is used as a starting point for the location search,
		* \li The given position is located in the navigation mesh,
		* \li The returned location is the identifier of the cell on which the returned position lies
		*	(-1 if the given position doesn't exist in the navigation mesh),
		* \li If the returned location is valid, the the given position is projected along the vertical axis
		* on the navigation mesh (not the ground), if the distance between the projection and the original
		* position is less than the \ref locateAltitudeTolerance "tolerance", the returned position is the
		* projection, otherwise, the process fails, and the returned location is invalid.
		*
		* \param[in,out] position The position.
		* \param[in,out] cell The location.
		* \return True if the given position can be located in the navigation mesh.
		*/
		virtual bool locate(Vector3 & position, NavigationMeshCellId & cellId) const = 0;

		/**
		* \name Cells
		*/
		//@{

		/**
		* \brief Retrieve the number of cell identifiers handled by this navigation mesh.
		* This value might be different from the number of cells if some cellIds are not valid in this navigationMesh.
		*/
		virtual unsigned int getNbCellIds() const = 0;

		/**
		* \brief Check if a given cell id is valid within this navigation mesh.
		*/
		virtual bool isCellIdValid(const NavigationMeshCellId & cellId) const = 0;

		/**
		* \brief Retrieve a navigation cell's triangle.
		* \param cell The cell's identifier.
		* \return The 3D triangle.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual const Triangle3 & getCellTriangle3(const NavigationMeshCellId & cellId) const = 0;

		/**
		* \brief Retrieve a navigation cell's triangle 2D projection.
		* \param cell The cell's identifier.
		* \return The 2D triangle.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual const Triangle2 & getCellTriangle2(const NavigationMeshCellId & cellId) const = 0;

		/**
		* \brief Retrieve a navigation cell's edge.
		* \param cellId The cell's identifier.
		* \param idxEdge The index of the edge in the cell (0, 1 or 2).
		* \return The Edge identifier.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual NavigationMeshEdgeId getCellEdge(const NavigationMeshCellId & cellId, unsigned int idxEdge) const = 0;

		/**
		* \brief Retrieve the number of a cell's neighbors.
		* \param cellId The cell's identifier.
		* \return The number of neighbors (0, 1 or 2).
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual unsigned int getCellNbNeighbors(const NavigationMeshCellId & cellId) const = 0;

		/**
		* \brief Retrieve a navigation cell's neighbor.
		* \param cellId The cell's identifier.
		* \param idxNeighbor The index of the neighbor (0, 1 or 2).
		* \return The neighbor's cell identifier.
		* \pre The given identifier and the index of the neighor must be valid in this navigation mesh.
		*/
		virtual NavigationMeshCellId getCellNeighbor(const NavigationMeshCellId & cellId, unsigned int idxNeighbor) const = 0;

		/**
		* \brief Retrieve the semantic object instance attached to the given cell.
		* \param cell The cell's identifier.
		* \return The semantic object instance.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual SemanticObjectTypeValue getCellSemanticObject(const NavigationMeshCellId & cellId) const = 0;

		/**
		* \brief Retrieve the number of a cell which projection overlaps a given one.
		* \param cellId The cell's identifier.
		* \return The number of stacked cells.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual unsigned int getCellNbStackedCells(const NavigationMeshCellId & cellId) const = 0;

		/**
		* \brief Retrieve a cell which projection overlaps a given one.
		* \param cellId The cell's identifier.
		* \param idxStackCell The index of the stacked cell.
		* \return The stacked cell identifier.
		* \pre The given identifier and the index of the stacked cell must be valid in this navigation mesh.
		*/
		virtual NavigationMeshCellId getCellStackedCell(const NavigationMeshCellId & cellId, unsigned int idxStackCell) const = 0;

		/**
		* \brief Check if two cells' projections overlap.
		* \param cellId1 The first cell.
		* \param cellId2 The second cell.
		* \return True if the two cells projections overlap.
		* \pre The given identifiers must be valid in this navigation mesh.
		*/
		virtual bool areStacked(const NavigationMeshCellId & cellId1, const NavigationMeshCellId & cellId2) const = 0;

		/**
		* \brief Check if two cells are neighbors.
		* \param cellId1 The first cell.
		* \param cellId2 The second cell.
		* \return True if the two cells are neighbors.
		* \pre The given identifiers must be valid in this navigation mesh.
		*/
		virtual bool areNeighbors(const NavigationMeshCellId & cellId1, const NavigationMeshCellId & cellId2) const = 0;
		//@}

		/**
		* \name Edges
		*/
		//@{
		/**
		* \brief Retrieve the number of edges in this navigation mesh.
		*/
		virtual unsigned int countEdges() const;

		/**
		* \brief Retrieve the number of edge identifiers handled by this navigation mesh.
		* This value might be different from the number of edges if some edgeIds are not valid in this navigationMesh.
		*/
		virtual unsigned int getNbEdgeIds() const = 0;

		/**
		* \brief Check if a given edge id is valid within this navigation mesh.
		*/
		virtual bool isEdgeIdValid(const NavigationMeshEdgeId & edgeId) const = 0;

		/**
		* \brief Retrieve an edge's segment.
		* \param edge The edge's identifier.
		* \return The 3D segment.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual const Segment3 & getEdgeSegment3(const NavigationMeshEdgeId & edgeId) const = 0;

		/**
		* \brief Retrieve an edge's segment 2D projection.
		* \param edge The edge's identifier.
		* \return The 2D edge.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual const Segment2 & getEdgeSegment2(const NavigationMeshEdgeId & edgeId) const = 0;

		/**
		* \brief Retrieve the number of cells that share a given edge.
		* \param edge The edge's identifier.
		* \return The number of cells that shares this edge.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual unsigned int getEdgeNbCells(const NavigationMeshEdgeId & edgeId) const = 0;

		/**
		* \brief Retrieve an edge's cell.
		* \param edge The edge's identifier.
		* \param idxCell The index of the cell in the edge.
		* \return The Cell identifier.
		* \pre The given identifier and indices must be valid in this navigation mesh.
		*/
		virtual const NavigationMeshCellId & getEdgeCell(const NavigationMeshEdgeId & edgeId, unsigned int idxCell) const = 0;

		/**
		* \brief Retrieve the semantic object instance attached to the given edge.
		* \param edgeId The edge's identifier.
		* \return The semantic object instance.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual SemanticObjectTypeValue getEdgeSemanticObject(const NavigationMeshEdgeId & edgeId) const = 0;

		/**
		* \brief Check if the given edge is tagged as shared (i.e. might be shared between two navigation meshes / navigation meshes components).
		* \param edgeId The edge's identifier.
		* \return True if the edge is shared.
		* \pre The given identifier must be valid in this navigation mesh.
		*/
		virtual bool isEdgeShared(const NavigationMeshEdgeId & edgeId) const = 0;
		//@}

		/**
		* \name GraphicInfos
		*/
		//@{
		/**
		* \brief Compute a 3D multisegment representing the edges.
		* \param[out] geometry The geometry.
		*/
		virtual void computeEdgesGeometry(glm::Array<Segment3> & geometry, const std::pair<Vector2, Vector2 >& boundingBox = std::make_pair(Vector2(0.f), Vector2(0.f))) const;
		virtual void computeSpecificEdgesGeometry(glm::Array<Segment3> & geometry, const glm::PODArray<NavigationMeshEdgeId>& edgeIds, const std::pair<Vector2, Vector2 >& boundingBox = std::make_pair(Vector2(0.f), Vector2(0.f))) const;

		/**
		* \brief Compute a 3D multisegment representing the edges having the given role.
		* \param[out] geometry The geometry.
		* \param[in] semanticType The desired role RTTI infos.
		*/
		virtual void computeEdgesGeometry(glm::Array<Segment3> & geometry, const SemanticObjectTypeValue & semanticType, const std::pair<Vector2, Vector2 >& boundingBox = std::make_pair(Vector2(0.f), Vector2(0.f))) const;

		/**
		* \brief Compute a 3D multisegment representing the shared edges.
		* \param[out] geometry The geometry.
		*/
		virtual void computeSharedEdgesGeometry(glm::Array<Segment3> & geometry, const std::pair<Vector2, Vector2 >& boundingBox = std::make_pair(Vector2(0.f), Vector2(0.f))) const;

		/**
		* \brief Compute a list of triangles representing the cells.
		* \param[out] geometry The geometry.
		*/
		virtual void computeCellsGeometry(glm::Array<Triangle3>& geometry, const std::pair<Vector2, Vector2 >& boundingBox = std::make_pair(Vector2(0.f), Vector2(0.f))) const;
		virtual void computeSpecificCellsGeometry(glm::Array<Triangle3>& geometry, const glm::PODArray<NavigationMeshCellId>& cellIds, const std::pair<Vector2, Vector2 >& boundingBox = std::make_pair(Vector2(0.f), Vector2(0.f))) const;

		/**
		* \brief Compute a list of triangles representing the cells having the given role.
		* \param[out] geometry The geometry.
		* \param[in] semanticType The desired role RTTI infos.
		*/
		virtual void computeCellsGeometry(glm::Array<Triangle3> & geometry, const SemanticObjectTypeValue & semanticType, const std::pair<Vector2, Vector2 >& boundingBox = std::make_pair(Vector2(0.f), Vector2(0.f))) const;
		//@}

	protected:

		/// keep a cache of each edge type (needs to be udpated by updateEdgeTypes method whenever required)
		// stores edgeTypes in a cache data, for fast access, as it is heavily used
		glm::Array< NavMeshEdgeType::Value > _edgeTypes;

	private:

		GLM_DISALLOW_COPY_AND_ASSIGN(AbstractNavigationMesh);
	};

	//*****************************************************************************
	// AbstractNavigationMesh inline definitions
	//*****************************************************************************

	//-----------------------------------------------------------------------------
	// getEdgeType
	//-----------------------------------------------------------------------------
	inline NavMeshEdgeType::Value AbstractNavigationMesh::getEdgeType(const NavigationMeshEdgeId & edgeId) const
	{
		GLM_DEBUG_ASSERT((unsigned int)edgeId < getNbEdgeIds());
		return _edgeTypes[edgeId];
	}
}
