package classes.away3d.core.geom
{
	import classes.away3d.containers.*;
	
	import flash.geom.*;
		
	/**
	 * Holds information about a single Path definition.
	 */
    public class Path
    {
		/**
    	 * To display/debug the Path instance data
    	 */
		public var _pathDebug:PathDebug;
		
    	/**
    	 * The array that contains the path definition.
    	 */
        public var aSegments:Vector.<PathCommand>;
		
		/**
    	 * The worldAxis of reference
    	 */
		public var worldAxis:Vector3D = new Vector3D(0,1,0);
    	
		
        private var _smoothed:Boolean;
		/**
    	 * returns true if the smoothPath handler is being used.
    	 */
		public function get smoothed():Boolean
		{
			return _smoothed;
		}
		
		private var _averaged:Boolean;
		/**
    	* returns true if the averagePath handler is being used.
    	*/
		public function get averaged():Boolean
		{
			return _averaged;
		}
		
		/**
		 * display the path in scene
		 */
		public function debugPath(scene:Scene3D):void
        {
			_pathDebug = new PathDebug(scene, this);
        }
		/**
		 * Defines if the anchors must be displayed if debugPath has been called. if false, only curves are displayed
		 */
		public function get showAnchors():Boolean
        {
			if(!_pathDebug)
				throw new Error("Patheditor not set yet! Use Path.debugPath() method first");
				
			return _pathDebug.showAnchors;
		}
		public function set showAnchors(b:Boolean):void
        {
			if(!_pathDebug)
				throw new Error("Patheditor not set yet! Use Path.debugPath() method first");
			
			_pathDebug.showAnchors = b;
        }
		/**
		 * Defines if the path data must be visible or not if debugPath has been called
		 */
		public function get display():Boolean
        {
			return _pathDebug.display;
		}
		public function set display(b:Boolean):void
        {
			if(!_pathDebug)
				throw new Error("Patheditor not set yet! Use Path.debugPath() method first");
			
			_pathDebug.display = b;
        }
		
		 
		/**
		 * adds a PathCommand to the path
		 * @see PathCommand:
		 */
		public function add(cs:PathCommand):void
        {
			this.aSegments.push(cs);
        }
		
		/**
		 * returns the length of the Path elements array
		 * 
		 * @return	an integer: the length of the Path elements array
		 */
		public function get length():int
        {
			return this.aSegments.length;
        }
		
		/**
		 * returns the Path elements array
		 * 
		 * @return	an Array: the Path elements array
		 */
		public function get array():Vector.<PathCommand>
        {
			return this.aSegments;
        }
        
		/**
		 * Creates a new <code>Path</code> object.
		 * 
		 * @param	 aVectors		[optional] An array of a series of Vector3D's organized in the following fashion. [a,b,c,a,b,c etc...] a = pEnd, b=pControl (control point), c = v2
		 */
		 
        public function Path(aVectors:Array = null)
        {
			if(aVectors!= null && aVectors.length < 3)
				throw new Error("Path array must contain at least 3 Vector3D's");
			
            this.aSegments = new Vector.<PathCommand>();
			
			if(aVectors != null)
				for(var i:int = 0; i<aVectors.length; i+=3)
					this.aSegments.push( new PathCommand(PathCommand.CURVE, aVectors[i], aVectors[i+1], aVectors[i+2]));
			 
        }
		
		/**
		 * removes a segment in the path according to id.
		 *
		 * @param	 index	int. The index in path of the to be removed curvesegment 
		 * @param	 join 		Boolean. If true previous and next segments coordinates are reconnected
		 */
		public function removeSegment(index:int, join:Boolean = false):void
        {
			if(this.aSegments.length == 0 || this.aSegments[index ] == null )
				return;
			
			if(join && index < this.aSegments.length-1 && index>0){
				var seg:PathCommand = this.aSegments[index];
				var prevSeg:PathCommand = this.aSegments[index-1];
				var nextSeg:PathCommand = this.aSegments[index+1];
				prevSeg.pControl.x = (prevSeg.pControl.x+seg.pControl.x)*.5;
				prevSeg.pControl.y = (prevSeg.pControl.y+seg.pControl.y)*.5;
				prevSeg.pControl.z = (prevSeg.pControl.z+seg.pControl.z)*.5;
				nextSeg.pControl.x = (nextSeg.pControl.x+seg.pControl.x)*.5;
				nextSeg.pControl.y = (nextSeg.pControl.y+seg.pControl.y)*.5;
				nextSeg.pControl.z = (nextSeg.pControl.z+seg.pControl.z)*.5;
				prevSeg.pEnd.x = (seg.pStart.x + seg.pEnd.x)*.5;
				prevSeg.pEnd.y = (seg.pStart.y + seg.pEnd.y)*.5;
				prevSeg.pEnd.z = (seg.pStart.z + seg.pEnd.z)*.5;
				nextSeg.pStart.x = prevSeg.pEnd.x;
				nextSeg.pStart.y = prevSeg.pEnd.y;
				nextSeg.pStart.z = prevSeg.pEnd.z;
				
				if(_pathDebug != null)
					_pathDebug.updateAnchorAt(index-1);
					_pathDebug.updateAnchorAt(index+1);
			}
			
			if(this.aSegments.length > 1){
				this.aSegments.splice(index, 1);
			} else{
				this.aSegments = new Vector.<PathCommand>();
			}
        }
		
		/**
		 * handler will smooth the path using anchors as control vector of the PathCommands 
		 * note that this is not dynamic, the PathCommands values are overwrited
		 */
		public function smoothPath():void
        {
			if(this.aSegments.length <= 2)
				return;
			 
			_smoothed = true;
			_averaged = false;
			 
			var x:Number;
			var y:Number;
			var z:Number;
			var seg0:Vector3D;
			var seg1:Vector3D;
			var tmp:Array = [];
			var i:int;
			
			var startseg:Vector3D = new Vector3D(this.aSegments[0].pStart.x, this.aSegments[0].pStart.y, this.aSegments[0].pStart.z);
			var endseg:Vector3D = new Vector3D(this.aSegments[this.aSegments.length-1].pEnd.x, 
																		this.aSegments[this.aSegments.length-1].pEnd.y,
																		this.aSegments[this.aSegments.length-1].pEnd.z);
			for(i = 0; i< length-1; ++i)
			{
				if(this.aSegments[i].pControl == null)
					this.aSegments[i].pControl = this.aSegments[i].pEnd;
				
				if(this.aSegments[i+1].pControl == null)
					this.aSegments[i+1].pControl = this.aSegments[i+1].pEnd;
				
				seg0 = this.aSegments[i].pControl;
				seg1 = this.aSegments[i+1].pControl;
				x = (seg0.x + seg1.x) * .5;
				y = (seg0.y + seg1.y) * .5;
				z = (seg0.z + seg1.z) * .5;
				
				tmp.push( startseg,  new Vector3D(seg0.x, seg0.y, seg0.z), new Vector3D(x, y, z));
				startseg = new Vector3D(x, y, z);
				this.aSegments[i] = null;
			}
			
			seg0 = this.aSegments[this.aSegments.length-1].pControl;
			tmp.push( startseg,  new Vector3D((seg0.x+seg1.x)*.5, (seg0.y+seg1.y)*.5, (seg0.z+seg1.z)*.5), endseg);
			
			this.aSegments = new Vector.<PathCommand>();
			
			for(i = 0; i<tmp.length; i+=3)
				this.aSegments.push( new PathCommand(PathCommand.CURVE, tmp[i], tmp[i+1], tmp[i+2]) );
				tmp[i] = tmp[i+1] = tmp[i+2] = null;
			 
			tmp = null;
		}
		
		/**
		 * handler will average the path using averages of the PathCommands
		 * note that this is not dynamic, the path values are overwrited
		 */
		
		public function averagePath():void
        {
			_averaged = true;
			_smoothed = false;
			
			for(var i:int = 0; i<this.aSegments.length; ++i){
				this.aSegments[i].pControl.x = (this.aSegments[i].pStart.x+this.aSegments[i].pEnd.x)*.5;
				this.aSegments[i].pControl.y = (this.aSegments[i].pStart.y+this.aSegments[i].pEnd.y)*.5;
				this.aSegments[i].pControl.z = (this.aSegments[i].pStart.z+this.aSegments[i].pEnd.z)*.5;
			}
        }
        
  		public function continuousCurve(points:Array, closed:Boolean = false):void
  		{
  			var aVectors:Array = [];
  			var i:int;
  			var X:Number;
			var Y:Number;
			var Z:Number;
			var midPoint:Vector3D;
			
  			// Find the mid points and inject them into the array.
  			for(i = 0; i < points.length - 1; i++)
  			{
  				var currentPoint:Vector3D = points[i];
  				var nextPoint:Vector3D = points[i+1];
  				
  				X = (currentPoint.x + nextPoint.x)/2;
  				Y = (currentPoint.y + nextPoint.y)/2;
  				Z = (currentPoint.z + nextPoint.z)/2;
  				midPoint = new Vector3D(X, Y, Z);
  				
  				if (i) {
  					aVectors.push(midPoint);
  				}
  				
  				if (i < points.length - 2 || closed) {
	  				aVectors.push(midPoint);
	  				aVectors.push(nextPoint);
  				}
  			}
  			
  			if(closed) {
	  			currentPoint = points[points.length-1];
	  			nextPoint = points[0];
	  			X = (currentPoint.x + nextPoint.x)/2;
  				Y = (currentPoint.y + nextPoint.y)/2;
  				Z = (currentPoint.z + nextPoint.z)/2;
  				midPoint = new Vector3D(X, Y, Z);
  				
  				aVectors.push(midPoint);
  				aVectors.push(midPoint);
  				aVectors.push(points[0]);
  				aVectors.push(aVectors[0]);
	  		}
	  		
	  		
            this.aSegments = new Vector.<PathCommand>();
			
			for(i = 0; i< aVectors.length; i+=3)
				this.aSegments.push( new PathCommand(PathCommand.CURVE, aVectors[i], aVectors[i+1], aVectors[i+2]));
  		}
    }
}
