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

#pragma once

#include "glmVector3.h"
#include "glmSegment3.h"
#include "glmPlane3.h"
#include "glmRay3.h"

namespace glm
{
	/**
	* \brief A triangle as three points.
	*
	* \ingroup geometry3
	* \author Golaem <dev@golaem.com>
	*
	* \param float Manipulated scalar type (\ref Expression, double, float...)
	*/

	class GLM_CORE_API Triangle3
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared);

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

		/**
		* \brief Constructor from three given points.
		* \param a The first point.
		* \param b The second point.
		* \param c The third point.
		*/
		Triangle3(const Vector3 & a, const Vector3 & b, const Vector3 & c);

		/**
		* \brief Copy constructor.
		*/
		Triangle3(const Triangle3& triangle);

		void setPoints(const Vector3 & a, const Vector3 & b, const Vector3 & c);

		/**
		* \see glm::Triangle3::getVertex(unsigned int index)
		*/
        inline const Vector3& getVertex(unsigned int index) const;

        /**
		* \brief Translate the triangle following a given vector.
		*
		* \param translation The translation vector.
		* \return The translated triangle.
		*/
		Triangle3 operator+(const Vector3 & translation) const;

		/**
		* \brief assignment operator
		*/
		Triangle3& operator=(const Triangle3& triangle);

		/**
		* \brief test if triangle is degenerated (edges validity)
		*/
		bool isDegenerated(float precision = GLM_NUMERICAL_PRECISION) const;

		/**
		* \name Accessors
		*/
		//@{
		const Vector3 & operator[](unsigned int index) const;
		Vector3 & operator[](unsigned int index);

		void getEdge(Segment3& result, unsigned int index) const;
		//@}

		/**
		* \names Requests
		*/
		//@{
		/**
		* \brief Computes and returns the barycenter of the triangle.
		* The morePrecise version of this call use a coordinate translation prior to
		* the real barycenter computation.
		* \param morePrecise If true, the barycenter calculation is more precise (default is true).
		* \return the barycenter of the triangle.
		*/
		Vector3 getBarycenter(bool morePrecise = true) const;

		/**
		* \brief Computes and returns a random point inside this triangle.
		* \param rng The random number generator to use.
		*/
		Vector3 getRandomPoint(RandomNumberGenerator & rng) const;
		//@}

		/**
		* \brief Equality test between two triangles.
		*
		* \param triangle2 the other triangle.
		* \return True if the two triangles are equal.
		*/
		bool operator==(const Triangle3 & otherTriangle) const;

		/**
		* \brief Difference test between two triangles.
		*
		* \param triangle2 the other triangle.
		* \return True if the two triangles are different.
		*/
		bool operator!=(const Triangle3 & otherTriangle) const;

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

		/**
		* \name Requests
		*/
		//@{

		/**
		* \see glm::AbstractTriangle<3>::getSurface()
		*/
		float getSurface() const;

		/**
		* \brief Computes and returns the angle between the triangle and the XY plane.
		*/
		Angle xyAngle() const;

		/**
		* \brief Computes and returns the plan supporting the triangle.
		*/
		Plane3 getPlane() const;

		/**
		* \brief Returns the normal to this triangle.
		*/
		const Vector3 & getNormal() const;

		/**
		* \brief Return the normalized normal vector oriented toward the given point.
		*/
		Vector3 getNormal(const Vector3 & point) const;

		/**
		* \brief Returns the vector between the first and second vertex.
		*/
		const Vector3 & getUAxis() const;

		/**
		* \brief Returns the vector between the first and third vertex.
		*/
		const Vector3 & getVAxis() const;

		/**
		* \brief Computes and returns a position from u,v coordinates.
		* \param u The u coordinate of the point.
		* \param v The v coordinate of the point.
		* \return The computed position.
		*/
		Vector3 computePositionFromUV(float u, float v) const;

		/**
		* \name Methods used for raytracing.
		*/
		//@{

		///**
		//* \brief Returns the direction of a reflected ray.
		//* \param direction The original direction of the incident ray.
		//*/
		//Vector3 reflectionDirection(const Vector3 & direction) const;

		///**
		//* \brief Returns the direction of a reflected ray.
		//* \param ray The incident ray.
		//*/
		//Vector3 reflectionDirection(const Ray3 & ray) const;

		//@}

		/**
		* \name Intersections
		*/
		//@{
		/**
		* \brief Computes the intersection between a segment and the triangle.
		*
		* \param[in] segment The segment
		* \param[out] inter The intersection point (valid if the method returns true).
		* \return True if the segment and the triangle intersects.
		*/
		bool intersection(const Segment3 & segment, Vector3 & inter) const;

        /**
         * \brief Calcul de l'intersection entre un rayon et le triangle
         * \param ray Le rayon
         * \param t [t>=0] Le paramètre qui contiendra la position de l'intersection sur le rayon
         * \param u [u>=0 && u<=1] Le paramètre contenant la coordonnée u de l'intersection sur le triangle
         * \param v [v>=0 && v<=1 && u+v<=1] Le paramètre contenant la coordonnée v de l'intersection sur le triangle
         * \return Vrai si l'intersection est valide
         */
        bool intersection(const Ray3& ray, float& t, float& u, float& v) const;

        /**
         * \brief Calcul de l'intersection généralisée entre un rayon et le triangle, autrement dit, dans ce cas,
         * le triangle est considéré comme définissant un plan
         * \param r Le rayon
         * \param t Le paramètre qui contiendra la position de l'intersection sur le rayon
         * \param u Le paramètre contenant la coordonnée u de l'intersection sur le triangle
         * \param v Le paramètre contenant la coordonnée v de l'intersection sur le triangle
         * \return Vrai si le rayon n'est pas parallèle au plan défini par le triangle
         */
        bool generalIntersection(const Ray3 & ray, float & t, float & u, float & v) const;

		//@}

		/** \name Distances / projection */
		//@{

        /** \brief Permet de savoir si la projection d'un point perpendiculairement au plan support du triangle est à l'intérieur de celui-ci
         *
         * \param point Le point à tester
         * \return Vrai si la projection d'un point perpendiculairement au plan support du triangle est à l'intérieur de celui-ci
         */
        bool projectOn(const Vector3& point, Vector3& projection) const;
		//@}


		/**
		* \brief Check if a given point is inside the triangle.
		*
		* \param point The point to test.
		* \return True if the point is inside the triangle.
		*/
		bool contains(const Vector3 & point, float precision = GLM_NUMERICAL_PRECISION) const;
        bool containsFast(const Vector3& point, float precision = GLM_NUMERICAL_PRECISION) const;

        bool vertexInCommon(const Triangle3 & otherTriangle, float precision = GLM_EPSILON) const;

    protected:

		/**
		* \brief The three points defining the triangle.
		*/
		Vector3 _points[3];

		/**
		* \brief The first axis defining the triangle.
		*/
		Vector3 _uAxis;
		/**
		* \brief The second axis defining the triangle.
		*/
		Vector3 _vAxis;
		/**
		* \brief The normalized normal vector to the triangle.
		*/
		Vector3 _normalizedNormal;
		/**
		* \brief Triangle's surface.
		*/
		float _surface;
	};

    //-------------------------------------------------------------------------
    inline const Vector3& Triangle3::getVertex(unsigned int index) const
    {
        // GLM_DEBUG_ASSERT( index < 3 );
        return _points[index];
    }
}

