
function ActionManager() {
	var STATE_STOPPED = 1;
	var STATE_PAUSED = 2;
	var STATE_RUNNING = 3;

	this.actions = [];
	this.state = STATE_STOPPED;
}

ActionManager.prototype = {
		// Clears and stops all actions.
		clear: function() {
			for ( var i = 0; i < this.actions.length; i++ )
				this.actions[i].stop();
			this.actions = [];
			this.state = 1;
		},
		
		addAction: function(action) {
			var count = this.actionCount()+1;
			if ( action.async == false ) {
				if ( action.depends == undefined && count > 1 ) 
					action.depends = [count-2];
			} else {// else: asynchroon, mag gewoon gestart worden.
				action.async = true;
				action.depends = [];
			}
			this.actions.push(new ActionManagerAction(action));
		},
		
		actionCount: function() {
			return this.actions.length;
		},
		
		
		start: function() {
			//this.state = STATE_RUNNING;
			for ( var i = 0; i < this.actions.length; i++ ) {
				var depends = this.actions[i].settings.depends;
				for ( var x = 0; x < depends.length; x++ ) {
					this.actions[depends[x]].addListener(this.actions[i]);
					//alert('Add Listener to ' + this.actions[x].settings.action + ' listener: ' + this.actions[i].settings.action + "\n So: when the first is done, the second will be notified.");
				}
			}
			
			for ( var i = 0; i < this.actions.length; i++ ) {
				//alert('Start action: ' + this.actions[i].settings.action);
				this.actions[i].start();
			}
		}
				
};


/**
 * A special action manager action
 * @param action
 * @return
 */
function ActionManagerAction(action) {
	this.settings = action;
	this.listeners = [];
	// Current state. 0 = not started, 1 = waiting, 2 = busy, 3 = done, 4 = error, 5 = stopped
	this.state = 0;
	this.pending = this.settings.depends.length;
	
	this.error = {code: null, description: null};
}

ActionManagerAction.prototype = {
		// Start this action. This method waits for 
		start: function() {
			this.sendMessage('ACTION_START', 'Action start() method is called, action is going to start when its dependancies are done.');
			this.state = 1;
			//alert('start! action: '+this.settings.action+', listeners on: ' + this.listeners.length + '; pending: ' + this.pending);
			if (this.pending <= 0)
				this.execute();
		},
		
		stop: function() {
			if ( this.state == 1 || this.state == 2 ) {
				this.sendMessage('ACTION_STOP', 'Action has to stop. Stop event was sent.');
				this.state = 5;
			} else {
				// can not stop already stopped.
			}
		},
		
		sendDone: function() {
			this.sendMessage('ACTION_DONE', 'Method execute() was done executing.');
			this.state = 3;
		},
		
		sendError: function(error) {
			this.sendMessage('ACTION_ERROR', 'There occurred an error: ' + error);
			this.state = 4;
		},
		
		execute: function() {
			if ( this.state != 2 ) {
				this.state = 2;
				this.sendMessage('ACTION_EXECUTE', 'Action execute() method is called, action is going to start.');
				// Dispatch the call.
				var method = this.settings.action;
				try {
					this.actions[method].apply(this, [this.settings]);
					return true;
				} catch ( e ) {
					throw 'Illegal method name: ' + method;
					return false;
				}
			} else {
				// invalid state..
				return false;
			}
		},
		
		actions: {
			setTitle: function(action) {
				document.title = action.title;
				this.sendDone();
			},
			
			scroll: function(action) {
				$.scrollTo(action.to, action.duration, {});
				var x = this;
				var done = function() {x.sendDone();}
				window.setTimeout(done, action.duration);
			},
			
			jscall: function(action) {
				window.setTimeout(action.func, action.timeout);
				// TODO: after acction.timeout
				this.sendDone();
			},
			
			hide : function(action) {
				var x = this;
				$('div.MainHolder').css('min-height', 1500); // ongeveer 1200... TODO: Determine the min-height
				var duration = action.duration;
				var elem = $(action.element);
				elem.animate({
					"height": "toggle", 
					"opacity": "toggle"
				}, duration, function() {
					x.sendDone();
				});
			},
			
			show : function(action) {
				var x = this;
				
				var elem = $(action.element);
				var duration = action.duration;
				elem.animate({
					"height": "toggle", 
					"opacity": "toggle"
				}, duration, function() {
					//$('div.MainHolder').css('min-height', 800);
					x.sendDone();
				});			
			},
			
			preload: function(action) {
				var images = action.images;
				for ( var i = 0; i < images.length; i++ ) {
					var img = new Image();
					img.src = images[i];
					img.style.display = "none";
					img.className = "PreloadedImage";
					$(document.body).append(img);
				}
				var x = this;
				var done = function() { $('body img.PreloadedImage').remove(); x.sendDone();}
				window.setTimeout(done, (images.length * 200));
			},
			
			replace: function(action) {
				var elem = $(action.element);
				elem.empty();
				elem.html(action.content);
				this.sendDone();
			},
			
			remove: function(action) {
				var elem = $(action.element);
				elem.remove();
				this.sendDone();
			},
			
			insert: function(action) {
				var elem = $(action.element);
				if ( action.before ) {
					elem.find(action.before).before(action.content);
				} else {
					elem.append(action.content);
				}
				this.sendDone();
			}
		},
		
		// Add a listener
		addListener: function(listenerObject) {
			for ( var i = 0; i < this.listeners.length; i++ ) {
				if ( this.listeners[i] == listenerObject )
					return false; // object was already in list
			}
			this.listeners.push(listenerObject);
		},
		
		// Is called whenever an observer sends an message to its listeners
		update: function(sender, code, message) {
			if ( code == 'ACTION_DONE' ) {
				//if ( this.settings.action == 'scroll')
				//	alert('pending -- (from: '+sender.settings.action+')');
				this.pending--;
				if ( this.pending <= 0 && this.state == 1 )
					this.execute();
			}
		},
		
		// Send a specific message to all listeners
		sendMessage: function(code, message) {
			for ( var i = 0; i < this.listeners.length; i++ ) {
				this.listeners[i].update(this, code, message);
			}
		},
		
		getState: function() {
			return this.state;
		},

		// Is called whenever an error occurs
		error: function(code, description) {
			this.state = 4; // error
			this.error.code = code;
			this.error.description = description;
		},
		
		getError: function() {
			if ( this.error.code != null )
				return this.error;
			else
				return false; // no error
		}
};