/*!
 * Slide Joe JS
 * ------------
 * Copyright 2011, Gerrit Balsam / Reygers SYSTEMHAUS
 * 
 * Web: http://www.reygers.de
 * 
 * Dual licensed under the MIT or GPL Version 2 licenses.
 */
var SlideJoe = {
	project: "Slide Joe (C) 2011, Gerrit Balsam / Reygers SYSTEMHAUS",
	version: "0.1.0"
};

/**
 * Definition of the SlideJoe Container.
 */
SlideJoe.Container = function(options) {
		
	this.options = options;
	this.id = options.id;
	this.activeFrame = options.start.frame - 1;
	this.frames;
	this.isRunning = false;
	
	// Initialize our slide-show container. For each
	// element on level 1 we will create a frame instance.
	this.init = function() {
		var elements = new Array();
		var container = this;
		
		if (this.options.frames != null) {
			$.each(this.options.frames, function(key, value) {
				elements.push(new SlideJoe.Frame(container, container, value));
			});
		}
		
		if (this.options.css != null) {
			var element = $(this.options.id);
			
			$.each(this.options.css, function(key, value) {
				element.css(key, value);
			});
		}
		
		this.frames = elements;
		
		// set default css
		$(this.id).css("position", "relative");
	};
	
	this.start = function() {
		var frame = this.frames[this.activeFrame];
		
		if (this.options.start.fxIn != null) {
			if (this.options.start.delay != null) {
				frame.delay(this.options.start.delay);
			}
			this.options.start.fxIn.method = "show";
			frame.performEffect(this.options.start.fxIn)
		}
		else {
			frame.show();
		}
		
		frame.render();
		this.isRunning = true;
	};
	
	this.stop = function() {
		this.isRunning = false;
	}
	
	this.slideTo = function(frameIndex, speed) {
		var activeFrame = this.frames[this.activeFrame];
		var nextFrame = this.frames[frameIndex-1];
		var step = (this.activeFrame < (frameIndex) ? 1 : -1);
		var dirOut = (this.activeFrame < frameIndex ? "left" : "right");
		var dirIn = (this.activeFrame < frameIndex ? "right" : "left");
		var startIndex = parseInt(this.activeFrame)
		var i = startIndex;
		var c = 0;
		
		while (true) {
			var frame = this.frames[i];
			var nextFrame = this.frames[i + step];
			var width = 1 * parseInt($(frame.options.id).width());
			var from = (i == startIndex ? "0px" : (startIndex < frameIndex ? width : -width) + "px");
			var to = (i == frameIndex-1 ? "0px" : (startIndex < frameIndex ? -width : width) + "px");
			
			$(frame.options.id).css("left", from);
			$(frame.options.id).show();
			$(frame.options.id).stop().delay(speed * c).animate({left: to}, speed, "linear");
			
			if (i == frameIndex-1) {
				break;
			}
			
			i = i + step;
			c = c + 1;
		}
		
		this.activeFrame = frameIndex-1;
		this.stop();
	}
	
	// construction	
	this.init(options);
	
	this.nextFrame = null;
};

/**
 * Definition of a single frame within our container.
 * 
 * Once initialized you are able to render the frame using
 * the render() method. When rendering, the frame will be
 * first use its fxIn-Effect if specified. Afterwards it will
 * sleep the given duration and invoke all layer's rendering
 * methods. Finally the frame will perform its fxOut-Effect.
 * 
 * Following options may be passed to the Frame:
 * 	html		specifies the occurrence of the frame	""
 *  nextFrame 	index of the next frame					null (ends cycle)
 *  duration	duration of the main operation while	MAX(duration of each layer)
 *  			the layers are rendered
 *  fxIn		JsonArray for the in-effect				null
 *  fxOut		JsonArray for the out-effect			null
 *  layers		Contains all layer definitions
 *  
 * In/Out effect may be configured as following:
 * 	effect		JQuery UI Effect
 * 	duration	duration for the effect
 * 	option		effect specific options
 */
SlideJoe.Frame = function(parent, container, options) {

	this.options = options;
	this.parent = parent;
	this.container = container;
	this.frames;
	
	// initialize the frame using the given element
	// by parsing each child. Any child found will be
	// wrapped into a new layer
	this.init = function() {
		
		$(this.options.id).hide();
		
		// setup css
		if (parent == container) {
			$(this.options.id).css("position", "absolute");
			$(this.options.id).css("top", "0");
			$(this.options.id).css("left", "0");
		}
	}
	
	this.performEffect = function(option) {
		var element = $(this.options.id);
		var delay = option.delay;
		var effect = option.effect;
		var options = option.options;
		var duration = option.duration;
		var method = option.method;
		
		if (delay != null) {
			element.delay(delay);
		}
		
		
		if (method == "hide") {
			element.hide(effect, options, duration);
		}
		else if (method == "effect") {
			
			element.effect(effect, options, duration);
		}
		else if (method == "show") {
			element.show(effect, options, duration);
		}
		else {
			// custom effects
			throw new Exception("Method '" + method + "' not supported: Must be 'hide', 'show' or 'effect'!");
		}
	}
	
	this.render = function() {
		var totalDuration = 1;
		
		// delay action
		$(this.options.id).delay(this.options.delay);
		totalDuration = totalDuration + this.options.delay;
		
		// create and render sub-frames
		if (this.options.frames != null) {
			var parentFrame = this;
			var container = this.container;
			var myFrames = Array();
			
			$.each(this.options.frames, function (key, value) {
				if (value != null) {
					var frame = new SlideJoe.Frame(parentFrame, container, value);
					frame.render();
					myFrames.push(frame);
				}
			});
			
			this.frames = myFrames;
		}
		
		if (this.options.fx != null) {
			var me = this;
			
			$.each(this.options.fx, function (key, value) {
				if (value != null) {
					me.performEffect(value);
				}
			});
		}
		
		if (this.options.next != null) {
			var delay = this.options.next.delay;
			var nextFrame = this.options.next.frame;
			
			// setTimeout(function(obj) { obj.renderNext(); }, delay, this);
			var obj = this;
			setTimeout(function() { renderNext(obj) }, delay);
		}
	}
	
	this.renderNext = function () {
		if (this.options.next.frame != null && this.container.isRunning) {
			var nextFrame = this.container.frames[this.options.next.frame-1];
			this.container.activeFrame = this.options.next.frame-1;
			
			// invoke callback method of container
			if (this.container.renderNext != null) {
				this.container.renderNext({
					nextFrame: this.options.next.frame
				});
			}
			
			if (this.options.next.fxOut != null) {
				this.options.next.fxOut.method = "hide";
				this.performEffect(this.options.next.fxOut);
			}
			else {
				this.hide();
			}
			
			if (this.options.next.fxIn != null) {
				this.options.next.fxIn.method = "show";
				nextFrame.performEffect(this.options.next.fxIn);
			}
			else {
				this.show();
			}
			
			nextFrame.render();
		}
	}
	
	this.hide = function() {
		$(this.options.id).hide();
	}
	
	this.show = function() {
		$(this.options.id).show();
	}
	
	this.delay = function(duration) {
		$(this.options.id).delay(duration);
	}
	
	this.showAll = function() {
		$(this.options.id).stop();
		$(this.options.id).show();
		
		if (this.frames != null) {
			$.each(this.frames, function (key, value) {
				if (value != null) {
					value.showAll();
				}
			});
		}
	}
	
	this.hideAll = function() {
		$(this.options.id).stop();
		$(this.options.id).hide();
		
		if (this.frames != null) {
			$.each(this.frames, function (key, value) {
				if (value != null) {
					value.hideAll();
				}
			});
		}
	}
	
	// construction
	this.init();
};



/*
 * Now since we have defined all classes we need, we will
 * add JQuery support to our SlideJoe...
 */
$.fn.slideshow = function(options) {
	if (options == null) {
		options = {
			autostart: true
		};
	}
	options.id = "#" + $(this).attr("id");
	if (options.activeFrame == null) options.activeFrame = 1;

	var slideShow = new SlideJoe.Container(options);
	if (options.autostart) {
		slideShow.start();
	}
	
	return slideShow;
};


function renderNext(obj) {
	obj.renderNext();
};
