/**
 * Fx.Path - Pathing for Mootools
 *
 * Dependencies: MooTools 1.2 [Core]
 *
 * @version			0.5 (20081024)
 *
 * @license			MIT-style license
 * @author			Ivanicus <ivannpaz [at] gmail.com>
 * @copyright		Author???
 * @documentation	{coming soon}
 *
 * Bezier calculation and basic ideas:
 *
 * > Jonas Raoni Soares Silva (http://jsfromhell.com/math/bezier [rev. #1])
 * > Dan Pupius (http://www.pupius.net/)
 *
 * Big thanks for your help:
 *
 * > Guillermo Rauch: http://devthought.com/
 * > Nathan White: http://www.nwhite.net/
 * > Tom Occhino: http://tomocchino.com/
 */


/*
Class: Fx.BasePath
	Blueprint for Pathing Classes

Notes:
	Not meant to be used on its own
	It doesnt do anything right now, may be removed later.
*/
Fx.BasePath = new Class({

		Implements: Options,

		options: {},

		initialize: function(options){
			this.setOptions(options);
		}

});

/*
Class: Fx.Path
	Describes a Poly-Path (Path with multiple segments)

Arguments:
	segments 	- Array of Segment Classes (see below)
	options 	- Options for the class (see below)

*/
Fx.Path = new Class({

	Implements: Options,

	Extends: Fx.BasePath,

	options: {
		axis: 'top',  		// [bottom|top]
		origin: {x:0, y:0}	// added to calculations [to use relative values]
	},

	initialize: function(segments, options)
	{
		var params = Array.link(arguments, {segments:Array.type, options:Object.type});
		this.parent(params.options || {});
		this._segments = params.segments || [];
		this._blocksize	= segments ? (1 / this._segments.length) : 0;
		this.setOrigin(this.options.origin);

	},

	//adds a segment at the end of this collection
	addSegment: function(segment)
	{
		segment.origin = this.options.origin;
		this._segments.push(segment);
		this._blocksize	= (1 / this._segments.length);
	},

	//sets the origin of the whole bezier object
	setOrigin: function(origin)
	{
		//store locally
		this.setOptions({origin:origin});

		//replicate to children
		this._segments.each(function(el){
			el.offset = origin;
		});
	},

	//p[1 -> 0] ret[x]
	x: function(p)
	{
		//find the segment where this 'time' falls
		var seg = this.getSegment(p);

		var mapped = (((p % this._blocksize) * 100) / this._blocksize) / 100;

		return seg.x(mapped);
	},

	//p[1 -> 0] ret[y]
	y: function(p)
	{
		//find the segment where this 'time' falls
		var seg = this.getSegment(p);

		var mapped = (((p % this._blocksize) * 100) / this._blocksize) / 100;

		return seg.y(mapped);
	},

	//p[0 -> 1] ret[{x,y}]
	getCoordinates: function(p)
	{
		//find the segment where this 'time' falls
		var seg = this.getSegment(p);

		var mapped = (((p % this._blocksize) * 100) / this._blocksize) / 100;

		return seg.getCoordinates(mapped);
	},

	//p[0 -> 1] r[0-#elements]
	getSegment: function(p)
	{
		var n = this._segments.length;

		var index = (n - Math.floor(p * n)) - 1 ;

		return this._segments[index];
	},

	//plot poly-path
	plot: function(cb)
	{
		this._segments.each(function(el){
			el.plot(cb);
		}, this);
	}
});

/*
Class: CubicBezier
	Describes a Cubic Bezier Curve.

Arguments:
	p0, p1: Start/End Vertex Points
	c0, c1: Start/End Control Points

*/
var CubicBezier = new Class({

	initialize: function(p0, p1, c0, c1)
	{
		this.x0 	= p0[0]; // A x1
		this.y0 	= p0[1];
		this.x1 	= p1[0]; // D x4
		this.y1 	= p1[1];
		this.cx0 	= c0[0]; // B x2
		this.cy0 	= c0[1];
		this.cx1 	= c1[0]; // C x3
		this.cy1 	= c1[1];

		this.offset = {x:0, y:0};

		this.f1 = function(t) { return (t*t*t); }
		this.f2 = function(t) { return (3*t*t*(1-t)); }
		this.f3 = function(t) { return (3*t*(1-t)*(1-t)); }
		this.f4 = function(t) { return ((1-t)*(1-t)*(1-t)); }

	},

	//p[1 -> 0] ret[x]
	x: function(p)
	{
		return this.x0 * this.f1(p) + this.cx0 * this.f2(p) + this.cx1 * this.f3(p) + this.x1 * this.f4(p);
	},

	//p[1 -> 0] ret[y]
	y: function(p)
	{
		return this.y0 * this.f1(p) + this.cy0 * this.f2(p) + this.cy1 * this.f3(p) + this.y1 * this.f4(p);
	},

	//p[0 -> 1] ret[{x,y}]
	getCoordinates: function(p)
	{
		return { x: this.x(p) + this.offset.x, y: this.y(p) + this.offset.y };
	},

	//returns the controls points of this curve
	getControls: function()
	{
		return {c0: {x:this.cx0, y:this.cy0}, c1: {x:this.cx1, y:this.cy1}};
	},

	//returns the start/end points of this curve REAL ABSOLUTE VALUES
	getStartEnd: function()
	{
		return {p0: {x:this.x0, y:this.y0}, p1: {x:this.x1, y:this.y1}};
	},

	//utility to plot the path for debugging purposes
	plot: function(cb)
	{
		for(var i = 0; i < 1000; i++) cb(this.getCoordinates(i/1000), i);
	}

});

/*
Class: QuadBezier
	Describes a Quadratic Bezier Curve.

Arguments:
	p0, p1: Start/End Vertex Points
	c: 		Control Point

*/
var QuadBezier = new Class({

	Extends: CubicBezier,

	initialize: function(p0, p1, c)
	{
		this.x0 	= p0[0]; // A x1
		this.y0 	= p0[1];
		this.x1 	= p1[0]; // D x4
		this.y1 	= p1[1];
		this.cx0 	= c[0]; // B x2
		this.cy0 	= c[1];
		this.cx1 	= c[0]; // C x3
		this.cy1 	= c[1];

		this.origin = {x:0, y:0};

		this.f1 = function(t) { return (t*t*t); }
		this.f2 = function(t) { return (3*t*t*(1-t)); }
		this.f3 = function(t) { return (3*t*(1-t)*(1-t)); }
		this.f4 = function(t) { return ((1-t)*(1-t)*(1-t)); }
	}

});
