// Simple extensions of the standard math library & the O3D library
//

o3djs.require('o3djs.math');

o3djs.require('patapi.base');


var Math3D = o3djs.math;	// O3D Math

////////////////////////////////////////////////////////////////////////////
// Extends the standard math object
//
patapi.helpers.Extend( Math,
{
	sign : function( a ) { return a < 0 ? -1 : (a > 0 ? +1 : 0); }
	
} );


////////////////////////////////////////////////////////////////////////////
// Extends the O3D math object
//
patapi.helpers.Extend( Math3D.matrix4,
{
	// Gets the XYZ Euler angles from a matrix
	// Can be used later in the rotationZYX() method to reconstruct the matrix
	getEulerAngles : function( _m )
	{
		var	Ret = [];
		var	fSinY = Math.min( +1.0, Math.max( -1.0, _m[0][2] ) );
		var	fCosY = Math.sqrt( 1.0 - fSinY*fSinY );
		if ( _m[0][0] < 0.0 && _m[2][2] < 0.0 )
			fCosY = -fCosY;

		if ( Math.abs( fCosY ) > 1e-6 )
		{
			Ret[0] =  Math.atan2( _m[1][2] / fCosY, _m[2][2] / fCosY );
			Ret[1] = -Math.atan2( fSinY, fCosY );
			Ret[2] =  Math.atan2( _m[0][1] / fCosY, _m[0][0] / fCosY );
		}
		else
		{
			Ret[0] =  Math.atan2( -_m[2][1], _m[1][1] );
			Ret[1] = -Math.asin( fSinY );
			Ret[2] = 0.0;
		}

		return	Ret;
	}
} );


patapi.helpers.Extend( Math,
{
	// Returns the array of roots for any polynomial of degree 0 to 4
	//
	SolvePolynomial : function( a, b, c, d, e )
	{
		if ( e != 0.0 )
			return Math.SolveQuartic( a, b, c, d, e );
		else if ( d != 0.0 )
			return Math.SolveCubic( a, b, c, d );
		else if ( c != 0.0 )
			return Math.SolveQuadratic( a, b, c );

		return Math.SolveLinear( a, b );
	},

	// Returns the array of 1 real root of a linear polynomial  a + b x = 0
	//
	SolveLinear : function( a, b )
	{
		if ( b == 0.0 )
			return [undefined];

		return [-a / b];
	},

	// Returns the array of 2 real roots of a quadratic polynomial  a + b x + c x^2 = 0
	// NOTE: If roots are imaginary, the returned value in the array will be "undefined"
	//
	SolveQuadratic : function( a, b, c )
	{
		if ( c == 0.0 )
			throw "3th coefficient is 0! You should resolve a linear polynomial instead !"

		var	Delta = b * b - 4 * a * c;
		if ( Delta < 0.0 )
			return	[undefined, undefined];

		Delta = Math.sqrt( Delta );
		var	OneOver2a = 0.5 / a;

		return	[OneOver2a * (-b - Delta), OneOver2a * (-b + Delta)];
	},

	// Returns the array of 3 real roots of a cubic polynomial  a + b x + c x^2 + d x^3 = 0
	// NOTE: If roots are imaginary, the returned value in the array will be "undefined"
	// Code from http://www.codeguru.com/forum/archive/index.php/t-265551.html (pretty much the same as http://mathworld.wolfram.com/CubicFormula.html)
	//
	SolveCubic : function( a, b, c, d )
	{
		if ( d == 0.0 )
			throw "4th coefficient is 0! You should resolve a quadratic polynomial instead !"

		// Adjust coefficients
		var a1 = c / d;
		var a2 = b / d;
		var a3 = a / d;

		var Q = (a1 * a1 - 3 * a2) / 9;
		var R = (2 * a1 * a1 * a1 - 9 * a1 * a2 + 27 * a3) / 54;
		var Qcubed = Q * Q * Q;
		var d = Qcubed - R * R;

		var	Result = [];
		if ( d >= 0 )
		{	// Three real roots
			if ( Q < 0.0 )
				return [undefined, undefined, undefined];

			var theta = Math.acos( R / Math.sqrt(Qcubed) );
			var sqrtQ = Math.sqrt( Q );

			Result[0] = -2 * sqrtQ * Math.cos( theta / 3) - a1 / 3;
			Result[1] = -2 * sqrtQ * Math.cos( (theta + 2 * Math.PI) / 3 ) - a1 / 3;
			Result[2] = -2 * sqrtQ * Math.cos( (theta + 4 * Math.PI) / 3 ) - a1 / 3;
		}
		else
		{	// One real root
			var e = Math.pow( Math.sqrt( -d ) + Math.abs( R ), 1.0 / 3.0 );
			if ( R > 0 )
				e = -e;

			Result[0] = Result[1] = Result[2] = (e + Q / e) - a1 / 3.0;
		}

		return	Result;
	},

	// Returns the array of 4 real roots of a quartic polynomial  a + b x + c x^2 + d x^3 + e x^4 = 0
	// NOTE: If roots are imaginary, the returned value in the array will be "undefined"
	// Code from http://mathworld.wolfram.com/QuarticEquation.html
	//
	SolveQuartic : function( a, b, c, d, e )
	{
		if ( e == 0.0 )
			throw "5th coefficient is 0! You should resolve a cubic polynomial instead !"

		// Adjust coefficients
		var a0 = a / e;
		var a1 = b / e;
		var a2 = c / e;
		var a3 = d / e;

		// Find a root for the following cubic equation : y^3 - a2 y^2 + (a1 a3 - 4 a0) y + (4 a2 a0 - a1 ^2 - a3^2 a0) = 0
		var	b0 = 4 * a2 * a0 - a1 * a1 - a3 * a3 * a0;
		var	b1 = a1 * a3 - 4 * a0;
		var	b2 = -a2;
		var	Roots = Math.SolveCubic( b0, b1, b2, 1 );
		var	y = Math.max( Roots[0], Math.max( Roots[1], Roots[2] ) );

		// Compute R, D & E
		var	R = 0.25 * a3 * a3 - a2 + y;
		if ( R < 0.0 )
			return [undefined, undefined, undefined, undefined];
		R = Math.sqrt( R );

		var	D, E;
		if ( R == 0.0 )
		{
			D = Math.sqrt( 0.75 * a3 * a3 - 2 * a2 + 2 * Math.sqrt( y * y - 4 * a0 ) );
			E = Math.sqrt( 0.75 * a3 * a3 - 2 * a2 - 2 * Math.sqrt( y * y - 4 * a0 ) );
		}
		else
		{
			var	Rsquare = R * R;
			var	Rrec = 1.0 / R;
			D = Math.sqrt( 0.75 * a3 * a3 - Rsquare - 2 * a2 + 0.25 * Rrec * (4 * a3 * a2 - 8 * a1 - a3 * a3 * a3) );
			E = Math.sqrt( 0.75 * a3 * a3 - Rsquare - 2 * a2 - 0.25 * Rrec * (4 * a3 * a2 - 8 * a1 - a3 * a3 * a3) );
		}

		// Compute the 4 roots
		var	Result =
		[
			-0.25 * a3 + 0.5 * R + 0.5 * D,
			-0.25 * a3 + 0.5 * R - 0.5 * D,
			-0.25 * a3 - 0.5 * R + 0.5 * E,
			-0.25 * a3 - 0.5 * R - 0.5 * E
		];

		return	Result;
	}
} );

// alert( Math.sign( -4 ) );
// alert( Math.abs( -4 ) );
// alert( Math3D.matrix4.getEulerAngles( Math3D.matrix4.rotationX( 0.1 ) ) )
