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

#pragma once

#include "glmMathBase.h"
#include "glmAssert.h"
#include "glmRTTI.h"
#include "glmAttribute.h"

namespace glm
{
	/**
	* \brief A class representing a simple interval.
	*
	* The interval is bounded and each bound is included in the interval.
	*
	* \ingroup types
	* \author Golaem <dev@golaem.com>
	*
	* \param Type Manipulated type (\ref double, float, Vector...)
	*/
	template <class Type>
	class Interval
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

			/**
			* \brief Default constructor (build an empty interval).
			*/
			Interval() : _lowerBound(), _upperBound(), _empty(true)
		{

		}

		/**
		* \brief Constructor from two bounds.
		* \param lowerBound The interval lower bound.
		* \param upperBound The interval upper bound.
		* \warning If the lowerBound is superior to the upperBound the interval is considered empty.
		*/
		Interval(const Type & lowerBound, const Type & upperBound) : _lowerBound(lowerBound), _upperBound(upperBound), _empty(false)
		{
			_empty = (_lowerBound > _upperBound);
		}

		/**
		* \brief Generic copy constructor.
		*/
		template<class OtherType>
		Interval(const Interval<OtherType> & interval) :_lowerBound(), _upperBound(), _empty(interval.isEmpty())
		{
			if (!_empty)
			{
				_lowerBound = Type(interval.getLowerBound());
				_upperBound = Type(interval.getUpperBound());
			}
		}

		/**
		* \brief Generic affectation
		*/
		template<class OtherType>
		Interval<Type> & operator= (const Interval<OtherType> & interval)
		{
			if (this != &interval)
			{
				_empty = interval.isEmpty();
				if (!_empty)
				{
					_lowerBound = Type(interval.getLowerBound());
					_upperBound = Type(interval.getUpperBound());
				}
			}
			return (*this);
		}

		/**
		* \brief Return true if two interval are disjoint.
		*/
		bool disjoint(const Interval<Type> & interval2) const
		{
			return !isEmpty() && !interval2.isEmpty() && ((_upperBound < interval2._lowerBound) || (interval2._upperBound < _lowerBound));
		}

		/**
		* \brief Return true if two interval intersects.
		*/
		bool intersect(const Interval<Type> & interval2) const
		{
			return !disjoint(interval2);
		}

		/**
		* \brief Computes the union of two intervals.
		*/
		Interval<Type> operator+ (const Interval<Type> & interval2) const
		{
			if (_empty)
			{
				return Interval<Type>(interval2);
			}
			else if (interval2._empty)
			{
				return Interval<Type>(*this);
			}
			else if (intersect(interval2))
			{
				return Interval<Type>(min(_lowerBound, interval2._lowerBound), max(_upperBound, interval2._upperBound));
			}
			else
			{
				return Interval<Type>();
			}
		}

		/**
		* \brief Computes the intersection of two intervals
		*/
		Interval<Type> operator* (const Interval<Type> & interval2) const
		{
			if (_empty || interval2._empty)
			{
				return Interval<Type>();
			}
			else
			{
				return Interval<Type>(max(_lowerBound, interval2._lowerBound), min(_upperBound, interval2._upperBound));
			}
		}

		/**
		* \name Accessors
		*/
		//@{
		/**
		* \brief Returns true if the interval is empty.
		*/
		bool isEmpty() const
		{
			return _empty;
		}

		/**
		* \brief Returns the lower bound of the interval.
		*/
		const Type & getLowerBound()const
		{
			GLM_DEBUG_ASSERT(!isEmpty());
			return _lowerBound;
		}

		/**
		* \brief Returns the upper bound of the interval.
		*/
		const Type & getUpperBound() const
		{
			GLM_DEBUG_ASSERT(!isEmpty());
			return _upperBound;
		}
		//@}

		/**
		* \brief Check if a given value is inside the interval.
		*/
		bool contains(const Type & scalar) const
		{
			return (scalar >= _lowerBound) && (scalar <= _upperBound);
		}

		/**
		* \brief Check if a given interval is contained in the current interval.
		*/
		bool contains(const Interval<Type> & interval) const
		{
			return !isEmpty() && !interval.isEmpty() && contains(interval.getLowerBound()) && contains(interval.getUpperBound());
		}

		/**
		* \brief Interval comparisons (used for STl containers)
		*/
		bool operator<(const Interval<Type> & i) const
		{
			return !isEmpty() && !i.isEmpty() && (getLowerBound() < i.getLowerBound() || (getLowerBound() == i.getLowerBound() && getUpperBound() < i.getUpperBound()));
		}

		/**
		* \brief Interval egality.
		*/
		bool operator==(const Interval<Type> & interval2) const
		{
			return (this->isEmpty() && interval2.isEmpty()) || (this->_lowerBound == interval2._lowerBound && this->_upperBound == interval2._upperBound);
		}
	protected:
		/**
		* \brief Interval lower bound.
		*/
		Type _lowerBound;
		/**
		* \brief Interval upper bound.
		*/
		Type _upperBound;
		/**
		* \brief True if the interval is empty.
		*/
		bool _empty;
	};
}

