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


#ifndef	__NVECTOR__
#define	__NVECTOR__


		#include "math/nmath.h"
		#include "alloc/ialloc.h"


namespace GS {
	class Matrix3;
	class Matrix4;

	namespace NML {
		class File;
		class Tag;
	}

/*!
	@short	Vector 2d template class.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
template <class T> struct tVector2
{
		NPLACEMENT_NEW(Vector)

		T				x, y;

inline	bool			operator == (const tVector2 <T> &b) const	{	return (x == b.x) && (y == b.y);	}
inline	bool			operator != (const tVector2 <T> &b) const	{	return (x != b.x) || (y != b.y);	}

inline	void			operator += (const tVector2 <T> &b)			{	x += b.x; y += b.y;	}
inline	void			operator += (const float k)					{	x += k; y += k;		}
inline	void			operator -= (const tVector2 <T> &b)			{	x -= b.x; y -= b.y;	}
inline	void			operator -= (const float k)					{	x -= k; y -= k;		}
inline	void			operator *= (const tVector2 <T> &b)			{	x *= b.x; y *= b.y;	}
inline	void			operator *= (const float k)					{	x *= k; y *= k;		}
inline	void			operator /= (const tVector2 <T> &b)			{	x /= b.x; y /= b.y;	}
inline	void			operator /= (const float k)					{	x /= k; y /= k;	}

inline	tVector2 <T>	operator + (const tVector2 <T> &b) const	{	return tVector2 <T> (x + b.x, y + b.y);	}
inline	tVector2 <T>	operator + (const T v) const				{	return tVector2 <T> (x + v, y + v);	}
inline	tVector2 <T>	operator - (const tVector2 <T> &b) const	{	return tVector2 <T> (x - b.x, y - b.y);	}
inline	tVector2 <T>	operator - (const T v) const				{	return tVector2 <T> (x - v, y - v);	}
inline	tVector2 <T>	operator * (const tVector2 <T> &b) const	{	return tVector2 <T> (x * b.x, y * b.y);	}
inline	tVector2 <T>	operator * (const T v) const				{	return tVector2 <T> (x * v, y * v);	}
inline	tVector2 <T>	operator / (const tVector2 <T> &b) const	{	return tVector2 <T> (x / b.x, y / b.y);	}
inline	tVector2 <T>	operator / (const T v) const				{	return tVector2 <T> (x / v, y / v);	}

		tVector2 <T>	operator * (const Matrix3 &m) const;

		/// Squared vector length.
inline	float			Len2() const	{	return (float)(x * x + y * y);	}
		/// Vector length.
inline	float			Len() const		{	return Math::Sqrt((float)(x * x + y * y));	}

		/// Normalize this vector.
inline	void			Normalize()		{	float l = Len(); if (l) { float k = 1.f / l; x *= k; y *= k;	}	}
		/// Normalize vector.
inline	tVector2 <T>	Normalized() const
		{
			float k = 1.f / Len();
			return tVector2 <T>(x * k, y * k);
		}

		/// Reversed vector.
inline	tVector2 <T>	Reversed() const
		{	return tVector2 <T> (-x, -y);	}

		/// Vector squared distance.
static	float			Dist2(const tVector2 &a, const tVector2 &b)
		{	return ((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));	}
		/// Vector distance.
static	float			Dist(const tVector2 &a, const tVector2 &b)
		{	return Math::Sqrt((float)((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y)));	}

		/// Set vector 2D components.
inline	void			Set(const T a, const T b) { x = a; y = b; }

		tVector2 <T> (T a, T b) { Set(a, b); }
		tVector2 <T> () { Set(0, 0); }
};

typedef tVector2 <float> Vector2;

/*!
	@short	4-Component vector
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
struct	Vector4
{
		NPLACEMENT_NEW(Vector)

		float			x, y, z, w;

inline	bool			operator == (const Vector4 &b) const	{	return Math::TestEqual(x, b.x) && Math::TestEqual(y, b.y) && Math::TestEqual(z, b.z);	}
inline	bool			operator != (const Vector4 &b) const	{	return !Math::TestEqual(x, b.x) || !Math::TestEqual(y, b.y) || !Math::TestEqual(z, b.z);	}

inline	void			operator += (const Vector4 &b)			{	x += b.x; y += b.y; z += b.z;	};
inline	void			operator += (const float k)				{	x += k; y += k; z += k;			};
inline	void			operator -= (const Vector4 &b)			{	x -= b.x; y -= b.y; z -= b.z;	};
inline	void			operator -= (const float k)				{	x -= k; y -= k; z -= k;			};
inline	void			operator *= (const Vector4 &b)			{	x *= b.x; y *= b.y; z *= b.z;	};
inline	void			operator *= (const float k)				{	x *= k; y *= k; z *= k;			};
inline	void			operator /= (const Vector4 &b)			{	x /= b.x; y /= b.y; z /= b.z;	};
inline	void			operator /= (const float k)				{	float k_ = k ? 1 / k : 0; x *= k_; y *= k_; z *= k_;	};

inline	Vector4			operator + (const Vector4 &b) const		{	return Vector4(x + b.x, y + b.y, z + b.z);	}
inline	Vector4			operator + (const float v) const		{	return Vector4(x + v, y + v, z + v);	}
inline	Vector4			operator - (const Vector4 &b) const		{	return Vector4(x - b.x, y - b.y, z - b.z);	}
inline	Vector4			operator - (const float v) const		{	return Vector4(x - v, y - v, z - v);	}
inline	Vector4			operator * (const Vector4 &b) const		{	return Vector4(x * b.x, y * b.y, z * b.z);	}
inline	Vector4			operator * (const float v) const		{	return Vector4(x * v, y * v, z * v);	}
inline	Vector4			operator / (const Vector4 &b) const		{	return Vector4(x / b.x, y / b.y, z / b.z);	}
inline	Vector4			operator / (const float v) const		{	float i = v ? 1 / v : 0; return Vector4(x * i, y * i, z * i);	}

inline	float			operator [] (size_t n) const { return (&x)[n]; }
inline	float			&operator [] (size_t n) { return (&x)[n]; }

inline	Vector4			SafeDivided(const Vector4 &b) const
		{	return Vector4(b.x ? x / b.x : 0, b.y ? y / b.y : 0, b.z ? z / b.z : 0);	}

		/// Set vector components.
inline	void			Set(float x_, float y_, float z_, float w_)
		{	x = x_; y = y_;	z = z_;	w = w_;		}
inline	void			Set(float x_ = 0.f, float y_ = 0.f, float z_ = 0.f)		// Used to provide script overload.
		{	x = x_; y = y_;	z = z_;	w = 1.0f;	}
inline	void			Set(Vector4 &v)
		{	x = v.x; y = v.y; z = v.z; w = v.w;	}

		/// Dot product.
inline	float			Dot(const Vector4 &b) const
		{	return x * b.x + y * b.y + z * b.z;	}
		/// Cross product.
inline	Vector4			Cross(const Vector4 &b) const
		{   return Vector4(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x);	}

		void			operator *= (const Matrix4 &);
		Vector4			operator * (const Matrix4 &) const;
		void			operator *= (const Matrix3 &);
		Vector4			operator * (const Matrix3 &) const;

		/// Reverse this vector.
inline	void			Reverse()		{	x = -x; y = -y; z = -z;	}
		/// Inverse vector.
inline	void			Inverse()		{	x = x ? 1.f / x : 0; y = y ? 1.f / y : 0; z = z ? 1.f / z : 0;	}

		/// Normalize this vector.
inline	void			Normalize()		{	float l = Len(); if (l) { float k = 1.f / l; x *= k; y *= k; z *= k; }	}
		/// Normalize vector.
inline	Vector4			Normalized() const
		{
			float l = Len(); 
			float k = l ? 1.f / l : 1.f;
			return Vector4(x * k, y * k, z * k);
		}

		/// Clamp vector components to [min;max].
		Vector4			Clamped(float min, float max) const;
		/// Clamp vector components to [min;max].
		Vector4			Clamped(const Vector4 &min, const Vector4 &max) const;
		/// Clamp vector magnitude to [min;max].
		Vector4			ClampedMagnitude(float min, float max) const;

		/// Return the opposite vector to this vector.
inline	Vector4			Reversed() const
		{	return Vector4(-x, -y, -z);	}
		/// Return the inverse vector to this vector.
inline	Vector4			Inversed() const
		{	return Vector4(1.f / x, 1.f / y, 1.f / z);	}
		/// Absolute vector.
		Vector4			Abs() const;
		/// Sign vector.
inline	Vector4			Sign() const
		{	return Vector4(x < 0.f ? -1.f : 1.f, y < 0.f ? -1.f : 1.f, z < 0.f ? -1.f : 1.f);	}

		/// Maximum of two vectors.
static	Vector4			Maximum(const Vector4 &a, const Vector4 &b)
		{	return Vector4(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y, a.z > b.z ? a.z : b.z);	}
		/// Minimum of two vectors.
static	Vector4			Minimum(const Vector4 &a, const Vector4 &b)
		{	return Vector4(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y, a.z < b.z ? a.z : b.z);	}

		/*!
			@short	Reflect vector.
			@note	Vector must be normalized.
		*/
inline	Vector4			Reflected(const Vector4 &n) const
		{
			Vector4	rv = Reversed();
			return n * (2.f * rv.Dot(n)) - rv;
		}
		/*!
			@short	Refract vector.
			@note	Vector must be normalized.
		*/
inline	Vector4			Refracted(const Vector4 &n, float kin = 1, float kout = 1) const
		{
			const float	k = kin / kout;
			return (*this) * k + n * (k - 1.f);
		}

		/// Squared vector length.
inline	float			Len2() const { return (float)(x * x + y * y + z * z); }
		/// Vector length.
inline	float			Len() const { return Math::Sqrt((float)(x * x + y * y + z * z)); }
		/// Hash vector.
		int				Hash() const;

		Vector4			Floor() const;
		Vector4			Ceil() const;

		/*!
			@short	Return a random vector.
			@note	w component is not randomized but set to 1.
		*/
static	Vector4			Random(float min = -1, float max = 1);
		/// Vector squared distance.
static	float			Dist2(const Vector4 &a, const Vector4 &b)
		{	return ((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) + (b.z - a.z) * (b.z - a.z));	}
		/// Vector distance.
static	float			Dist(const Vector4 &a, const Vector4 &b)
		{	return Math::Sqrt((float)((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) + (b.z - a.z) * (b.z - a.z)));	}
		/*!
			@short	Vector base to Euler.
			@note	base convention u = {0,0,1}, v = {1,0,0}, second axis is optional.
		*/
static	void			BaseToEuler(Vector4 &euler, Vector4 &u, Vector4 *v = NULL);

		/*!
			@short	Return a vector which is facing a given direction.

			Returns a copy of this vector if it is already facing the given
			direction or the opposite of this vector otherwise.
		*/
		Vector4			FaceForward(Vector4 &dir);

		NML::Tag		*AsMetaTag(const char *id, bool full_dump = false) const;
		bool			FromMetaTag(NML::Tag &tag);

		Vector4(float a, float b, float c, float d = 1) : x(a), y(b), z(c), w(d) {}
		Vector4(const tVector2 <float> &v2) : x(v2.x), y(v2.y), z(1), w(1) {}
		Vector4(const tVector2 <int> &v2) : x(float(v2.x)), y(float(v2.y)), z(1), w(1) {}
		Vector4() {}
};

} // GS


#define	Vec3Set(v, a, b, c)		{	(v).x = a; (v).y = b; (v).z = c;	}

#define	Vec3Len2(v)				((v).x * (v).x + (v).y * (v).y + (v).z * (v).z)
#define	Vec3Len(v)				Math::Sqrt((float)Vec3Len2(v))

#define	Vec3Add(r, a, b)		{	(r).x = (a).x + (b).x; (r).y = (a).y + (b).y; (r).z = (a).z + (b).z;	}
#define	Vec3AddConst(r, a, k)	{	(r).x = (a).x + k; (r).y = (a).y + k; (r).z = (a).z + k;	}
#define	Vec3Sub(r, a, b)		{	(r).x = (a).x - (b).x; (r).y = (a).y - (b).y; (r).z = (a).z - (b).z;	}
#define	Vec3SubConst(r, a, k)	{	(r).x = (a).x - k; (r).y = (a).y - k; (r).z = (a).z - k;	}
#define	Vec3Mul(r, a, b)		{	(r).x = (a).x * (b).x; (r).y = (a).y * (b).y; (r).z = (a).z * (b).z;	}
#define	Vec3MulConst(r, a, k)	{	float	_k = k;	(r).x = (a).x * _k; (r).y = (a).y * _k; (r).z = (a).z * _k;	}
#define	Vec3Div(r, a, b)		{	(r).x = (a).x / (b).x; (r).y = (a).y / (b).y; (r).z = (a).z / (b).z;	}
#define	Vec3DivConst(r, a, k)	{	float	ik = 1.f / k; (r).x = (a).x * ik; (r).y = (a).y * ik; (r).z = (a).z * ik;	}

#define	Vec3Inc(r, a)			{	(r).x += (a).x; (r).y += (a).y; (r).z += (a).z;	}
#define	Vec3IncConst(r, k)		{	(r).x += k; (r).y += k; (r).z += k;	}
#define	Vec3Dec(r, a)			{	(r).x -= (a).x; (r).y -= (a).y; (r).z -= (a).z;	}
#define	Vec3DecConst(r, k)		{	(r).x -= k; (r).y -= k; (r).z -= k;	}
#define	Vec3Scale(r, a)			{	(r).x *= (a).x; (r).y *= (a).y; (r).z *= (a).z;	}
#define	Vec3ScaleConst(r, k)	{	float _k = k; (r).x *= _k; (r).y *= _k; (r).z *= _k;	}
#define	Vec3Shrink(r, a)		{	(r).x /= (a).x; (r).y /= (a).y; (r).z /= (a).z;	}
#define	Vec3ShrinkConst(r, k)	{	float ik = 1.f / k; (r).x *= ik; (r).y *= ik; (r).z *= ik;	}

#define	Vec3Dot(a, b)			((a).x * (b).x + (a).y * (b).y + (a).z * (b).z)
#define	Vec3Cross(r, a, b)		{\
	(r).x = (a).y * (b).z - (a).z * (b).y;\
	(r).y = (a).z * (b).x - (a).x * (b).z;\
	(r).z = (a).x * (b).y - (a).y * (b).x;\
}

#define	Vec3Clamp(v, a, b)		{\
	if ((v).x < a) (v).x = a; else if ((v).x > b) (v).x = b;\
	if ((v).x < a) (v).x = a; else if ((v).x > b) (v).x = b;\
	if ((v).x < a) (v).x = a; else if ((v).x > b) (v).x = b;\
}
#define	Vec3ClampMag(v, m)		{\
	const float	m2 = (m) * (m);\
	const float	l2 = Vec3Len2(v);\
	if	(l2 > m2)\
	{\
		const float	k = Math::Sqrt((float)(m2 / l2));\
		Vec3ScaleConst(v, k);\
	}\
}


#endif	// __NVECTOR__
