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

#pragma once

#include "glmSDKAPI.h"
#include "glmArray.h"
#include "glmPODArray.h"
#include "glmVector2.h"
#include "glmTriangle2.h"
#include "glmIndexTriplet.h"
#include "glmAttribute.h"
#include "glmIndex.h"
#include <utility>

namespace glm
{
	/** 
	* \brief A triangle partition of a 2D space.
	*
	* \ingroup spatialDataStructures
	*
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_SDK_API TrianglePartition
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

		/** 
		* \brief Unique identifier for a TrianglePartition's <tt>Vertex</tt>.
		*/
		/// \cond CUSTOM_INDEX
		typedef int VertexId;
		/// \endcond CUSTOM_INDEX

		/** 
		* \brief Unique identifier for a TrianglePartition's <tt>std::pair<VertexId, VertexId></tt>.
		*/
		/// \cond CUSTOM_INDEX
		typedef int EdgeId;
		/// \endcond CUSTOM_INDEX

		/** 
		* \brief Unique identifier for a TrianglePartition's <tt>Cell</tt>.
		*/
		/// \cond CUSTOM_INDEX
		typedef int CellId;
		/// \endcond CUSTOM_INDEX

	public:

		/** 
		* \name Construction/Destruction
		*/
		//@{
		TrianglePartition();

		//TrianglePartition(const TrianglePartition & partition);

		virtual ~TrianglePartition();

		//TrianglePartition & operator=(const TrianglePartition & partition);

		/**
		* \brief Initialize
		* \param vertices The vertices.
		* \param edges The edges as pair of vertex id.
		* \param cellsEdges The cells defined using their edges.
		* \return True if the partition has been successfully initialized
		*/
		bool init(
			glm::Array< Vector2 > vertices
			, glm::Array< std::pair<VertexId, VertexId> > edges
			, glm::Array< IndexTriplet<EdgeId> > cellsEdges
			, bool& cancel
			);

		/**
		* \brief Clear the data structure.
		*/
		virtual void clear();
		//@}

		/**
		* \name Vertices
		*/
		//@{
		inline unsigned int getNbVertices() const;

		inline const Vector2 & getVertex(const VertexId & index) const;
		//@}

		/**
		* \name Edges
		*/
		//@{
		inline unsigned int getNbEdges() const;

		inline Segment2 getEdgeSegment(const EdgeId & index) const;

		inline const std::pair<VertexId, VertexId> & getEdge(const EdgeId & index) const;

		inline unsigned int getEdgeNbCells(const EdgeId & index) const;

		inline const CellId & getEdgeCell(const EdgeId & index, unsigned int idxCell) const;

		EdgeId getCommonEdge(const CellId & cell1, const CellId & cell2) const ;
		//@}

		/**
		* \name Cells
		*/
		//@{
		inline unsigned int getNbCells() const;

		inline const IndexTriplet<VertexId> & getCellVertices(const CellId & index) const;

		inline const IndexTriplet<EdgeId> & getCellEdges(const CellId & index) const;

		inline Triangle2 getCellTriangle(const CellId & index) const;

		void getCellNeighbors(CellId triangleIndex, glm::PODArray<CellId> & neighbors) const ;
		//@}

		/** 
		* \brief Locate a 2D position in the partition.
		*
		* \param position The position.
		* \param locationHeuristic An approximate location of the given position.
		* \return The location of the given position as a CellId. If the locate fails, it returns CellId(-1).
		*/
		CellId locate(const Vector2 & position, CellId locationHeuristic = CellId(0)) const;
		
		// for unit tests only
        bool operator==(const TrianglePartition& partition) const;

    private:

		GLM_DISALLOW_COPY_AND_ASSIGN(TrianglePartition);

        bool operator!=(const TrianglePartition& /*partition*/) const
        {
            GLM_EXIT("operator!= not implemented for TrianglePartition (who would use it anyway ?)");
        }

    protected:
		
		/**
		* \brief Initialize the triangle partition using existing data structures.
		*/
		bool init( bool& cancel );

		/** 
		* \brief Check if two points are on the same side of a given edge.
		*
		* \param edge The edge.
		* \param p1 The first point.
		* \param p2 The second point.
		* \return true if p1 and p2 are on the same side of the edge.
		*/
		bool sameSide(const std::pair<VertexId, VertexId> & edge, const Vector2 & p1, const Vector2 & p2) const;

		/**
		* \brief The vertices.
		*/
		glm::Array< Vector2 > _vertices;
		/**
		* \brief The edges as pair of VertexId.
		*/
		glm::Array< std::pair<VertexId, VertexId> > _edges2Vertices;
		/**
		* \brief The cells defined using their edges.
		*/
		glm::Array< IndexTriplet<EdgeId> > _cells2Edges;
		/**
		* \brief The cells defined using their vertices.
		*/
		glm::Array< IndexTriplet<VertexId> > _cells2Vertices;
		/**
		* \brief The association between each edge and its neighboring cells.
		*/
		glm::Array< glm::PODArray<CellId> > _edges2Cells; // max size = 2, will be reserved
		/** 
		* \brief The invalid cells.
		*/
		glm::PODArray<CellId> _invalidCells;
	} ;

	//*****************************************************************************
	// TrianglePartition inline definitions
	//*****************************************************************************

	//-----------------------------------------------------------------------------
	// getNbVertices
	//-----------------------------------------------------------------------------
	unsigned int TrianglePartition::getNbVertices() const
	{ 
		return static_cast<unsigned int>( _vertices.size() ) ; 
	}

	//-----------------------------------------------------------------------------
	// getVertex
	//-----------------------------------------------------------------------------
	const Vector2 & TrianglePartition::getVertex(const VertexId & tpvi) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( tpvi, static_cast<int>(_vertices.size() ) ) ) ;
		return _vertices[tpvi] ;
	}

	//-----------------------------------------------------------------------------
	// getNbEdges
	//-----------------------------------------------------------------------------
	unsigned int TrianglePartition::getNbEdges() const
	{ 
		return static_cast<unsigned int>( _edges2Vertices.size() ) ; 
	}

	//-----------------------------------------------------------------------------
	// getEdgeSegment
	//-----------------------------------------------------------------------------
	Segment2 TrianglePartition::getEdgeSegment(const EdgeId & index) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( index, static_cast<int>(_edges2Vertices.size()) ) ) ;
		return Segment2(_vertices[_edges2Vertices[index].first], _vertices[_edges2Vertices[index].second]) ; 
	}

	//-----------------------------------------------------------------------------
	// getEdge
	//-----------------------------------------------------------------------------
	inline const std::pair<TrianglePartition::VertexId, TrianglePartition::VertexId> & TrianglePartition::getEdge(const EdgeId & index) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( index, static_cast<int>(_edges2Vertices.size()) ) ) ;
		return _edges2Vertices[index]; 
	}

	//-----------------------------------------------------------------------------
	// getEdgeNbCells
	//-----------------------------------------------------------------------------
	unsigned int TrianglePartition::getEdgeNbCells(const EdgeId & index) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( index, static_cast<int>( _edges2Cells.size()) ) );
		return static_cast<unsigned int>(_edges2Cells[index].size());
	}

	//-----------------------------------------------------------------------------
	// getEdgeCell
	//-----------------------------------------------------------------------------
	const TrianglePartition::CellId & TrianglePartition::getEdgeCell(const EdgeId & index, unsigned int idxCell) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( index, static_cast<int>(_edges2Cells.size()) ) && idxCell < static_cast<unsigned int>(_edges2Cells[index].size()));
		return _edges2Cells[index][idxCell];
	}

	//-----------------------------------------------------------------------------
	// getNbCells
	//-----------------------------------------------------------------------------
	unsigned int TrianglePartition::getNbCells() const
	{ 
		return static_cast<unsigned int>( _cells2Edges.size() ) ; 
	}

	//-----------------------------------------------------------------------------
	// getCellTriangle
	//-----------------------------------------------------------------------------
	Triangle2 TrianglePartition::getCellTriangle(const CellId & index) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( index, static_cast<int>(_cells2Edges.size()) ) ) ;
		const IndexTriplet<VertexId> & triangle = _cells2Vertices[index] ;
		return Triangle2(_vertices[triangle[0]], _vertices[triangle[1]], _vertices[triangle[2]] ) ;
	}

	//-----------------------------------------------------------------------------
	// getCellVertices
	//-----------------------------------------------------------------------------
	const IndexTriplet<TrianglePartition::VertexId> & TrianglePartition::getCellVertices(const CellId & index) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( index, static_cast<int>(_cells2Edges.size()) ) ) ;
		return _cells2Vertices[index];
	}

	//-----------------------------------------------------------------------------
	// getCellEdges
	//-----------------------------------------------------------------------------
	const IndexTriplet<TrianglePartition::EdgeId> & TrianglePartition::getCellEdges(const CellId & index) const
	{
		GLM_DEBUG_ASSERT( isIndexValid( index, static_cast<int>(_cells2Edges.size()) ) ) ;
		return _cells2Edges[index];
	}
}
