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


#ifndef	__NQUATERNION__
#define	__NQUATERNION__


		#include "math/nmath.h"


namespace GS {
	struct Vector4;
	class Matrix3;

	namespace NML {
		class File;
		class Tag;
	}

/*!
	@short	Quaternion.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
struct	Quaternion
{
		float		x, y, z, w;

		void		operator += (const Quaternion &b)	{	x += b.x; y += b.y; z += b.z; w += b.w;	};
		void		operator += (float k)				{	x += k; y += k; z += k;	w += k;	};
		void		operator -= (const Quaternion &b)	{	x -= b.x; y -= b.y; z -= b.z; w -= b.w;	};
		void		operator -= (float k)				{	x -= k; y -= k; z -= k;	w -= k;	};
		void		operator *= (const Quaternion &b)
		{
			Quaternion t = *this;
			w = t.w * b.w - (t.x * b.x + t.y * b.y + t.z * b.z);
			x = t.w * b.x + b.w * t.x + t.y * b.z - t.z * b.y;
			y = t.w * b.y + b.w * t.y + t.z * b.x - t.x * b.z;
			z = t.w * b.z + b.w * t.z + t.x * b.y - t.y * b.x;
		};
		void		operator *= (float k)	{	x *= k; y *= k; z *= k;	w *= k;		};
		void		operator /= (float k)	{	k = 1.f / k; x *= k; y *= k; z *= k; w *= k;	};

		Quaternion	operator + (const Quaternion &b) const
		{	return Quaternion(x + b.x, y + b.y, z + b.z, w + b.w);	}
		Quaternion	operator + (float v) const
		{	return Quaternion(x + v, y + v, z + v, w + v);	}
		Quaternion	operator - (const Quaternion &b) const
		{	return Quaternion(x - b.x, y - b.y, z - b.z, w - b.w);	}
		Quaternion	operator - (float v) const
		{	return Quaternion(x - v, y - v, z - v, w - v);	}
		Quaternion	operator * (const Quaternion &b) const
		{
			return Quaternion	(
									w * b.x + b.w * x + y * b.z - z * b.y,
									w * b.y + b.w * y + z * b.x - x * b.z,
									w * b.z + b.w * z + x * b.y - y * b.x,
									w * b.w - (x * b.x + y * b.y + z * b.z)
								);
		}
		Quaternion	operator * (float v) const
		{	return Quaternion(x * v, y * v, z * v, w * v);	}
		Quaternion	operator / (float v) const
		{	v = 1.f / v; return Quaternion(x * v, y * v, z * v, w * v);	}

		/// Dot product.
		float		Dot(const Quaternion &b) const
		{	return x * b.x + y * b.y + z * b.z + w * b.w;	}

		/// Normalize quaternion.
		Quaternion	Normalize() const;
		/// Inverse quaternion.
		Quaternion	Inverse() const;
		/// To rotation matrix.
		Matrix3		AsMatrix3() const;

		/// Distance to quaternion.
static	float		Distance(const Quaternion &a, const Quaternion &b);
		/// Slerp.
static	Quaternion Slerp(float t, const Quaternion &a, const Quaternion &b);

		/// From Euler angle.
static	Quaternion FromEuler(float x, float y, float z, Math::rOrder rorder = Math::rOrder_Default);
		/// Get an orientation from a 'look at' vector (look_at = to - from).
static	Quaternion LookAt(const Vector4 &at);
		/// From matrix3.
static	Quaternion FromMatrix3(const Matrix3 &m);
		/// From axis-angle.
static	Quaternion FromAxisAngle(float angle, float x, float y, float z);

		/// Set quaternion values.
		void		Set(float _x = 0, float _y = 0, float _z = 0, float _w = 1.f)
		{	x = _x; y = _y; z = _z;	w = _w;		}

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

		Quaternion(float _x = 0, float _y = 0, float _z = 0, float _w = 1.f)
		{	Set(_x, _y, _z, _w);	}
};

} // GS


#endif	// __NQUATERNION__
