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

#pragma once

#include "glmSDKAPI.h"
#include "glmAbstractNavigationMesh.h"

//golaem core includes
#include <glmTaggedTrianglePartition.h>
#include <glmBoundingBox3.h>

namespace glm
{
	/**
	* \brief A 2D navigation mesh based on voxels
	*
	* \ingroup NavMesh
	*
	* \author Golaem <dev@golaem.com> 
	*/
	class GLM_SDK_API VoxelNavigationMesh2 : public AbstractNavigationMesh
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation)

		/// The underlying triangle partition type.
		typedef TaggedTrianglePartition<int, SemanticObjectTypeValue> ComponentTrianglePartition; // unsigned int is SemanticObjectType::value, required for serialization
		
		/**
		* \brief destructor
		*/
		virtual ~VoxelNavigationMesh2() ;

		/**
		* \see INavigationMesh::locate
		*/
		//@{
		bool locate(Vector3& position, NavigationMeshCellId& cellId) const;

		bool locate(Vector2& position, NavigationMeshCellId& cellId) const;
		//@}

		/**
		* \name Cells
		* \see INavigationMesh
		*/
		//@{
		unsigned int getNbCellIds() const;

		bool isCellIdValid(const NavigationMeshCellId& cellId) const;

		const Triangle3& getCellTriangle3(const NavigationMeshCellId& cellId) const;

		const Triangle2& getCellTriangle2(const NavigationMeshCellId& cellId) const;

		NavigationMeshEdgeId getCellEdge(const NavigationMeshCellId& cellId, unsigned int idxEdge) const;

		unsigned int getCellNbNeighbors(const NavigationMeshCellId& cellId) const;

		NavigationMeshCellId getCellNeighbor(const NavigationMeshCellId& cellId, unsigned int idxNeighbor) const;

		unsigned int getCellNbStackedCells(const NavigationMeshCellId & cellId) const;

		NavigationMeshCellId getCellStackedCell(const NavigationMeshCellId & cellId, unsigned int idxStackCell) const;

		bool areStacked(const NavigationMeshCellId & cellId1, const NavigationMeshCellId & cellId2) const;

		bool areNeighbors(const NavigationMeshCellId& cellId1, const NavigationMeshCellId& cellId2) const;

		SemanticObjectTypeValue getCellSemanticObject(const NavigationMeshCellId& edgeId) const;
		//@}

		/**
		* \name Edges
		* \see INavigationMesh
		*/
		//@{
		unsigned int getNbEdgeIds() const;

		bool isEdgeIdValid(const NavigationMeshEdgeId& edgeId) const;

		const Segment3& getEdgeSegment3(const NavigationMeshEdgeId& edgeId) const;

		const Segment2& getEdgeSegment2(const NavigationMeshEdgeId& edgeId) const;

		unsigned int getEdgeNbCells(const NavigationMeshEdgeId& edgeId) const;

		const NavigationMeshCellId& getEdgeCell(const NavigationMeshEdgeId& edgeId, unsigned int idxCell) const;

		SemanticObjectTypeValue getEdgeSemanticObject(const NavigationMeshEdgeId& edgeId) const;

		bool isEdgeShared(const NavigationMeshEdgeId& edgeId) const;
		//@}

		/**
		* \brief Convert coordinates from golaem to recast
		*/
		static void golaemToRecast(const Vector3& glmPos, float* recastPos);

		/**
		* \brief Convert coordinates from golaem to recast
		*/
		static void recastToGolaem(const float* recastPos, Vector3& glmPos);

	protected:

		/**
		* \brief Default Constructor
		*/
		VoxelNavigationMesh2();

		/**
		* \brief Deallocation of all heap memory allocated by this class for its members.
		*/
		void clear();

		/// Triangle partition representing the 2d subdivision
		ComponentTrianglePartition _trianglePartition;

		/// Cells 2D triangles cache / NavigationMeshCellId
		mutable glm::PODArray< Triangle2* > _cellsTriangles2Cache;

		/// Cells 3D triangles cache / NavigationMeshCellId
		mutable glm::PODArray< Triangle3* > _cellsTriangles3Cache;

		/// Cells neighbors cache / NavigationMeshCellId
		mutable glm::PODArray< StaticArray<NavigationMeshCellId, 3>* > _cellsNeighborsCache;

		/// Edges cells cache / NavigationMeshEdgeId
		mutable glm::PODArray< StaticArray<NavigationMeshCellId, 2>* > _edgesCellsCache;

		/// Edges 2D segments cache / NavigationMeshEdgeId
		mutable glm::PODArray< Segment2* > _edgesSegments2Cache;

		/// Edges 3D segments cache / NavigationMeshEdgeId
		mutable glm::PODArray< Segment3* > _edgesSegments3Cache;

		/// Cells semantic objects
		glm::Array<SemanticObjectTypeValue> _cellsSemanticObjects;

		/// 2D to 3D points cache
		mutable glm::GlmMap<Vector2, float> _points2dTo3d;

		/**
		* \brief Bounding box of the component
		*/
		BoundingBox3 _boundingBox;

		/**
		* \brief Returns the Z coordiante of a 2d point
		*/
		float getZCoordinate(const Vector2& point2, float zHint) const;

	private:

		GLM_DISALLOW_COPY_AND_ASSIGN(VoxelNavigationMesh2);

		friend class VoxelNavigationMesh2Creator;

	} ;
}
