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

		#include "script_squirrel/cobject/quaternion_decl.h"
		#include "script_squirrel/cobject/matrix_decl.h"
		#include "script_squirrel/cobject/vector_decl.h"
		#include "math/matrix3.h"
		#include "math/quaternion.h"
		#include "math/vector.h"
		#include "nstring/nstring.h"
		#include "log/log.h"

		using namespace GS;


_DECL_CLASS(Quaternion)
_IMPL_NATIVE_CONSTRUCTION(Quaternion, Quaternion)

//------------------------------------------------------------------------------
_MEMBER_FUNCTION_IMPL(Quaternion, constructor)
	Quaternion	temp;
	int			nparams = sa.GetParamCount();

	switch (nparams)
	{
		case 1:	temp.Set();	break;
		case 5:
			temp.Set(sa.GetFloat(2), sa.GetFloat(3), sa.GetFloat(4), sa.GetFloat(5));
			break;

		default:
			return sa.ThrowError("Quaternion() wrong parameter count");
	}
	return construct_Quaternion(v, new Quaternion(temp));

_END_IMPL

//------------------------------------------------------------------------------
_MEMBER_FUNCTION_IMPL(Quaternion, _cloned)
	_GetTypedParam(quat, 2, Quaternion, Quaternion);
	return construct_Quaternion(v, new Quaternion(*quat));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, _set)
	_GetSelf(Quaternion, Quaternion);

	const SQChar	*s = sa.GetString(2);
	int				index = s ? s[0] : sa.GetInt(2);

	switch (index)
	{
		case 0: case 'x':
			return sa.Return(self->x = sa.GetFloat(3));
		case 1: case 'y':
			return sa.Return(self->y = sa.GetFloat(3));
		case 2: case 'z':
			return sa.Return(self->z = sa.GetFloat(3));
		case 3: case 'w':
			return sa.Return(self->w = sa.GetFloat(3));
	}
	return SQ_ERROR;
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, _get)
	_GetSelf(Quaternion, Quaternion);
	const SQChar	*s = sa.GetString(2);
	if	(s && (s[1] != 0))
		return SQ_ERROR;
	int				index = s && (s[1] == 0) ? s[0] : sa.GetInt(2);

	switch (index)
	{
		case 0: case 'x':
			return sa.Return(self->x);
		case 1: case 'y':
			return sa.Return(self->y);
		case 2: case 'z':
			return sa.Return(self->z);
		case 3: case 'w':
			return sa.Return(self->w);
	}
	return SQ_ERROR;
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, _mul)
	_GetSelf(Quaternion, Quaternion);
	switch (sa.GetType(2))
	{
		case	OT_INSTANCE:
		{
			// Quaternion * Quaternion.
			_CHECK_INST_PARAM_RAW(quat, 2, Quaternion, Quaternion);
			if	(quat)
				_SA_RETURN_OBJECT(new_Quaternion(v, *self * *quat));
		}
		break;

		// Quaternion * Scalar
		case	OT_INTEGER:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self * (float)sa.GetInt(2)));
		case	OT_FLOAT:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self * sa.GetFloat(2)));
	}
	return sa.ThrowError("Quaternion * operator: Invalid argument type.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, _div)
	_GetSelf(Quaternion, Quaternion);
	switch (sa.GetType(2))
	{
		// Quaternion / Scalar
		case	OT_INTEGER:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self / (float)sa.GetInt(2)));
		case	OT_FLOAT:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self / sa.GetFloat(2)));
	}
	return sa.ThrowError("Quaternion / operator: Invalid argument type.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, _add)
	_GetSelf(Quaternion, Quaternion);
	switch (sa.GetType(2))
	{
		case	OT_INSTANCE:
		{
			// Quaternion + Quaternion.
			_CHECK_INST_PARAM_RAW(quat, 2, Quaternion, Quaternion);
			if	(quat)
				_SA_RETURN_OBJECT(new_Quaternion(v, *self + *quat));
		}
		break;

		// Quaternion + Scalar
		case	OT_INTEGER:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self + (float)sa.GetInt(2)));
		case	OT_FLOAT:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self + sa.GetFloat(2)));
	}
	return sa.ThrowError("Quaternion + operator: Invalid argument type.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, _sub)
	_GetSelf(Quaternion, Quaternion);
	switch (sa.GetType(2))
	{
		case	OT_INSTANCE:
		{
			// Quaternion - Quaternion.
			_CHECK_INST_PARAM_RAW(quat, 2, Quaternion, Quaternion);
			if	(quat)
				_SA_RETURN_OBJECT(new_Quaternion(v, *self - *quat));
		}
		break;

		// Quaternion - Scalar
		case	OT_INTEGER:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self - (float)sa.GetInt(2)));
		case	OT_FLOAT:
			_SA_RETURN_OBJECT(new_Quaternion(v, *self - sa.GetFloat(2)));
	}
	return sa.ThrowError("Quaternion - operator: Invalid argument type.\n");
_END_IMPL

//------------------------------------------------------------------------------
_MEMBER_FUNCTION_IMPL(Quaternion, Set)
	_GetSelf(Quaternion, Quaternion);
	switch	(sa.GetParamCount())
	{
		case 1:	self->Set(); break;
		case 2:	self->Set(sa.GetFloat(2)); break;
		case 3:	self->Set(sa.GetFloat(2), sa.GetFloat(3)); break;
		case 4:	self->Set(sa.GetFloat(2), sa.GetFloat(3), sa.GetFloat(4)); break;
		case 5:	self->Set(sa.GetFloat(2), sa.GetFloat(3), sa.GetFloat(4), sa.GetFloat(5)); break;
		default:
			return sa.ThrowError("Quaternion Set() wrong parameters");
	}
	return 0;
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, Slerp)
	_GetSelf(Quaternion, Quaternion);
	float	k = sa.GetFloat(2);
	_GetTypedParam(quat, 3, Quaternion, Quaternion)
		_SA_RETURN_OBJECT(new_Quaternion(v, Quaternion::Slerp(k, *self, *quat)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, Normalize)
	_GetSelf(Quaternion, Quaternion);
	_SA_RETURN_OBJECT(new_Quaternion(v, self->Normalize()));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, Inverse)
	_GetSelf(Quaternion, Quaternion);
	_SA_RETURN_OBJECT(new_Quaternion(v, self->Inverse()));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, Dot)
	_GetSelf(Quaternion, Quaternion);
	_GetTypedParam(quat, 2, Quaternion, Quaternion);
	return sa.Return(self->Dot(*quat));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, AsMatrix3)
	_GetSelf(Quaternion, Quaternion);
	_SA_RETURN_OBJECT(new_Matrix3(v, self->AsMatrix3()));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, QuaternionFromAxisAngle)
	_GetSelf(Quaternion, Quaternion);
	_GetTypedParam(vec, 3, Vector4, Vector);
	_SA_RETURN_OBJECT(new_Quaternion(v, Quaternion::FromAxisAngle(sa.GetFloat(2), vec->x, vec->y, vec->z)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, QuaternionFromMatrix3)
	_GetSelf(Quaternion, Quaternion);
	_GetTypedParam(mtx, 2, Matrix3, Matrix3);
	_SA_RETURN_OBJECT(new_Quaternion(v, Quaternion::FromMatrix3(*mtx)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, QuaternionLookAt)
	_GetSelf(Quaternion, Quaternion);
	_GetTypedParam(vec, 2, Vector4, Vector);
	_SA_RETURN_OBJECT(new_Quaternion(v, Quaternion::LookAt(*vec)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Quaternion, Print)
	_GetSelf(Quaternion, Quaternion);
	const char	*label = "Quaternion";
	if	(sa.GetParamCount() == 2)
		label = sa.GetString(2);
	__LOG__ << label << ": { x = " << self->x << ", y = " << self->y << ", z = " << self->z << ", w = " << self->w << "}\n";
	return 0;
_END_IMPL

/*#
	Topic: Quaternion
	Type: Quaternion
#*/

/*#
	Section: QuaternionGeneric
	Desc: Generic
#*/
_BEGIN_CLASS(Quaternion)

	/*#
		Func: Quaternion
		Proto: Quaternion:float x = 0,float y = 0,float z = 0,float w = 1
		Desc: Create a new quaternion.
		Example:
local u = Quaternion()
local v = Quaternion(0, 0, 0, 1)

u.Set(-1, 0, 0)
	#*/
_MEMBER_FUNCTION(Quaternion, constructor, -1, _T(".n|xnnn"))
_MEMBER_FUNCTION(Quaternion, _cloned, 2, _T(".x"))
_MEMBER_FUNCTION(Quaternion, _set, 2, _T("xs|n"))
_MEMBER_FUNCTION(Quaternion, _get, 2, _T("xs|n"))
_MEMBER_FUNCTION(Quaternion, _add, 2, _T("xx|n"))
_MEMBER_FUNCTION(Quaternion, _sub, 2, _T("xx|n"))
_MEMBER_FUNCTION(Quaternion, _mul, 2, _T("xx|n"))
_MEMBER_FUNCTION(Quaternion, _div, 2, _T("xn"))

	/*#
		Func: Set
		Proto: void:float x = 0,float y = 0,float z = 0,float w = 1
		Desc: Set quaternion values.
	#*/
_MEMBER_FUNCTION(Quaternion, Set, -1, _T("xnnnn"))
	/*#
		Func: Slerp
		Proto: Quaternion:float,Quaternion
		Desc: Returns a spherical linear interpolation.
	#*/
_MEMBER_FUNCTION(Quaternion, Slerp, 3, _T("xnx"))
	/*#
		Func: Normalize
		Proto: Quaternion:void
		Desc: Returns the normalized quaternion.
	#*/
_MEMBER_FUNCTION(Quaternion, Normalize, 1, _T("x"))
	/*#
		Func: Inverse
		Proto: Quaternion:void
		Desc: Returns the inverse quaternion.
	#*/
_MEMBER_FUNCTION(Quaternion, Inverse, 1, _T("x"))
	/*#
		Func: Dot
		Proto: Quaternion:Quaternion
		Desc: Returns the Dot product.
	#*/
_MEMBER_FUNCTION(Quaternion, Dot, 2, _T("xx"))
	/*#
		Func: AsMatrix3
		Proto: Matrix3:void
		Desc: Returns the Matrix3 from the quaternion.
	#*/
_MEMBER_FUNCTION(Quaternion, AsMatrix3, 1, _T("x"))
	/*#
		Func: QuaternionFromAxisAngle
		Proto: Quaternion:float angle,Vector axis
		Desc: Returns a quaternion based on angle and axis.
	#*/
_MEMBER_FUNCTION(Quaternion, QuaternionFromAxisAngle, 3, _T(".nx"))
	/*#
		Func: QuaternionFromMatrix3
		Proto: Quaternion:Matrix3
		Desc: Returns a quaternion based on a Matrix3.
	#*/
_MEMBER_FUNCTION(Quaternion, QuaternionFromMatrix3, 2, _T(".x"))
	/*#
		Func: QuaternionLookAt
		Proto: Quaternion:Vector direction
		Desc: Returns a quaternion looking at direction.
	#*/
_MEMBER_FUNCTION(Quaternion, QuaternionLookAt, 2, _T(".x"))
	/*#
		Func: Print
		Proto: void:
		Desc: Dump this quaternion components (x,y,z,w) to the engine log.
	#*/
_MEMBER_FUNCTION(Quaternion, Print, -1, _T("x"))

_END_CLASS(Quaternion)