/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/


#ifndef	__NBOUNDINGBOX__
#define	__NBOUNDINGBOX__


		#include "math/matrix3.h"


namespace GS {

/*!
	@short	AABB.
	@author Emmanuel Julien (ejulien@nworks.fr)
*/
class	MinMax
{
protected:

enum
{
	ClipNone	=	0,
	ClipRight	=	1,
	ClipLeft	=	2,
	ClipTop		=	4,
	ClipBottom	=	8,
	ClipFront	=	16,
	ClipBack	=	32
};

		/// Internal use.
		uint			cc_oc(const Vector4 &min, const Vector4 &max, const Vector4 &p) const
		{
			uint	oc = ClipNone;
					if	(p.x > max.x)	oc |= ClipRight;
			else	if	(p.x < min.x)	oc |= ClipLeft;
					if	(p.y > max.y)	oc |= ClipTop;
			else	if	(p.y < min.y)	oc |= ClipBottom;
					if	(p.z > max.z)	oc |= ClipBack;
			else	if	(p.z < min.z)	oc |= ClipFront;
			return oc;
		}

		/// Internal use.
		uint			ss_oc(const Vector4 &p) const
		{
			uint	oc = ClipNone;
			oc |= p.x > 0 ? ClipRight : ClipLeft;
			oc |= p.y > 0 ? ClipTop : ClipBottom;
			oc |= p.z > 0 ? ClipBack : ClipFront;
			return oc;
		}

public:

		Vector4			mn, mx;

		/// Return the start of the interval on a given axis.
		float			GetMin(uint axis) const
		{	return mn[axis];	}
		/// Return the end of the interval on a given axis.
		float			GetMax(uint axis) const
		{	return mx[axis];	}
		/// Return whether the MinMax object overlap with another one.
		bool			TestAxisOverlap(const MinMax &b, uint axis) const
		{	return (b.mn[axis] > mx[axis]) || (b.mx[axis] < mn[axis]) ? false : true;	}

		/// Intersect ray with this minmax.
		bool			IntersectRay(const Vector4 &o, const Vector4 &d, float &tmin, float &tmax);

		/// Returns whether a line intersect with the MinMax.
		bool			ClassifyLine(const Vector4 &p, const Vector4 &d, Vector4 &i, Vector4 *n = 0) const;
		/// Returns whether a segment intersect with the MinMax.
		bool			ClassifySegment(const Vector4 &p0, const Vector4 &p1, Vector4 &i, Vector4 *n = 0) const;

		/// Return whether two MinMax overlap at a given time.
		bool			TestOverlap(const MinMax &b) const
		{
			if	(mx.x < b.mn.x)	return false;
			if	(mx.y < b.mn.y)	return false;
			if	(mx.z < b.mn.z)	return false;
			if	(b.mx.x < mn.x)	return false;
			if	(b.mx.y < mn.y)	return false;
			if	(b.mx.z < mn.z)	return false;
			return true;
		}

		/// Test position.
inline	bool			IsInside(const Vector4 &p) const
		{	return (p.x < mn.x) || (p.y < mn.y) || (p.z < mn.z) || (p.x > mx.x) || (p.y > mx.y) || (p.z > mx.z) ? false : true;		}

		/// Grow the min~max boundaries to include another min~max structure.
		void			Grow(const MinMax &b)
		{
			mn = Vector4::Minimum(b.mn, mn);
			mx = Vector4::Maximum(b.mx, mx);
		}

		/// Grow the min~max boundaries to include a vector.
		void			Grow(const Vector4 &p)
		{
			mn = Vector4::Minimum(p, mn);
			mx = Vector4::Maximum(p, mx);
		}

		/// Get the min-max area.
		float			GetArea() const
		{	return (mx.x - mn.x) * (mx.y - mn.y) * (mx.z - mn.z);	}
		/// Get the min-max center.
		Vector4			GetCenter() const
		{	return (mn + mx) * 0.5f;	}

		/// Set min-max.
		void			Set(const Vector4 &min, const Vector4 &max)
		{	mn = min; mx = max;	}
		/// Set from position and size.
		void			SetFromPositionSize(const Vector4 &p, const Vector4 &s)
		{
			mn = p - s * 0.5f;
			mx = p + s * 0.5f;
		}

		void			Reset()
		{	mn.Set(); mx.Set();	}

/*!
	@name	Serialization
	@{
*/
		bool			FromMetaTag(NML::Tag &tag);
		NML::Tag		*AsMetaTag();
/// @}

		MinMax() : mn(0, 0, 0), mx(0, 0, 0) {}
		MinMax(const Vector4 &min, const Vector4 &max) : mn(min), mx(max) {}
};

/*!
	@short	Oriented bounding box.
	@author Emmanuel Julien (ejulien@nworks.fr)
*/
struct	OBB
{
		Vector4			bb_position;
		Vector4			bb_scale;
		Matrix3			bb_rotation;

		/// Compute the min/max of the OBB.
		void			ComputeMinMax(MinMax &minmax);

		/*!
			@short		Transform OBB.
			@warning 	Scaling is not supported.
		*/
		void			Transform(const Matrix4 &mtx);

		/// OBB from min~max.
static	OBB			FromMinMax(const MinMax &minmax)
		{	return OBB((minmax.mn + minmax.mx) * 0.5f, minmax.mx - minmax.mn);	}

		bool			FromMetaTag(NML::Tag &tag);
		NML::Tag		*AsMetaTag();

		OBB(const MinMax &minmax)
		{	*this = FromMinMax(minmax);	}
		OBB(const Vector4 &p, const Vector4 &s, const Matrix3 *m = 0)
		{
			bb_position = p;
			bb_scale = s;
			if	(m)
				bb_rotation = *m;
		}
		OBB() {}
};

} // GS


#endif	// __NBOUNDINGBOX__

