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

#pragma once

#include "glmVector3.h"
#include "glmRay3.h"

namespace glm
{
	/** 
	* \brief A Classified 3D Ray as described in [EisemannEtAl07]
	* 
	* \see http://graphics.tu-bs.de/publications/Eisemann07RS.pdf
	*
	* \ingroup geometry3
	* \author Golaem <dev@golaem.com>
	*
	* \param float Manipulated scalar type (\ref Expression, double, float...)
	*/		
	class GLM_CORE_API ClassifiedRay3
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

		class Classification
		{
		public:
			enum Value
			{
				MMM,MMP,MPM,MPP,MOM,MOP,MMO,MPO,MOO,
				PMM,PMP,PPM,PPP,POM,POP,PMO,PPO,POO,
				OMM,OMP,OPM,OPP,OOM,OOP,OMO,OPO,
				END
			};
		};

		/** 
		* \brief Constructor.
		* \param source The source of the ray (default is null vector)
		* \param direction The direction of the ray (default is null vector)
		*/
		ClassifiedRay3(const Vector3 & source = Vector3::getNullVector(), const Vector3 & direction = Vector3::getNullVector());

		/** 
		* \brief Constructor from an existing Ray3
		* \param ray The existing ray (from which a copy is made)
		*/
		ClassifiedRay3(const Ray3 & ray);

		/** 
		* \brief Copy constructor.
		*/
		ClassifiedRay3(const ClassifiedRay3 & classifiedRay);

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

		/**
		* \brief Assignment operator from another classified ray.
		*/
		ClassifiedRay3 & operator=(const ClassifiedRay3 & classifiedRay);

		/**
		* \brief Assignment operator from a ray3.
		*/
		ClassifiedRay3 & operator=(const Ray3 & classifiedRay);
		/** 
		* \name Accessors.
		*/
		//@{
		const Ray3 & getRay3() const;
		int getClassificationInt() const;
		ClassifiedRay3::Classification::Value getClassification() const;
		const Vector3 & getSource() const;
		const Vector3 & getDirection() const;
		const Vector3 & getDirectionInverses() const;
		float getSXY() const;
		float getSYX() const;
		float getSZY() const;
		float getSYZ() const;
		float getSXZ() const;
		float getSZX() const;
		float getCXY() const;
		float getCYX() const;
		float getCZY() const;
		float getCYZ() const;
		float getCXZ() const;
		float getCZX() const;
		//@}

		/** 
		* \brief Project a point on the ray.
		*
		* \param[in] point The given point.
		* \param[out] t The parameter for whom source + direction * t (ie value(t)) = the projected point.
		* \param[out] delta The vector between the given point and the projected point.
		*/
		void project(const Vector3 & point, float & t, Vector3 & delta);

		/** 
		* \brief Returns the ray point pamaretrized by t.
		*
		* \param t The parameter
		* \return The point.
		*/
		Vector3 value(const float & t) const;

		/**
		* \brief transtyping operator to a Ray3.
		*/
		operator const Ray3*() const;

	protected:

		void classify();

		Ray3 _ray;
		int _classification;
		Vector3 _directionInverses;
		float _sXY; 
		float _sYX;
		float _sZY;
		float _sYZ;
		float _sXZ;
		float _sZX;
		float _cXY; 
		float _cYX;
		float _cZY;
		float _cYZ;
		float _cXZ;
		float _cZX;
	} ;
}

