package net.flashpunk.tweens.motion 
{
	import flash.geom.Point;

	import net.flashpunk.FP;

	/**
	 * Determines motion along a quadratic curve.
	 */
	public class QuadMotion extends Motion
	{
		/**
		 * Constructor.
		 * @param	complete	Optional completion callback.
		 * @param	type		Tween type.
		 */
		public function QuadMotion(complete:Function = null, type:uint = 0)
		{
			super(0, complete, type, null);
		}
		
		/**
		 * Starts moving along the curve.
		 * @param	fromX		X start.
		 * @param	fromY		Y start.
		 * @param	controlX	X control, used to determine the curve.
		 * @param	controlY	Y control, used to determine the curve.
		 * @param	toX			X finish.
		 * @param	toY			Y finish.
		 * @param	duration	Duration of the movement.
		 * @param	ease		Optional easer function.
		 */
		public function setMotion(fromX:Number, fromY:Number, controlX:Number, controlY:Number, toX:Number, toY:Number, duration:Number, ease:Function = null):void
		{
			_distance = -1;
			x = _fromX = fromX;
			y = _fromY = fromY;
			_controlX = controlX;
			_controlY = controlY;
			_toX = toX;
			_toY = toY;
			_target = duration;
			_ease = ease;
			start();
		}
		
		/**
		 * Starts moving along the curve at the speed.
		 * @param	fromX		X start.
		 * @param	fromY		Y start.
		 * @param	controlX	X control, used to determine the curve.
		 * @param	controlY	Y control, used to determine the curve.
		 * @param	toX			X finish.
		 * @param	toY			Y finish.
		 * @param	speed		Speed of the movement.
		 * @param	ease		Optional easer function.
		 */
		public function setMotionSpeed(fromX:Number, fromY:Number, controlX:Number, controlY:Number, toX:Number, toY:Number, speed:Number, ease:Function = null):void
		{
			_distance = -1;
			x = _fromX = fromX;
			y = _fromY = fromY;
			_controlX = controlX;
			_controlY = controlY;
			_toX = toX;
			_toY = toY;
			_target = distance / speed;
			_ease = ease;
			start();
		}
		
		/** @private Updates the Tween. */
		override public function update():void 
		{
			super.update();
			if (delay > 0) return;
			x = _fromX * (1 - _t) * (1 - _t) + _controlX * 2 * (1 - _t) * _t + _toX * _t * _t;
			y = _fromY * (1 - _t) * (1 - _t) + _controlY * 2 * (1 - _t) * _t + _toY * _t * _t;
		}
		
		/**
		 * The distance of the entire curve.
		 */
		public function get distance():Number
		{
			if (_distance >= 0) return _distance;
			var a:Point = FP.point,
				b:Point = FP.point2;
			a.x = x - 2 * _controlX + _toX;
			a.y = y - 2 * _controlY + _toY;
			b.x = 2 * _controlX - 2 * x;
			b.y = 2 * _controlY - 2 * y;
			var A:Number = 4 * (a.x * a.x + a.y * a.y),
				B:Number = 4 * (a.x * b.x + a.y * b.y),
				C:Number = b.x * b.x + b.y * b.y,
				ABC:Number = 2 * Math.sqrt(A + B + C),
				A2:Number = Math.sqrt(A),
				A32:Number = 2 * A * A2,
				C2:Number = 2 * Math.sqrt(C),
				BA:Number = B / A2;
			return (A32 * ABC + A2 * B * (ABC - C2) + (4 * C * A - B * B) * Math.log((2 * A2 + BA + ABC) / (BA + C2))) / (4 * A32);
		}
		
		// Curve information.
		/** @private */ private var _distance:Number = -1;
		/** @private */ private var _fromX:Number = 0;
		/** @private */ private var _fromY:Number = 0;
		/** @private */ private var _toX:Number = 0;
		/** @private */ private var _toY:Number = 0;
		/** @private */ private var _controlX:Number = 0;
		/** @private */ private var _controlY:Number = 0;
	}
}