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

#pragma once

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

//golaem core includes
#include <glmBoundingBox3.h>
#include <glmAABBTree.h>
#include <glmIndex.h>

//stl includes
#include "glmString.h"
#include "glmSet.h"
#include "glmMap.h"

namespace glm
{
	class GseIStream;
	class GseOStream;

	class Geometry3; // for friendship declaration

	/**
	* \defgroup Geometry Geometry
	*
	* \ingroup Environment
	*
	* \author Golaem <dev@golaem.com>
	*
	* \brief 3D triangle soup, data structures, loaders and treatments.
	*/

	/**
	* \brief A triangle mesh in 3D.
	*
	* \ingroup Geometry
	*
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_SDK_API Geometry3
	{
		//GLM_VIRTUAL_RTTI("ae441624-ef98-460d-a850-bacb684b4ff7", glm::Geometry3, addAttributes)
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

		/**
		* \class glm::Geometry3::VertexId
		*
		* \brief Geometry's vertex identifier type.
		*/
		typedef int VertexId;

		/**
		* \class glm::Geometry3::EdgeId
		*
		* \brief Geometry's edge identifier type.
		*/
		typedef int EdgeId;

		/**
		* \class glm::Geometry3::FaceId
		*
		* \brief Geometry's face identifier type.
		*/
		typedef int FaceId;

		/**
		* \class glm::Geometry3::Edge
		* \brief Geometry's edge type.
		*/
		typedef std::pair<VertexId, VertexId> Edge;

		/**
		* \brief A face of the geometry.
		*/
		class GLM_SDK_API PathTriangle : public Triangle3
		{
			//GLM_VIRTUAL_RTTI("961601e0-67a8-40ae-abf9-02a04cc35bba", glm::Geometry3::PathTriangle, addAttributes)
		public:

			GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

			/**
			* \brief Default constructor.
			*/
			PathTriangle();

			/**
			* \brief Copy constructor (build a an instance representing the same triangle as the given one).
			*/
			PathTriangle(const PathTriangle& triangle);

			/**
			* \brief Destructor.
			*/
			virtual ~PathTriangle();

			/**
			* \brief Assignment operator (set an instance to represent the same triangle as the given one).
			*/
			PathTriangle& operator=(const PathTriangle& triangle);

			/**
			* \name Accessors
			*/
			//@{
			/**
			* \brief Retrieve the identifier of the triangle in its geometry.
			*/
			const FaceId & getIdentifier() const;

			/**
			* \brief Retrieve the underlying geometry.
			*/
			const Geometry3 & getGeometry() const;

			/**
			* \see Triangle3<ScalarType>::getVertex
			*/
			//const Vector3 & getVertex(unsigned int index) const;

			const VertexId & getVertexId(unsigned int index) const;
			//@}

			/**
			* \brief Strict ordering operator (for STL containers).
			*/
			bool operator<(const PathTriangle & triangle2) const;

			/**
			* \brief Update internal datas after a change.
			*/
			virtual void update();

		protected:

			void setVertexId(unsigned int index, const VertexId & vertexId);

			/**
			* \brief Inspection needed method declaring the inspectable attributes.
			*/
			//static void addAttributes();

			/**
			* \brief Pointer to the geometry that support the triangle.
			*/
			const Geometry3 * _geometry;

			/**
			* \brief Indices of the triangle vertices.
			*/
			VertexId _vertexIds[3];

			/**
			* \brief The identifier of the triangle in the main geometry.
			*/
			FaceId _identifier;

			//#ifdef WIN32
			friend class Geometry3;
			//#endif
		};

	public:
		/**
		* \brief Constructor.
		*/
		Geometry3();

		/**
		* \brief Copy constructor.
		*/
		Geometry3(const Geometry3 & geometry);

		/**
		* \brief Assignment.
		*/
		Geometry3 & operator=(const Geometry3 & geometry);

		/**
		* \brief Destructor.
		*/
		virtual ~Geometry3();

		/**
		* \brief Erase the faces of the geometry.
		*/
		inline void clearFaces();

		/**
		* \brief Clear the geometry.
		*/
		inline void clear();

		/**
		* \name Vertices
		*/
		//@{
		/**
		* \brief Add a vertex in the geometry.
		* \param position The position of the vertex.
		* \return The index of the inserted vertex.
		*/
		VertexId addVertex(const Vector3 & position);

		inline const Vector3 & getVertex(const VertexId & index) const;

		inline void setVertex(const VertexId & index,
			const Vector3 & position);

		inline unsigned int getNbVertices() const;

		inline const glm::GlmDeque< Vector3>
			& getVertices() const;
		//@}

		/**
		* \name Faces
		*/
		//@{
		/**
		* \brief Add a triangle to the geometry.
		*
		* \param a The index of the first vertex.
		* \param b The index of the second vertex.
		* \param c The index of the third vertex.
		*
		* \return The index of the inserted face, if the triangle is degenerated (i.e. its area is 0), no face is inserted and an invalid index is returned.
		*/
		FaceId addFace(VertexId a, VertexId b, VertexId c);

		/**
		* \brief Add a triangle to the geometry.
		*
		* \param triangle The triangle.
		*
		* \return The index of the inserted face, if the triangle is degenerated (i.e. its area is 0), no face and no vertices are inserted and an invalid index is returned.
		*/
		FaceId addFace(const Triangle3 & triangle);

		inline const PathTriangle & getFace(const FaceId & index) const;

		inline PathTriangle & touchFace(const FaceId & index);

		inline void setFace(const FaceId & index, const PathTriangle & triangle);

		inline unsigned int getNbFaces() const;
		//@}

		/**
		* \brief Retrieve the geometry bounding box.
		*/
		const BoundingBox3 & getBoundingBox() const;

		/**
		* \brief Retrieve (and compute if necessary) the AABBTree for this geometry.
		*/
		const AABBTree & getAABBTree() const;

		/**
		* \brief Equality operator.
		*/
		bool operator==(const Geometry3 & geometry2) const;

		/**
		* \brief Difference operator.
		*/
		bool operator!=(const Geometry3 & geometry2) const;

		/**
		* \name Serialization
		*/
		//@{
		virtual void serialize(GseOStream & out) const;
		virtual void deserialize(GseIStream & in);
		//@}

		/**
		* \brief Simplify the structure of the geometry deleting degenerated faces and redeondant vertices.
		*/
		void simplifyStructure(bool &cancel);

		/**
		* \brief Computes the graph linking each face to its neighbors (that is the other faces that shares an edge with it).
		*
		* \param[out] graph The graph.
		*/
		void extractFacesGraph(glm::Array<glm::Array<FaceId> > & graph) const;

		/**
		* \brief Compute the outer border of given faces.
		*
		* \param[in] faces The faces.
		* \param[out] borders The outer border of the given faces.
		*/
		void computeBorderEdges(const glm::Array<FaceId> & faces,
			glm::Array<Edge> & borders) const;

		/**
		* \brief Extract the edges of the geometry as 3D segments.
		* \param[out] segments The extracted segments.
		*/
		void extractEdges(glm::Array<Segment3 > & segments) const;

		/**
		* \brief Extract the list of triangles forming the goemetry.
		* \param[out] triangles A list of the triangles.
		*/
		void computeGeometry(glm::Array<Triangle3> & triangles) const;

		/**
		* \brief Extract the list of triangles forming the goemetry and contained in a 2d bounding box
		* \param[out] triangles A list of the triangles.
		* \param[in] boundingBox The 2d bounding box.
		*/
		void computeGeometry(glm::Array<Triangle3> & triangles,
			const std::pair<Vector2, Vector2>& boundingBox) const;

		/**
		* \brief Adds the meshes with the given name to the current geometry
		* \param[in] name The name of the meshes to add
		* \param[in] geometry The geometry from which to extract the meshes
		*/
		void addGeometry(const Geometry3& geometry);

	protected:

		/**
		* \brief Computes the geometry edges and the table mapping edge identifiers to the faces it belongs.
		*
		* \param[out] edges The geometry edges.
		* \param[out] edge2Faces Map expressing the faces that belong to a given edge.
		*/
		void
			computeEdgeFaces(
				glm::Array<Edge> & edges,
				glm::Array< glm::Array<FaceId> > & edge2Faces) const;

			/**
			* \brief Inspection needed method declaring the inspectable attributes.
			*/
		//static void addAttributes();

		/**
		* \brief The vertices.
		*/
		glm::GlmDeque< Vector3 > _vertices;

		/**
		* \brief The faces.
		*/
		glm::GlmDeque< Geometry3::PathTriangle > _faces;

		/**
		* \brief Tells if the bounding box is good.
		*/
		mutable bool _boundingBoxValid;

		/**
		* \brief The bounding box.
		*/
		mutable BoundingBox3 _boundingBox;

		/**
		* \brief Tells if the AABBTree is good.
		*/
		mutable bool _aabbTreeValid;

		/**
		* \brief The AABBTree classifying the faces of this geometry.
		*/
		mutable AABBTree _aabbTree;

	};

	/**
	* \brief Write a Geometry3
	*/
	GLM_SDK_API GseOStream & operator<<(GseOStream & rawFile,
		const Geometry3 & value);

	/**
	* \brief Read a Geometry3
	*/
	GLM_SDK_API GseIStream & operator >> (GseIStream & rawFile,
		Geometry3 & value);

	//*****************************************************************************
	// Geometry3 inline definitions
	//*****************************************************************************

	//-----------------------------------------------------------------------------
	// clearFaces
	//-----------------------------------------------------------------------------
	inline void Geometry3::clearFaces()
	{
		_faces.clear();
	}

	//-----------------------------------------------------------------------------
	// getName
	//-----------------------------------------------------------------------------
	inline void Geometry3::clear()
	{
		_faces.clear();
		_vertices.clear();
		_boundingBoxValid = false;
        _aabbTreeValid = false;
        _aabbTree.clear();
    }

    //-----------------------------------------------------------------------------
	// getVertex
	//-----------------------------------------------------------------------------
	const Vector3 & Geometry3::getVertex(const VertexId & index) const
	{
		return _vertices[index];
	}

	//-----------------------------------------------------------------------------
	// setVertex
	//-----------------------------------------------------------------------------
	void Geometry3::setVertex(const VertexId & index,
		const Vector3 & position)
	{
		_vertices[index] = position;
	}

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

	//-----------------------------------------------------------------------------
	// getVertices
	//-----------------------------------------------------------------------------
	const glm::GlmDeque< Vector3> & Geometry3::getVertices() const
	{
		return _vertices;
	}

	//-----------------------------------------------------------------------------
	// getFace
	//-----------------------------------------------------------------------------
	const Geometry3::PathTriangle & Geometry3::getFace(const Geometry3::FaceId & index) const
	{
		return _faces[index];
	}

	//-----------------------------------------------------------------------------
	// touchFace
	//-----------------------------------------------------------------------------
	Geometry3::PathTriangle & Geometry3::touchFace(const Geometry3::FaceId & index)
	{
		return _faces[index];
	}

	//-----------------------------------------------------------------------------
	// setFace
	//-----------------------------------------------------------------------------
	void Geometry3::setFace(const FaceId & index,
		const Geometry3::PathTriangle & triangle)
	{
		_faces[index] = triangle;
	}

	//-----------------------------------------------------------------------------
	// getNbVertices
	//-----------------------------------------------------------------------------
	unsigned int Geometry3::getNbFaces() const
	{
		return static_cast<unsigned int> (_faces.size());
	}
}
