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

#pragma once

#include "glmSDKAPI.h"
#include "glmPODArray.h"
#include "glmFastBoundingBox.h"
#include "glmClassifiedRay3.h"
#include "glmCoreDefinitions.h"

namespace glm
{
	class Segment3;
	struct CenteredSegment;
	class Triangle3;
	template <class T> class Array;
    /**
     * \brief Gestion d'un arbre de boites englobantes orientées suivant les axes
     *
     * \ingroup spatialDataStructures
     *
     * \author Golaem <dev@golaem.com>
     *
     */
    class GLM_SDK_API AABBTree
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

		/**
		* \brief Class defining the intersection between a ray and a tree element.
		*/
		class GLM_SDK_API RayIntersection
		{
		public:
			/**
			* \brief Constructor
			*/
			RayIntersection();

			/**
			* \brief Destructor
			*/
			~RayIntersection();

			/**
			* \brief Retrieve the coordinate of the intersection point on the ray.
			* This value represents the distance between the origin of the ray and the intersection point.
			*/
			inline float getT() const;

			/**
			* \brief Retrieve the u coordinate of the intersection point on the triangle.
			* This value represents the coordinate of the intersection along the U axis of the intersected triangle.
			*/
			inline float getU() const;

			/**
			* \brief Retrieve the v coordinate of the intersection point on the triangle.
			* This value represents the coordinate of the intersection along the V axis of the intersected triangle.
			*/
			inline float getV() const;

			/**
			* \brief Retrieve the intersected triangle.
			*/
			inline const Triangle3 * getTriangle() const;

		private:
			float _t;

			float _u;

			float _v;

			const Triangle3 * _triangle;

			friend class AABBTree;
		};

	public:
		/**
		* \brief Left subtree.
		*/
		AABBTree* _left;

		/**
		* \brief Right subtree.
		*/
		AABBTree* _right;

		/**
		* \brief Bounding box.
		*/
		FastBoundingBox _boundingBox;

		/**
		* \brief The triangles handled by the tree.
		*/
		glm::PODArray<const Triangle3*> _triangles;

		/**
		* \name Construction/Destruction
		*/
		//@{
		/**
		* \brief Default constructor.
		*/
		AABBTree();

		/**
		* \brief Destructor.
		*/
		~AABBTree();

		/**
		* \brief Initializes the AABBTree from a list of triangles.
		* \param triangles A reference to a container of constant pointer to
		* \param maxDepth The maximum depth of the tree (default value is 16).
		* \param maxTriangle The maximum number of triangles per leaf (if the max depth is not reached - default value is 16).
		*/
		bool init(const glm::PODArray< const Triangle3 * > & triangles, unsigned int maxDepth = 16, unsigned int maxTrianglesPerLeaf = 16);

		/**
		* \brief Clear the tree from its faces in order to reinitialize it.
		*/
		void clear();
		//@}

        /**
         * \brief Calcul de l'intersection d'un rayon avec toute la géométrie
         *
         * \param ray Le rayon lancé
         * \param intersections La liste des intersections avec les triangles contenus
         * \return vrai s'il y a intersection entre le rayon et la géométrie
         */
        bool sendRay(const ClassifiedRay3& ray, glm::Array<AABBTree::RayIntersection>& intersections) const;

        /** \brief Calcul de l'intersection d'un rayon avec la géométrie
         *
         * \param ray Le rayon lancé
         * \param intersection  [out] L'intersection la plus proche de la source du rayon. Attention, le champ _t du paramètre
         *							  contient la borne maximale de lancement du rayon. Autrement dit, si aucune intersection
         *							  n'est trouvée avant cette borne, la méthode renverra false.
         * \return vrai s'il y a intersection entre le rayon et la géométrie
         */
        bool sendRay(const ClassifiedRay3 & ray, const float& maximumRayLength, AABBTree::RayIntersection & intersection) const;

		/**
		* \name Comparisons
		*/
		//@{
		bool operator==(const AABBTree & tree) const;
		bool operator!=(const AABBTree & tree) const;
		//@}

		bool isLeaf() const;

		bool roughCollides(const FastBoundingBox& box, unsigned int maxDepth = 4, float precision = GLM_EPSILON);
		bool roughCollides(const Segment3& segment, unsigned int maxDepth = 4, float precision = GLM_EPSILON);
		bool roughCollides(const CenteredSegment& segment, unsigned int maxDepth = 4, float precision = GLM_EPSILON);

	private:
		GLM_DISALLOW_COPY_AND_ASSIGN(AABBTree);

		/**
		* \brief Initializes the AABBTree from a list of triangles.
		* \param triangles A reference to a container of constant pointer to
		* \param maxDepth The maximum depth of the tree.
		* \param currentDepth The current depth of the initialized subtree.
		* \param maxTriangle The maximum number of triangles per leaf (if the max depth is not reached).
		*/
		bool init(const glm::PODArray< const Triangle3 * > & triangles, unsigned int maxDepth, unsigned int currentDepth, unsigned int maxTrianglesPerLeaf);
	};

	//*****************************************************************************
	// Inline definitions
	//*****************************************************************************

	//-----------------------------------------------------------------------------
	inline float AABBTree::RayIntersection::getT() const
	{
		return _t;
	}

	//-----------------------------------------------------------------------------
	inline float AABBTree::RayIntersection::getU() const
	{
		return _u;
	}

	//-----------------------------------------------------------------------------
	inline float AABBTree::RayIntersection::getV() const
	{
		return _v;
	}

	//-----------------------------------------------------------------------------
	inline const Triangle3* AABBTree::RayIntersection::getTriangle() const
	{
		return _triangle;
	}

	//-----------------------------------------------------------------------------
	inline bool AABBTree::isLeaf() const
	{
		return _left == NULL || _right == NULL;
	}
}

