#include "Mix/Vector3.h"
#include "Mix/Vector4.h"

namespace Mix{

Vector3::Vector3( void )
{
	x = 0.0f;
	y = 0.0f;
	z = 0.0f;
}

Vector3::Vector3( const Vector3& v )
{
	x = v.x;
	y = v.y;
	z = v.z;
}

Vector3::Vector3( const Vector4& v )
{
	x = v.x;
	y = v.y;
	z = v.z;
}

Vector3::Vector3( Float32 vx, Float32 vy, Float32 vz )
{
	x = vx;
	y = vy;
	z = vz;
}

Boolean Vector3::operator == ( const Vector3& v ) const
{
	if( ( MIX_FLOAT_EQUAL( x, v.x ) == False ) ||
		( MIX_FLOAT_EQUAL( y, v.y ) == False ) ||
		( MIX_FLOAT_EQUAL( z, v.z ) == False ) )
	{
		return False;
	}

	return True;
}

Boolean Vector3::operator != ( const Vector3& v ) const
{
	if( ( MIX_FLOAT_EQUAL( x, v.x ) == False ) ||
		( MIX_FLOAT_EQUAL( y, v.y ) == False ) ||
		( MIX_FLOAT_EQUAL( z, v.z ) == False ) )
	{
		return True;
	}

	return False;
}

Vector3& Vector3::operator = ( const Vector3& v )
{
	x = v.x;
	y = v.y;
	z = v.z;

	return *this;
}

Vector3& Vector3::operator = ( const Vector4& v )
{
	x = v.x;
	y = v.y;
	z = v.z;

	return *this;
}

Vector3& Vector3::operator += ( const Vector3& v )
{
	x += v.x;
	y += v.y;
	z += v.z;

	return *this;
}

Vector3& Vector3::operator -= ( const Vector3& v )
{
	x -= v.x;
	y -= v.y;
	z -= v.z;

	return *this;
}

Vector3& Vector3::operator *= ( const Vector3& v )
{
	x *= v.x;
	y *= v.y;
	z *= v.z;

	return *this;
}

Vector3& Vector3::operator /= ( const Vector3& v )
{
	x = MIX_FLOAT_DIV( x, v.x );
	y = MIX_FLOAT_DIV( y, v.y );
	z = MIX_FLOAT_DIV( z, v.z );

	return *this;
}

Vector3 Vector3::operator + ( const Vector3& v ) const
{
	Vector3 ret( *this );

	ret.x += v.x;
	ret.y += v.y;
	ret.z += v.z;

	return ret;
}

Vector3 Vector3::operator - ( const Vector3& v ) const
{
	Vector3 ret( *this );

	ret.x -= v.x;
	ret.y -= v.y;
	ret.z -= v.z;

	return ret;
}

Vector3 Vector3::operator * ( const Vector3& v ) const
{
	Vector3 ret( *this );

	ret.x *= v.x;
	ret.y *= v.y;
	ret.z *= v.z;

	return ret;
}

Vector3 Vector3::operator / ( const Vector3& v ) const
{
	Vector3 ret( *this );

	ret.x = MIX_FLOAT_DIV( ret.x, v.x );
	ret.y = MIX_FLOAT_DIV( ret.y, v.y );
	ret.z = MIX_FLOAT_DIV( ret.z, v.z );

	return ret;
}

Vector3 Vector3::operator +( void ) const
{
	return *this;
}

Vector3 Vector3::operator -( void ) const
{
	return ( *this * -1.0f );
}

Vector3 Vector3::operator * ( Float32 s ) const
{
	Vector3 ret( *this );

	ret.x *= s;
	ret.y *= s;
	ret.z *= s;

	return ret;
}

Vector3 Vector3::operator / ( Float32 s ) const
{
	Vector3 ret;

	if( MIX_FLOAT_IS_ZERO( s ) == True )
	{
		ret.x = 0.0f;
		ret.y = 0.0f;
		ret.z = 0.0f;
	}
	else
	{
		Float32 invS = 1.0f / s;

		ret.x = x * invS;
		ret.y = y * invS;
		ret.z = z * invS;
	}

	return ret;
}

Vector3& Vector3::operator *= ( Float32 s )
{
	x *= s;
	y *= s;
	z *= s;

	return *this;
}

Vector3& Vector3::operator /= ( Float32 s )
{
	if( MIX_FLOAT_IS_ZERO( s ) == True )
	{
		x = 0.0f;
		y = 0.0f;
		z = 0.0f;
	}
	else
	{
		Float32 invS = 1.0f / s;

		x *= invS;
		y *= invS;
		z *= invS;
	}

	return *this;
}

void Vector3::Set( Float32 vx, Float32 vy, Float32 vz )
{
	x = vx;
	y = vy;
	z = vz;
}

Float32 Vector3::Normalize( void )
{
	Float32 length = GetLength();

	if( MIX_FLOAT_IS_ZERO( length ) == True )
	{
		x = 0.0f;
		y = 0.0f;
		z = 0.0f;
	}
	else
	{
		Float32 invLength = ( 1.0f / length );

		x *= invLength;
		y *= invLength;
		z *= invLength;
	}

	return length;
}

Vector3 Vector3::ToNormalize( Float32* pLength ) const
{
	Mix::Vector3 ret( *this );

	if( pLength == NULL )
	{
		ret.Normalize();
	}
	else
	{
		*pLength = ret.Normalize();
	}

	return ret;
}

void Vector3::Saturate( void )
{
	x = MIX_CLAMP( x, 0.0f, 1.0f );
	y = MIX_CLAMP( y, 0.0f, 1.0f );
	z = MIX_CLAMP( z, 0.0f, 1.0f );
}

Vector3 Vector3::ToSaturate( void ) const
{
	Mix::Vector3 ret( *this );

	ret.Saturate();

	return ret;
}

Float32 Vector3::GetLengthSqr( void ) const
{
	return ( x * x ) + ( y * y ) + ( z * z );
}

Float32 Vector3::GetLength( void ) const
{
	return ::sqrtf( ( x * x ) + ( y * y ) + ( z * z ) );
}

Float32 Vector3::GetLengthF( void ) const
{
	return Mix::SqrtF( ( x * x ) + ( y * y ) + ( z * z ) );
}

Float32 Vector3::Dot( const Vector3& l, const Vector3& r )
{
	return ( l.x * r.x ) + ( l.y * r.y ) + ( l.z * r.z );
}

Vector3 Vector3::Cross( const Vector3& l, const Vector3& r )
{
	Float32 y2z3 = ( l.y * r.z );
	Float32 z2y3 = ( l.z * r.y );
	Float32 z2x3 = ( l.z * r.x );
	Float32 x2z3 = ( l.x * r.z );
	Float32 x2y3 = ( l.x * r.y );
	Float32 y2x3 = ( l.y * r.x );

	return Vector3( ( y2z3 - z2y3 ), ( z2x3 - x2z3 ), ( x2y3 - y2x3 ) );
}

Vector3 Vector3::Rand( void )
{
	Mix::Vector3 v;

	v.x = Mix::RandF();
	v.y = Mix::RandF();
	v.z = Mix::RandF();

	return v;
}

Vector3 Vector3::Rand( const Mix::Vector3& v )
{
	Mix::Vector3 vv;

	vv.x = Mix::RandF( v.x );
	vv.y = Mix::RandF( v.y );
	vv.z = Mix::RandF( v.z );

	return vv;
}

Vector3 Vector3::Rand( const Mix::Vector3& v0, const Mix::Vector3& v1 )
{
	Mix::Vector3 v;

	v.x = Mix::RandF( v0.x, v1.x );
	v.y = Mix::RandF( v0.y, v1.y );
	v.z = Mix::RandF( v0.z, v1.z );

	return v;
}

Vector3 Vector3::Min( const Mix::Vector3& v0, const Mix::Vector3& v1 )
{
	Mix::Vector3 v;

	v.x = min( v0.x, v1.x );
	v.y = min( v0.y, v1.y );
	v.z = min( v0.z, v1.z );

	return v;
}

Vector3 Vector3::Max( const Mix::Vector3& v0, const Mix::Vector3& v1 )
{
	Mix::Vector3 v;

	v.x = max( v0.x, v1.x );
	v.y = max( v0.y, v1.y );
	v.z = max( v0.z, v1.z );

	return v;
}

Vector3 Vector3::Lerp( const Mix::Vector3& vs, const Mix::Vector3& ve, Float32 t )
{
	Float32 tt = MIX_CLAMP( t, 0.0f, 1.0f );

	return ( vs * ( 1.0f - tt ) ) + ( ve * tt );
}

Mix::Vector3 Vector3::Slerp( const Mix::Vector3& vs, const Mix::Vector3& ve, Float32 t )
{
	Mix::Vector3 s = vs.ToNormalize();
	Mix::Vector3 e = ve.ToNormalize();

	float angle = acosf( min( 1.0f, Mix::Vector3::Dot( s, e ) ) );
	float sinTh = sinf( angle );
	float invSinTh = MIX_FLOAT_RECIPROCAL( sinTh );

	float Ps = sinf( angle * ( 1.0f - t ) );
	float Pe = sinf( angle * t );

	return ( ( Ps * s + Pe * e ) * invSinTh ).ToNormalize();
}

}