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


		#include <cmath>
		#include "script_squirrel/cobject/vector_decl.h"
		#include "script_squirrel/cobject/matrix_decl.h"
		#include "math/matrix4.h"
		#include "math/matrix3.h"
		#include "math/vector.h"
		#include "log/log.h"

		using namespace GS;


#ifndef	_T
	#define _T
#endif

_IMPL_NATIVE_CONSTRUCTION(Vector, Vector4);

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

	switch (nparams)
	{
		case 1:	temp.Set();	break;
		case 2:
			if	(sa.GetType(2) == OT_INSTANCE)
			{
				_GetTypedParam(vec, 2, Vector4, Vector);
				if	(vec)
						temp = *vec;
				else	return sa.ThrowError("Vector() invalid instance type");
			}
			else
				temp.Set(sa.GetFloat(2));
			break;
		case 3:	temp.Set(sa.GetFloat(2), sa.GetFloat(3));	break;
		case 4:	temp.Set(sa.GetFloat(2), sa.GetFloat(3), sa.GetFloat(4));	break;
		case 5:	temp.Set(sa.GetFloat(2), sa.GetFloat(3), sa.GetFloat(4), sa.GetFloat(5));	break;

		default:
			return sa.ThrowError("Vector wrong parameters");
	}
	return construct_Vector(v, new Vector4(temp));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, _cloned)
	_GetTypedParam(vec, 2, Vector4, Vector);
	return construct_Vector(v, new Vector4(*vec));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, _set)
	_GetSelf(Vector4, Vector);

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

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

_MEMBER_FUNCTION_IMPL(Vector, _get)
	_GetSelf(Vector4, Vector);
	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': case 'r':
			return sa.Return(self->x);
		case 1: case 'y': case 'g':
			return sa.Return(self->y);
		case 2: case 'z': case 'b':
			return sa.Return(self->z);
		case 3: case 'w': case 'a':
			return sa.Return(self->w);
	}
	return SQ_ERROR;
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, _add)
	_GetSelf(Vector4, Vector);
	switch (sa.GetType(2))
	{
		case OT_INSTANCE:
		{	_GetTypedParam(vec, 2, Vector4, Vector);
			_SA_RETURN_OBJECT(new_Vector(v, *self + *vec))	}
		case OT_FLOAT:
			_SA_RETURN_OBJECT(new_Vector(v, *self + sa.GetFloat(2)));
	}
	return sa.ThrowError("Vector + operator: Invalid argument type.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, _sub)
	_GetSelf(Vector4, Vector);
	switch (sa.GetType(2))
	{
		case OT_INSTANCE:
		{	_GetTypedParam(vec, 2, Vector4, Vector);
			_SA_RETURN_OBJECT(new_Vector(v, *self - *vec));	}
		case OT_FLOAT:
			_SA_RETURN_OBJECT(new_Vector(v, *self - sa.GetFloat(2)));
	}
	return sa.ThrowError("Vector - operator: Invalid argument type.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, _mul)
	_GetSelf(Vector4, Vector);
	switch (sa.GetType(2))
	{
		case OT_INSTANCE:
		{
			// Vector * Vector.
			_CHECK_INST_PARAM_RAW(vec, 2, Vector4, Vector);
			if	(vec)
				_SA_RETURN_OBJECT(new_Vector(v, *self * *vec));
			// Vector * Matrix3.
			_CHECK_INST_PARAM_RAW(m3, 2, Matrix3, Matrix3);
			if	(m3)
				_SA_RETURN_OBJECT(new_Vector(v, *self * *m3));
			// Vector * Matrix4.
			_CHECK_INST_PARAM_RAW(m4, 2, Matrix4, Matrix4);
			if	(m4)
				_SA_RETURN_OBJECT(new_Vector(v, *self * *m4));
		}
		break;

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

_MEMBER_FUNCTION_IMPL(Vector,_div)
	_GetSelf(Vector4, Vector);
	switch (sa.GetType(2))
	{
		case OT_INSTANCE:
		{	_GetTypedParam(vec, 2, Vector4, Vector);
			_SA_RETURN_OBJECT(new_Vector(v, *self / *vec));	}
		case OT_FLOAT:
			_SA_RETURN_OBJECT(new_Vector(v, *self / sa.GetFloat(2)));
	}
	return sa.ThrowError("Vector / operator: Invalid argument type.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Set)
	_GetSelf(Vector4, Vector);
	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("Vector Set() wrong parameters");
	}
	return 0;
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Dot)
	_GetSelf(Vector4, Vector);
	_GetTypedParam(vec, 2, Vector4, Vector);
	return sa.Return(self->Dot(*vec));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Cross)
	_GetSelf(Vector4, Vector);
	_GetTypedParam(vec, 2, Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, self->Cross(*vec)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, AngleWithVector)
	_GetSelf(Vector4, Vector);
	_GetTypedParam(vec, 2, Vector4, Vector);
	return sa.Return(acosf(Types::Clamp(self->Dot(*vec), -1.f, 1.f)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Reverse)
	_GetSelf(Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, self->Reversed()));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Dist)
	_GetSelf(Vector4, Vector);
	_GetTypedParam(vec, 2, Vector4, Vector);
	return sa.Return(Vector4::Dist(*self, *vec));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Dist2)
	_GetSelf(Vector4, Vector);
	_GetTypedParam(vec, 2, Vector4, Vector);
	return sa.Return(Vector4::Dist2(*self, *vec));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Len)
	_GetSelf(Vector4, Vector);
	return sa.Return(self->Len());
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Len2)
	_GetSelf(Vector4, Vector);
	return sa.Return(self->Len2());
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, ClampMagnitude)
	_GetSelf(Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, self->ClampedMagnitude(0, sa.GetFloat(2))));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, clamp)
	_GetSelf(Vector4, Vector);
	Vector4 mn, mx;
	if	(sa.GetType(2) == OT_INSTANCE)
	{	_GetTypedParam(_mn, 2, Vector4, Vector); mn = *_mn;	}
	else	mn.Set(sa.GetFloat(2), sa.GetFloat(2), sa.GetFloat(2));
	if	(sa.GetType(3) == OT_INSTANCE)
	{	_GetTypedParam(_mx, 3, Vector4, Vector); mx = *_mx;	}
	else	mx.Set(sa.GetFloat(3), sa.GetFloat(3), sa.GetFloat(3));
	_SA_RETURN_OBJECT(new_Vector(v, self->Clamped(mn, mx)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, ApplyMatrix)
	_GetSelf(Vector4, Vector);
	_CHECK_INST_PARAM_RAW(m3, 2, Matrix3, Matrix3);
	if	(m3) _SA_RETURN_OBJECT(new_Vector(v, *self * *m3));
	_CHECK_INST_PARAM_RAW(m4, 2, Matrix4, Matrix4);
	if	(m4) _SA_RETURN_OBJECT(new_Vector(v, *self * *m4));
	return sa.ThrowError("Vector::ApplyMatrix(): Invalid parameter");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, ApplyRotationMatrix)
	_GetSelf(Vector4, Vector);
	_CHECK_INST_PARAM_RAW(m3, 2, Matrix3, Matrix3);
	if	(m3) _SA_RETURN_OBJECT(new_Vector(v, *self * *m3));
	_CHECK_INST_PARAM_RAW(m4, 2, Matrix4, Matrix4);
	if	(m4) _SA_RETURN_OBJECT(new_Vector(v, *self * Matrix3::FromMatrix4(*m4)));
	return sa.ThrowError("Vector::ApplyRotationMatrix(): Invalid parameter");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Randomize)
	if	(sa.GetParamCount() == 2)
		_SA_RETURN_OBJECT(new_Vector(v, Vector4::Random(0, sa.GetFloat(2))));
	_SA_RETURN_OBJECT(new_Vector(v, Vector4::Random(sa.GetFloat(2), sa.GetFloat(3))));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Normalize)
	_GetSelf(Vector4, Vector);
	if	(sa.GetParamCount() == 1)
			_SA_RETURN_OBJECT(new_Vector(v, self->Normalized()))
	else	_SA_RETURN_OBJECT(new_Vector(v, self->Normalized() * sa.GetFloat(2)))
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Lerp)
	_GetSelf(Vector4, Vector);
	float k = sa.GetFloat(2), ik = 1.f - k;
	_GetTypedParam(vec, 3, Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, *self * k + *vec * ik));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, _cmp)
	_GetSelf(Vector4, Vector);
	_GetTypedParam(vec, 2, Vector4, Vector);
	return sa.Return((*self) == (*vec) ? true : false);
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, IsEqual)
	_GetSelf(Vector4, Vector);
	_GetTypedParam(vec, 2, Vector4, Vector);
	float d2 = 0.0001f;
	switch (sa.GetParamCount())
	{
		case 2:		break;
		case 3:		d2 = sa.GetFloat(3);	break;
		default:	return sa.ThrowError("Vector::IsEqual() wrong parameter count");
	}
	d2 *= d2;
	return sa.Return(Vector4::Dist2(*self, *vec) < d2 ? true : false);
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Min)
	_GetSelf(Vector4, Vector);

	if	((sa.GetParamCount() == 2) || (sa.GetParamCount() == 4))
		switch (sa.GetType(2))
		{
			case OT_INSTANCE:
			{	_GetTypedParam(vec, 2, Vector4, Vector);
				_SA_RETURN_OBJECT(new_Vector(v, Vector4(self->x < vec->x ? self->x : vec->x, self->y < vec->y ? self->y : vec->y, self->z < vec->z ? self->z : vec->z)));	}
			case OT_FLOAT:
			{	float	x = sa.GetFloat(2), y = sa.GetFloat(3), z = sa.GetFloat(4);
				_SA_RETURN_OBJECT(new_Vector(v, Vector4(self->x < x ? self->x : x, self->y < y ? self->y : y, self->z < z ? self->z : z)));	}
		}
	return sa.ThrowError("Vector Min(): Invalid parameter list.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Max)
	_GetSelf(Vector4, Vector);
	if	((sa.GetParamCount() == 2) || (sa.GetParamCount() == 4))
		switch (sa.GetType(2))
		{
			case	OT_INSTANCE:
			{	_GetTypedParam(vec, 2, Vector4, Vector);
				_SA_RETURN_OBJECT(new_Vector(v, Vector4(self->x > vec->x ? self->x : vec->x, self->y > vec->y ? self->y : vec->y, self->z > vec->z ? self->z : vec->z)));	}
			case	OT_FLOAT:
			{	float	x = sa.GetFloat(2), y = sa.GetFloat(3), z = sa.GetFloat(4);
				_SA_RETURN_OBJECT(new_Vector(v, Vector4(self->x > x ? self->x : x, self->y > y ? self->y : y, self->z > z ? self->z : z)));	}
		}
	return sa.ThrowError("Vector Max(): Invalid parameter list.\n");
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, Scale)
	_GetSelf(Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, *self * sa.GetFloat(2)));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, AddReal)
	_GetSelf(Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, *self + Vector4(sa.GetFloat(2), sa.GetFloat(2), sa.GetFloat(2))));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, MulReal)
	_GetSelf(Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, *self * Vector4(sa.GetFloat(2), sa.GetFloat(2), sa.GetFloat(2))));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, SubReal)
	_GetSelf(Vector4, Vector);
	_SA_RETURN_OBJECT(new_Vector(v, *self - Vector4(sa.GetFloat(2), sa.GetFloat(2), sa.GetFloat(2))));
_END_IMPL

_MEMBER_FUNCTION_IMPL(Vector, DivReal)
	_GetSelf(Vector4, Vector);
	const float	ik = 1.f / sa.GetFloat(2);
	_SA_RETURN_OBJECT(new_Vector(v, *self * Vector4(ik, ik, ik)));
_END_IMPL

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

/*#
	Topic: Vector
	Type: Vector
#*/

/*#
	Section: VectorGeneric
	Desc: Generic
#*/
_BEGIN_CLASS(Vector)

	/*#
		Func: Vector
		Proto: Vector:float x = 0,float y = 0,float z = 0,float w = 1
		Desc: Create a new vector.
		Example: local v = Vector(1, 0, 0)
	#*/
_MEMBER_FUNCTION(Vector, constructor, -1, _T(".n|xnnn"))
_MEMBER_FUNCTION(Vector, _cloned, 2, _T(".x"))
_MEMBER_FUNCTION(Vector, _set, 3, _T("xs|n"))
_MEMBER_FUNCTION(Vector, _get, 2, _T("xs|n"))
_MEMBER_FUNCTION(Vector, _add, 2, _T("xx|n"))
_MEMBER_FUNCTION(Vector, _sub, 2, _T("xx|n"))
_MEMBER_FUNCTION(Vector, _mul, 2, _T("xx|n"))
_MEMBER_FUNCTION(Vector, _div, 2, _T("xx|n"))
_MEMBER_FUNCTION(Vector, _cmp, 2, _T("xx"))

	/*#
		Func: Set
		Proto: void:float x = 0,float y = 0,float z = 0,float w = 1
		Desc: Set vector values.
	#*/
_MEMBER_FUNCTION(Vector, Set, -1, _T("xnnnn"))
	/*#
		Func: ApplyMatrix
		Proto: Vector:[Matrix4|Matrix3] matrix
		Desc: Transform vector by a given matrix.
	#*/
_MEMBER_FUNCTION(Vector, ApplyMatrix, 2, _T("xx"))
	/*#
		Func: ApplyRotationMatrix
		Proto: Vector:[Matrix4|Matrix3] matrix
		Desc: Transform vector by the rotation part of a given matrix.
	#*/
_MEMBER_FUNCTION(Vector, ApplyRotationMatrix, 2, _T("xx"))

_MEMBER_FUNCTION(Vector, AddReal, 2, _T("xn"))
_MEMBER_FUNCTION(Vector, MulReal, 2, _T("xn"))
_MEMBER_FUNCTION(Vector, SubReal, 2, _T("xn"))
_MEMBER_FUNCTION(Vector, DivReal, 2, _T("xn"))
_MEMBER_FUNCTION(Vector, Scale, 2, _T("xn"))
	/*#
		Func: Clamp
		Proto: Vector:(Vector|float) min,(Vector|float) max
		Desc: Individually clamp vector components to a given range.
	#*/
_MEMBER_FUNCTION(Vector, clamp, 3, _T("x x|n x|n"))
	/*#
		Func: ClampMagnitude
		Proto: Vector:float len
		Desc: Clamp vector magnitude to a specific length.
	#*/
_MEMBER_FUNCTION(Vector, ClampMagnitude, 2, _T("xn"))
	/*#
		Func: Reverse
		Proto: Vector:
		Desc: Return the reverse vector.
	#*/
_MEMBER_FUNCTION(Vector, Reverse, 1, _T("x"))

	/*#
		Func: Min
		Proto: Vector:[Vector|float x,float y, float z] min
		Desc: Return the smallest value of the vector component or parameter for all the vector components.
	#*/
_MEMBER_FUNCTION(Vector, Min, -2, _T("xn|xnn"))
	/*#
		Func: Max
		Proto: Vector:[Vector|float x,float y, float z] max
		Desc: Return the largest value of the vector component or parameter for all the vector components.
	#*/
_MEMBER_FUNCTION(Vector, Max, -2, _T("xn|xnn"))

	/*#
		Func: Dot
		Proto: float:Vector v
		Desc: Return the dot product between two vectors.
	#*/
_MEMBER_FUNCTION(Vector, Dot, 2, _T("xx"))
	/*#
		Func: Cross
		Proto: Vector:Vector v
		Desc: Return the cross product between two vectors.
	#*/
_MEMBER_FUNCTION(Vector, Cross, 2, _T("xx"))
	/*#
		Func: Len
		Proto: float:Vector v
		Desc: Return the length of this vector.
	#*/
_MEMBER_FUNCTION(Vector, Len, 1, _T("x"))
	/*#
		Func: Len2
		Proto: float:Vector v
		Desc: Return the squared length of this vector.
	#*/
_MEMBER_FUNCTION(Vector, Len2, 1, _T("x"))
	/*#
		Func: AngleWithVector
		Proto: float:Vector v
		Desc: Return the angle between two vectors.
	#*/
_MEMBER_FUNCTION(Vector, AngleWithVector, 2, _T("xx"))

	/*#
		Func: Normalize
		Proto: Vector:float length = 1
		Desc: Return a normalized version of this vector scaled to a constant.
	#*/
_MEMBER_FUNCTION(Vector, Normalize, -1, _T("xn"))
	/*#
		Func: Dist
		Proto: float:Vector v
		Desc: Return the distance between two vectors.
	#*/
_MEMBER_FUNCTION(Vector, Dist, 2, _T("xx"))
	/*#
		Func: Dist2
		Proto: float:Vector v
		Desc: Return the squared distance between two vectors.
	#*/
_MEMBER_FUNCTION(Vector, Dist2, 2, _T("xx"))
	/*#
		Func: Lerp
		Proto: float:Vector v,float t
		Desc: Return a linearly interpolated vector between two vectors.
	#*/
_MEMBER_FUNCTION(Vector, Lerp, 3, _T("xnx"))
	/*#
		Func: Randomize
		Proto: Vector:Vector v,float min,float max
		Desc: Return a random vector in a given range.
	#*/
_MEMBER_FUNCTION(Vector, Randomize, -2, _T(".nn"))

	/*#
		Func: IsEqual
		Proto: bool:Vector v,float epsilon = 0.0001
		Desc: Test vectors for equality with a given espilon tolerance.
	#*/
_MEMBER_FUNCTION(Vector, IsEqual, -2, _T("xxn"))
	/*#
		Func: Print
		Proto: void:
		Desc: Dump this vector components to the engine log.
	#*/
_MEMBER_FUNCTION(Vector, Print, -1, _T("x"))

_END_CLASS(Vector)

//-----------------------------------------------
void		UCBind_RegisterVector(HSQUIRRELVM vm)
//-----------------------------------------------
{
	_INIT_CLASS(vm, Vector);
}
