package org.papervision3d.core.math
{
	import org.papervision3d.Papervision3D;
	
/**
* The Number3D class represents a value in a three-dimensional coordinate system.
*
* Properties x, y and z represent the horizontal, vertical and z the depth axes respectively.
*
*/
public class Number3D
{
	/**
	* The horizontal coordinate value.
	*/
	public var x: Number;

	/**
	* The vertical coordinate value.
	*/
	public var y: Number;

	/**
	* The depth coordinate value.
	*/
	public var z: Number;

	/**
	 * pre-made Number3D : used by various methods as a way to temporarily store Number3Ds. 
	 */
	static private var temp : Number3D = Number3D.ZERO; 
	
	static public var toDEGREES :Number = 180/Math.PI;
	static public var toRADIANS :Number = Math.PI/180;
	
	/**
	* Creates a new Number3D object whose three-dimensional values are specified by the x, y and z parameters. If you call this constructor function without parameters, a Number3D with x, y and z properties set to zero is created.
	*
	* @param	x	The horizontal coordinate value. The default value is zero.
	* @param	y	The vertical coordinate value. The default value is zero.
	* @param	z	The depth coordinate value. The default value is zero.
	*/
	public function Number3D( x: Number=0, y: Number=0, z: Number=0 )
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}


	/**
	* Returns a new Number3D object that is a clone of the original instance with the same three-dimensional values.
	*
	* @return	A new Number3D instance with the same three-dimensional values as the original Number3D instance.
	*/
	public function clone():Number3D
	{
		return new Number3D( this.x, this.y, this.z );
	}
	
	/**
	 * Copies the values of this Number3d to the passed Number3d.
	 * 
	 */
	public function copyTo(n:Number3D):void
	{
		n.x = x;
		n.y = y;
		n.z = z;
	}	
	/**
	 * Copies the values of this Number3d to the passed Number3d.
	 * 
	 */
	public function copyFrom(n:Number3D):void
	{
		x = n.x; 
		y = n.y; 
		z = n.z; 
	}
	
	/** 
	 * Quick way to set the properties of the Number3D
	 * 
	 */
	public function reset(newx:Number = 0, newy:Number = 0, newz:Number = 0):void
	{
		x = newx; 
		y = newy; 
		z = newz; 
	}

	// ______________________________________________________________________ MATH

	/**
	* Modulo
	*/

	public function get modulo() : Number
	{
		return Math.sqrt( this.x*this.x + this.y*this.y + this.z*this.z );
	}

	/**
	* Add
	*/
	public static function add( v:Number3D, w:Number3D ):Number3D
	{
		return new Number3D
		(
			v.x + w.x,
			v.y + w.y,
			v.z + w.z
		);
	}

	/**
	 * Subtract.
	 */
	public static function sub( v:Number3D, w:Number3D ):Number3D
	{
		return new Number3D
		(
			v.x - w.x,
			v.y - w.y,
			v.z - w.z
		);
	}

	/**
	 * Dot product.
	 */
	public static function dot( v:Number3D, w:Number3D ):Number
	{
		return ( v.x * w.x + v.y * w.y + w.z * v.z );
	}

	/**
	 * Cross product. Now optionally takes a target Number3D to put the change into. So we're not constantly making new number3Ds. 
	 * Maybe make a crossEq function? 
	 */
	public static function cross( v:Number3D, w:Number3D, targetN:Number3D = null ):Number3D
	{
		if(!targetN) targetN = ZERO; 
		 
		targetN.reset((w.y * v.z) - (w.z * v.y), (w.z * v.x) - (w.x * v.z), (w.x * v.y) - (w.y * v.x));
		return targetN; 
	}



	/**
	 * Normalize.
	 */
	public function normalize():void
	{
		var mod:Number = Math.sqrt( this.x*this.x + this.y*this.y + this.z*this.z );

		if( mod != 0 && mod != 1)
		{
			mod = 1 / mod; // mults are cheaper then divs
			this.x *= mod;
			this.y *= mod;
			this.z *= mod;
		}
	}
	/**
	 * Multiplies the vector by a number. The same as the *= operator
	 */
	public function multiplyEq(n:Number):void
	{
		x*=n; 
		y*=n;
		z*=n; 	
	}
	
	/**
	 * Adds the vector passed to this vector. The same as the += operator. 
	 */
	public function plusEq(v:Number3D):void
	{
		x+=v.x; 
		y+=v.y; 
		z+=v.z; 	
	}
	
	/**
	 * Subtracts the vector passed to this vector. The same as the -= operator. 
	 */
	
	public function minusEq(v:Number3D):void
	{
		x -= v.x; 
		y -= v.y; 
		z -= v.z; 	
		
	}

	// ______________________________________________________________________
	
	/**
	 * Super fast modulo(length, magnitude) comparisons.
	 * 
	 *  
	 */
	public function isModuloLessThan(v:Number):Boolean
	{
			
		return (moduloSquared<(v*v)); 
		
	}
	
	public function isModuloGreaterThan(v:Number):Boolean
	{
			
		return (moduloSquared>(v*v)); 
		
	}
	public function isModuloEqualTo(v:Number):Boolean
	{
			
		return (moduloSquared==(v*v)); 
		
	}
		
	public function get moduloSquared():Number
	{
		return ( this.x*this.x + this.y*this.y + this.z*this.z );
	}
	
	
	// ______________________________________________________________________



	/**
	* Returns a Number3D object with x, y and z properties set to zero.
	*
	* @return A Number3D object.
	*/
	static public function get ZERO():Number3D
	{
		return new Number3D( 0, 0, 0 );
	}


	/**
	* Returns a string value representing the three-dimensional values in the specified Number3D object.
	*
	* @return	A string.
	*/
	public function toString(): String
	{
		return 'x:' + Math.round(x*100)/100 + ' y:' + Math.round(y*100)/100 + ' z:' + Math.round(z*100)/100;		
	
	}
	
	//------- TRIG FUNCTIONS
	
	/**
	 * 
	 * 
	 * 
	 */
	
	public function rotateX(angle:Number) :void
	{
		if(Papervision3D.useDEGREES) angle*= toRADIANS; 
		
		var cosRY:Number = Math.cos(angle);
		var sinRY:Number = Math.sin(angle);

		temp.copyFrom(this); 

		this.y = (temp.y*cosRY)-(temp.z*sinRY);
		this.z = (temp.y*sinRY)+(temp.z*cosRY);
		
	}	

	public function rotateY(angle:Number) :void
	{
		
		if(Papervision3D.useDEGREES) angle*= toRADIANS; 
		
		var cosRY:Number = Math.cos(angle);
		var sinRY:Number = Math.sin(angle);

		temp.copyFrom(this); 
		
		this.x= (temp.x*cosRY)+(temp.z*sinRY);
		this.z= (temp.x*-sinRY)+(temp.z*cosRY);
		
		
	}	
	public function rotateZ(angle:Number) :void
	{
		
		if(Papervision3D.useDEGREES) angle*= toRADIANS; 

		var cosRY:Number = Math.cos(angle);
		var sinRY:Number = Math.sin(angle);

		temp.copyFrom(this); 		
		//this.x= temp.x;
		this.x= (temp.x*cosRY)-(temp.y*sinRY);
		this.y= (temp.x*sinRY)+(temp.y*cosRY);
		
		
	}
}
}