/*!
*	MooAnimator v1.0
*	A Mootools FX Animator. Runs specified effects, and once complete runs the onComplete function.
*	by Toni Rantanen 2009-10-30 www.kotimaanetti.fi
*/

/*
todo:
- animation id ?
- callstack (opt: chain/ignore) ?
	- prevent anim call crashes, duplicates...
	- step counting ?
	- flow/animation controller (only one animation at a time?)
		- flow must be stoppable, closes all animations (stops to next possible "flow frame")
*/

function MooAnimatorEvent(bind, callback, args)
{
	this.bind = bind;
	this.callback = callback;
	this.args = args;
	
	this.func = function() {
		this.callback.apply(this.bind, this.args);
		return false;
	}.bind(this);
}

var MooAnimator = new Class(
{
	running: false,
	onComplete: null,
	list: {},
	
	/**
	*	onComplete:	(function) function to run once complete
	*	args:		(Array) arguments for onComplete
	*	bind:		(Object) context for onComplete
	*/
	initialize: function(onComplete)
	{
		this.onComplete = typeof(onComplete) == 'undefined' ? null : onComplete;
		this.list = {count: 0, current: 0, anims: {}};
	},
	
	/**
	*	anim:	(String) name of animation
	*	step:	(String) name of animation step
	*	item:	(Function, Fx, Tween or Morph)
	*	args:	(Array) arguments for fx.start()
	*/
	add: function(anim, step, item, args)
	{
		if(typeof(args) == 'undefined')
			args = [];
		
		// add animation
		if(typeof(this.list.anims[anim]) == 'undefined')
		{
			this.list.anims[anim] = {count: 0, current: 0, steps: {}};
			this.list['count']++; // anim count
		}
		
		// step count
		if(typeof(this.list.anims[anim].steps[step]) == 'undefined')
			this.list.anims[anim]['count']++;
		
		// add step item
		this.list.anims[anim].steps[step] = {item: item, args: args, events: {}};
	},
	
	// get current anim & advance
	_getAnim: function()
	{
		var i = 0;
		for(var anim in this.list.anims)
		{
			if(i >= this.list.current)
				return anim;
			i++;
		}
		return null;
	},
	
	_runNextAnim: function()
	{
		// select anim
		var anim = this._getAnim();
		if(anim === null)
			return; // failed...
		
		// run steps
		for(var step in this.list.anims[anim].steps)
		{
			// get item
			var obj = this.list.anims[anim].steps[step];
			
			// get args
			if(typeof(obj['args']) == 'function')
				obj['args'] = obj['args']();
			
			// run fx/function
			switch(typeof(obj['item']))
			{
				case 'function':
					// call & manual complete
					obj['item'].apply(
						obj['item'],
						obj['args']
					);
					this._stepComplete(anim, step);
				break;
				case 'object':
					// event id
					var eventId = anim +'_'+ step +'_'+ (new Date()).getTime();
					// add event
					obj['events'][eventId] = new MooAnimatorEvent(
						this, this._stepComplete, [anim, step, eventId]);
					obj['item'].addEvent('complete', obj['events'][eventId].func);
					// call
					obj['item'].start.apply(
						obj['item'],
						obj['args']
					);
				break;
			}
		}
	},
	
	_stepComplete: function(anim, step, eventId)
	{
		// remove event
		if(typeof(eventId) != 'undefined')
		{
			var obj = this.list.anims[anim].steps[step];
			if(typeof(obj['item']) == 'object')
			{
				obj['item'].removeEvent('complete', obj['events'][eventId]['func']);
				if(typeof(obj['events'][eventId]) == 'undefined')
				{
					return; // failed...
				}
				obj['item'].removeEvent('complete', obj['events'][eventId]);
				delete(obj['events'][eventId]);
			}
		}
		
		// this anim complete?
		this.list.anims[anim].current++;
		if(this.list.anims[anim].current >= this.list.anims[anim]['count'])
		{
			// all anims complete?
			this.list.current++;
			if(this.list.current >= this.list['count'])
			{
				// animation finished!
				if(this.onComplete !== null)
					this.onComplete();
				//this.reset();
				this.running = false;
				return;
			}
			// continue to next anim
			this._runNextAnim();
			return;
		}
	},
	
	reset: function()
	{
		for(var anim in this.list.anims)
		{
			this.list.anims[anim].current = 0;
			for(var step in this.list.anims[anim].steps)
				this.list.anims[anim].steps[step].events = {};
		}
		this.list.current = 0;
	},
	
	start: function()
	{
		if(!this.running)
		{
			this.reset();
			this.running = true;
			this._runNextAnim();
		}
	}
});
