/**
 * SlideShow - Creates a slideshow of images & content with automatic or manual rotation
 * @param	object	options		optional;Key/value pair object to set slideshow options.
 *
 * options:
 * 	imgClass		The classname used to detect images that are part of the slideshow
 * 	contentClass	The classname used to detect content items are part of the slideshow
 * 	loop			Whether the slideshow should loop
 * 	direction		Direction of the traversal of slides. (forward or reverse)
 * 	delay			Default delay (in milliseconds) between changing slides (used when no per-image delay set)
 * 	speed			Speed of the transition animation (in milliseconds)
 * 	autoStart		Whether the slideshow should start as soon as it's loaded
 */
function SlideShow(options) {
	/* Interal use items */
	this.images = [];
	this.content = [];
	this.delays = [];
	this.current = 0;
	this.last = 0;
	this.timers = [];
	this.layers = [];
	this.rotating = false;
	this.rotationTimer = null;
	this.isIE = false;
	
	/* Settings */
	this.imgClass = 'ss_photo';
	this.contentClass = 'ss_content';
	this.loop = true;
	this.direction = SlideShow.FORWARD;
	this.delay = 10000; //milliseconds
	this.speed = 1000; //milliseconds
	this.autoStart = false;
	
	/* Event callbacks */
	this.events = {
		'next': [],
		'previous': [],
		'goto': [],
		'start': [],
		'stop': [],
		'transition': []
	};
	
	for(var k in options) {
		if(options.hasOwnProperty(k) && (k in this) && this[k] !== undefined) {
			this[k] = options[k];
		}
	}
	
	domLoader.register(function(){
		this.isIE = window.ActiveXObject ? (window.XMLHttpRequest ? 7 : 6) : false;
		var images = slice(dbc(this.imgClass));
		var content_items = slice(dbc(this.contentClass));
		images.forEach(function(img,i, imgs){
			this.images.push(img);
			this.layers.unshift(i);
		},this);
		
		content_items.forEach(function(item,i){
			item.style.zIndex = i;
			this.content.push(item);
		},this);
		if(this.images[0]) {
			this.setOpacity(this.images[0],100);
		}
		if(this.content[0]) {
			this.setOpacity(this.content[0],100);
		}
		this.relayer();
		if(this.autoStart) {
			this.start();
		}
	},this);
}
/**
 * Constants
 */
SlideShow.FORWARD = 'forward';
SlideShow.REVERSE = 'reverse';
/**
 * Methods
 */
SlideShow.prototype = {
	'get': function(name) {
		return (name in this) ? this[name] : null;
	},
	'set': function(name, value) {
		if(!(name in this) || this[name] === undefined) {
			return false;
		}
		switch(name) {
			case 'imgClass':
			case 'contentClass':
				this[name] = String(value);
				break;
			case 'loop':
			case 'autoStart':
				this[name] = Boolean(value);
				break;
			case 'delay':
			case 'speed':
				this[name] = Number(value);
				break;
			case 'delays':
				if(value instanceof Array) {
					value = value.filter(function(val){ return !isNaN(Number(val)); });
					this[name] = value;
				}
				break;
			case 'direction':
				if(value == SlideShow.FORWARD || value == SlideShow.REVERSE) {
					this[name] = value;
				}
				break;
		}
		return this;
	},
	/**
	 * Register a function to be called when a valid slideshow event happens
	 * see the constructor for valid events.
	 */
	'register': function(event, callback) {
		if(event in this.events && typeof callback == 'function') {
			this.events[event].push(callback);
			return true;
		}
		return false;
	},
	/**
	 * Internal usage	Calls registered callbacks for the specified event
	 */
	'fireCallback': function(event, xargs) {
		if(event in this.events && this.events[event] instanceof Array) {
			var args = [this.current, this.last, event].concat((xargs instanceof Array) ? xargs: []);
			this.events[event].forEach(function(fn){
				fn.apply(this, args);
			},this);
		}
		return this;
	},
	'next': function() {
		if(this.direction == SlideShow.REVERSE) {
			return this.previous();
		}
		var next = this.current+1;
		if(next >= this.images.length && this.loop) {
			next = 0;
		} else if(next >= this.images.length) {
			next = this.current;
		}
		if(this.goto(next)) {
			this.fireCallback('next');
			return true;
		}
		return false;
	},
	'previous': function() {
		if(this.direction == SlideShow.FORWARD) {
			return this.next();
		}
		var prev = this.current-1;
		if(prev < 0 && this.loop) {
			prev = this.images.length-1;
		} else if(prev < 0) {
			prev = this.current;
		}
		if(this.goto(prev)) {
			this.fireCallback('previous');
			return true;
		}
		return false;
	},
	'goto': function(idx) {
		idx = Number(idx);
		if(isNaN(idx)) {
			return this;
		}
		if(idx >= 0 && idx < this.images.length) {
			this.last = this.current;
			this.current = idx;
		}
		if(this.last != this.current) {
			var topLayer = this.images.length-1;
			while(this.layers[topLayer-1] != this.current) {
				this.layers.unshift(this.layers.pop());
			}
			this.fireCallback('goto');
			this.transition();
			return true;
		}
		return false;
	},
	/**
	 * Internal usage	Creates the fading transition animation
	 */
	'transition': function() {
		var imgFrom = this.images[this.last], imgTo = this.images[this.current];
		var opac = 100, opac2 = 0;
		var step = 1, speed = this.speed/100;
		var self = this;
		
		function changeOpacity(index) {
			self.timers[index] = window.setTimeout(function() {
				opac -= step;
				opac2 += step;
				self.setOpacity(self.images[self.last], opac);
				self.setOpacity(self.images[self.current], opac2);
				
				if(self.content[self.last]) {
					self.setOpacity(self.content[self.last],opac);
				}
				if(self.content[self.current]) {
					self.setOpacity(self.content[self.current],opac2);
				}
				self.fireCallback('transition',[opac2]);
				window.clearTimeout(self.timers[index]);
				self.timers[index] = null;
			},(index * speed));
		}
		
		for(var i=0;i < 100;i++) {
			changeOpacity(i);
		}
		
		this.timers[101] = window.setTimeout(function(){
			var topLayer = self.images.length-1;
			while(self.layers[topLayer] != self.current) {
				self.layers.unshift(self.layers.pop());
			}
			self.relayer();
			window.clearTimeout(self.timers[101]);
		},(101 * speed));
	},
	/**
	 * Internal usage	Handles the rotation timer
	 */
	'rotate': function() {
		if(!this.rotating) {
			return;
		}
		var delay = (this.delays[this.current] || this.delay)+this.speed;
		var self = this, dirfn = (this.direction == SlideShow.REVERSE ? 'previous':'next');
		this.rotationTimer = window.setTimeout(function(){
			if(self[dirfn]()) {
				self.rotate();
			} else {
				self.stop();
			}
		},delay);
	},
	'start': function() {
		this.fireCallback('start');
		this.rotating = true;
		this.rotate();
		return this;
	},
	'stop': function() {
		this.fireCallback('stop');
		this.rotating = false;
		window.clearTimeout(this.rotationTimer);
		this.rotationTimer = null;
		return this;
	},
	/**
	 * Internal usage	Sets the opacity of an element
	 */
	'setOpacity': function(obj, value) {
		if(this.isIE) {
			if(obj.filters.length) {
				obj.filters[0].opacity = value;
			} else {
				obj.style.filter += 'progid:DXImageTransform.Microsoft.Alpha(opacity='+value+')';
			}
		} else {
			obj.style.opacity = value/100;
		}
	},
	/**
	 * Internal usage	Sets z-index of elements so they are still usable
	 */
	'relayer': function() {
		this.layers.forEach(function(id, n) {
			this.images[id].style.zIndex = n;
			if(this.content[id]) {
				this.content[id].style.zIndex = n;
			}
		},this);
	},
	'constructor': SlideShow
};