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

#pragma once

#include "glmMathBase.h"
#include "glmVector2.h"

namespace glm
{
	/**
	* \brief This class defines 2D lines.
	*
	* The line is defined in two ways :
	*	- Two points accessible by getP1() and getP2(),
	*	- The three scalar of the equation ax + by + c = 0 (accessibles by getX(), getY() and getZ()).
	*
	* \ingroup geometry2
	* \author Golaem <dev@golaem.com>
	*
	* \param float Manipulated scalar type (\ref Expression, double, float...)
	*/

	class Line2
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

			/**
			* \brief Default constructor.
			*/
			Line2() : _a(-GLM_SQRT_2_DIV_2), _b(GLM_SQRT_2_DIV_2), _c(0), _p1(0, 0), _p2(1, 1)
		{}

		/**
		* \brief Constructor from 2 points.
		*
		* \param p1 The first point.
		* \param p2 The second point.
		*/
		Line2(const Vector2 & p1, const Vector2 & p2) : _a(0), _b(0), _c(0), _p1(p1), _p2(p2)
		{
			GLM_DEBUG_ASSERT_MSG(glmFinite(p1.x) && glmFinite(p1.y) && glmFinite(p2.x) && glmFinite(p2.y), "Line2 constructor called with non finite value");
			GLM_DEBUG_ASSERT_MSG(p1 != p2, "Line2 constructor called with two equal points");
			Vector2 directionVector = (p2 - p1);
			directionVector.normalizeSelf();
			_a = -directionVector.y;
			_b = directionVector.x;
			_c = directionVector.y*p1.x - directionVector.x*p1.y;
		}

		/**
		* \brief Copy constructor
		*/
		Line2(const Line2 & l) : _a(l._a), _b(l._b), _c(l._c), _p1(l._p1), _p2(l._p2)
		{

		}

		/**
		* \name Accessors.
		*/

		//@{
		const float & getA() const
		{
			return _a;
		}

		const float & getB() const
		{
			return _b;
		}

		const float & getC() const
		{
			return _c;
		}

		const Vector2 & getP1() const
		{
			return _p1;
		}

		const Vector2 & getP2() const
		{
			return _p2;
		}
		//@}

		/**
		* \brief Check if two lines are parallel.
		*
		* \param line2 the second line.
		*
		* \return true if the two intersects.
		*/
		bool parallel(const Line2 & line2) const
		{
			return parallel((*this), line2);
		}

		/**
		* \brief Check if two lines are parallel.
		*
		* \param line1 the first line.
		* \param line2 the second line.
		*
		* \return true if the two intersects.
		*/
		static bool parallel(const Line2 & line1, const Line2 & line2)
		{
			return glm::areCollinear(line1.getNormalizedDirectionVector(), line2.getNormalizedDirectionVector());
		}

		/**
		* \brief Computes and returns the normalized normal vector to this line.
		*/
		Vector2 getNormalizedNormalVector() const
		{
			return Vector2(_a, _b);
		}

		/**
		* \brief Computes and returns the normalized colinear vector to this line.
		*/
		Vector2 getNormalizedDirectionVector() const
		{
			return Vector2(_b, -_a);
		}

		/**
		* \brief Computes and returns the signed distance between a point and the line.
		*/
		float signedDistance(const Vector2 & point) const
		{
			return getNormalizedNormalVector()*point + _c;
		}

		/**
		* \brief Computes and returns the projection of a point on this line.
		*/
		Vector2 projection(const Vector2 & point) const
		{
			return point - getNormalizedNormalVector()*signedDistance(point);
		}

		/**
		* \brief Computes and returns the vector between a point and its projection on the line.
		*/
		Vector2 projectionNormal(const Vector2 & point) const
		{
			return getNormalizedNormalVector()*signedDistance(point);
		}

		/**
		* \brief Check if two given points are on the same side of the line.
		*
		* If at least one of the points is a part of the line, the result will be false.
		*
		* \param[in] point1 The first point.
		* \param[in] point2 The second point.
		* \return True if point1 and point2 are on the same side of the line.
		*/
		bool onSameSide(const Vector2 & point1, const Vector2 & point2) const
		{
			return ((getNormalizedNormalVector()*(point1 - _p1))*(getNormalizedNormalVector()*(point2 - _p1))) >= 0.0;
		}

		/**
		* \brief Check if a point belongs to the line.
		*/
		bool contains(const Vector2 & point, float precision = GLM_NUMERICAL_PRECISION) const
		{
			return glm::approxEqual(signedDistance(point), 0.f, precision);
		}

		/**
		* \brief Equality operator
		*/
		bool operator==(const Line2 & line) const
		{
			return (this->_a == line._a) && (this->_b == line._b) && (this->_c == line._c);
		}

		/**
		* \brief Difference operator
		*/
		bool operator!=(const Line2 & line) const
		{
			return (this->_a != line._a) || (this->_b != line._b) || (this->_c != line._c);
		}

	protected:
		float _a;
		float _b;
		float _c;
		Vector2 _p1;
		Vector2 _p2;
	};
}

