/**
 * @classDescription Vivieb.Rotator
 *	displays images and/or text from an array of objects rotating them.
 
 *	The text can be displayed over the picture (at the bottom of it)
 *	or in the area immediately below it. The area assigned to the text
 * is large as the picture. The position and the style of the text area
 * can be set in the options object.
 * 
 * Usage: new Vivieb.ContentBrowser(options)
 * 
 * Requires: 
 * 	class.js, jquery.js, jquery.vivieb.js, vivieb.buttons.js, vivieb.links.js,
 *  j2browse.js, iepngfix.htc
 *  
 * Manual setup:
 * - The resource path for the button manager (Vivieb._RotatorImpl.bm). It must
 * indicate the path where to find the images and the iepngfix.htc file.
 * - In the iepngfix.htc file, set the blankImg variable. 
 * 
 */

Vivieb._Rotator = {

	/**
	 * @constructor Vivieb.ContentBrowser
	 *
	 * @param options {Object}
	 *	object that specifies the options for the class behavior through its properties.
	 *	These are:
	 *
	 * animationSpeed {integer}:
	 *   the number of milliseconds to fade in the image
	 *   Default: 1000
	 *  
	 * containerStyle {object}:
	 *  object that specifies through its fields the CSS properties for the
	 *  rotator's container (the main container with the "id" attribute set to
	 *  the "id" option). Its width and height (excluding margin, border and 
	 *  padding) will be set to the "width" and "height" option.
	 * 
	 * dataSource {array of objects}:
	 * 	An array of object containing the data to be displayed.
	 * 	Every object must define his properties according to the value of
	 * 	the "srcField", "txtField" and "urlField" options. They indicate
	 * 	respectively the name of the string property containing the
	 * 	image url to be displayed, the name of the string property containing
	 * 	the related text to be displayed and the name of the string property
	 * 	containing the url the image and the text must be linked to.
	 *	Default: undefined.
	 *
	 * height {integer}
	 *  the height in pixels which will be assigned to the rotator stage
	 *  (including margin, border and padding). It is also the height of the 
	 *  main container (escluding margin, border and padding).
	 *  Default: 200
	 *
	 * id {string}: 
	 * 	Unique identifier.
	 * 	Must be equal to the 'id' attribute value of the div element acting
	 * 	as the rotator's container in the DOM tree.
	 * 	Default: 'rotator'.
	 * 
	 * linked {boolean}
	 *   true if the displayed data must be linked to an url. In that case, 
	 *   the url  must be specified in the "url" property of every objects
	 *   of the "dataSource" option.
	 *   Default: false
	 *  
	 * navigator {Object}:
	 * 	if defined, the navigator box is enabled. This option is valid only
	 * 	for "image" and "image-text" type. Its properties are:
	 * 		counterSeparator {string}
	 * 			the string to use to separate the current image number to the 
	 * 			total image number. 
	 * 			Default: '/' 
	 * 		counterStyle {object}:
	 * 			CSS properties for the counter box
	 * 		hasBox {boolean}:
	 * 			if true, the previous and next buttons and the counter will be
	 * 			in a navigator box. Otherwise they will be directly put into
	 * 			the main container.
	 * 		nextExt {string}:
	 * 			file names extension for the previous button. 
	 * 			Default: 'png'
	 * 		nextImage {string}:
	 * 			basic file name for rendering the next button. 
	 * 			Default: 'next'
	 * 		nextStyle {object}:
	 * 			CSS properties for the next Button
	 * 		nextTitle {string}:
	 * 			hint string for the next Button
	 * 			Default: 'next' 
	 * 		previousExt {string}:
	 * 			file names extension for the previous button. 
	 * 			Default: 'png'
	 * 		previousImage {string}:
	 * 			basic file name for rendering the previous button.
	 * 			Default: 'previous'
	 * 		previousStyle {object}:
	 * 			CSS properties for the previous Button
	 * 		previousTitle {string}:
	 * 			hint string for the previous Button
	 * 			Default: 'previous' 
	 * 		style {object}: 
	 * 			CSS properties for the navigator box. Valid only if "hasBox"
	 * 			is set to true.
	 * 		 
	 *  Default: undefined.
	 * 
	 * nextDelay {integer}:
	 *   the number of milliseconds to wait for starting the next animation
	 *   Default: 5000
	 *  
	 * src {string}:
	 * 	the property name in every objects of the "dataSource" option
	 *  containing the url of the image to be displayed. 
	 *  Only for type "image" and "image-text". 
	 *  Default: src 
	 * 
	 * sceneStyle {object}:
	 *  object that specifies through its fields the CSS properties for the
	 *  rotator scene (the box which the content (image or text) is put into).
	 *  Its total width and height (including margin, border and padding) 
	 *  will be constrained to be equal to the "width" and "height" of the
	 *  stage (escluding its margin, border and padding).
	 *  The rotator uses two scenes that are animated during the rotation and
	 *  which contents are updated. 

	 * stageStyle {object}:
	 *  object that specifies through its fields the CSS properties for the
	 *  rotator's stage (the box which the scenes are put into). Its total width
	 *  and height (including margin, border and padding) will be constrained 
	 *  to be equal to the "width" and "height" option.
	 *  
	 * stopOnOver {boolean}:
	 * 	if true, the sliding does not sto when the mouse is over the image.
	 * 
	 * stretch {boolean}:
	 *  if true, the width and the heigth of the images will be stretched to
	 *  the content stage box. Otherwhise, the image is rendered with its 
	 *  natural dimensions and the extra content will be cut.
	 * 
	 * textActiveStyle {Object}:
	 *  object that specifies through its fields the CSS properties for the 
	 *  text during a mouse click. Only if "linked" is true.
	 * 	
	 * textAreaStyle {Object}:
	 *  object that specifies through its fields the CSS properties for the 
	 *  area where to put the text. This options is valid only for the 
	 *  "image-text" type.
	 *  
	 * textNormalStyle {Object}:
	 *  object that specifies through its fields the CSS properties for the
	 *  text displayed.
	 *   
	 * textOverStyle {Object}:
	 *  object that specifies through its fields the CSS properties for the 
	 * 	text while the mouse cursor is over it. Only if "linked" is true.
	 * 
	 * textStyle {Object}:
	 *  object that specifies through its fields the CSS properties for the 
	 *  direct container of the text.
	 * 	
	 * type {string}:
	 * 	the type of data to be displayed. It can be one of the following value:
	 * 		"text": 
	 * 			only displays text. The txt property must be defined in every 
	 * 			objects of the "dataSource" option.
	 * 		"image": 
	 * 			only displays images. The src property must be defined in every
	 * 			objects of the "dataSource" option.
	 * 		"image-text": 
	 * 			displays text and images. Both the txt and src options must be
	 * 			defined in every objects of the "dataSource" option.
	 * Default: "image-text".
	 *
	 * txt {string}:
	 * 	the property name in every objects of the "dataSource" option
	 *  containing the text to be displayed. 
	 *  Only for type "text" and "image-text". 
	 *  Default: txt 
	 *
	 * url {string}:
	 * 	the property name in every objects of the "dataSource" option
	 *  containing the url which the displayed data must be linked to. 
	 *  Only if "linked" is set ot true. 
	 *  Default: url 
	 *
	 * width {integer}
	 *  the width in pixels which will be assigned to the rotator stage
	 *  (including margin, border and padding). It is also the width of the 
	 *  main container (escluding margin, border and padding).
	 *  Default: 200
	 *
	 *
	 */
	initialize: function(options){
		var instance = this;
		instance.impl = Vivieb._RotatorImpl;
		var impl = instance.impl; 
		impl._setOptions(instance, options);
		var o = instance.options;
		if (!o.dataSource || o.dataSource.length == 0)
			jQuery.vv_exception(instance, {name: 'DataSourceNotFound'});
		instance._endAnimation = impl._getEndAnimation(instance);
		instance._nextAnimation = impl._getNextAnimation(instance);
		if(o.type != 'text')
			impl._cacheImage(instance, 0);
		impl._build(instance);
		jQuery(window).unload(impl._destroy(instance));
		impl._render(instance, 0);
	},
	
	toString: function(){
		var instance = this;
		return 'Rotator (id: ' + instance.options.id + ')';
	}
};

Vivieb._RotatorImpl = {
	
	exception: {
		ContainerNotFound	: 'Container \'{0}\'not found in the DOM tree!',
		DataSourceNotFound	: 'Data source missing or empty!'
	},
	
	lm: new Vivieb.LinksManager({id: '_RotatorImpl_lm',	type: 'link', rollover: true}),
	
	bm: new Vivieb.ButtonsManager({
			id: '_RotatorImpl_bm', 
			type: 'button', 
			path: '/html/js/ext/images/', 
			rollover: true
		}),
	
	AHEAD	: 1,
	BACK	: 2,
	
	_build: function(instance) {
		var o = instance.options;
		var container = jQuery('#' + o.id);
		if(container.length == 0)
			jQuery.vv_exception(instance, {name: 'ContainerNotFound'}, o.id);
		var impl = instance.impl;
		container.css(o.containerStyle || {})
			.vv_makePositioned()
			.css('overflow', 'hidden')
			.width(o.width)
			.height(o.height);
		if(o.stopOnOver)
			container.hover(impl._getMouseoverHandler(instance), impl._getMouseoutHandler(instance));
		impl._buildStage(instance);
		impl._buildNavigator(instance);
		impl._cleanOptions(instance);
	},
	
	_buildNavigator: function(instance) {
		var o = instance.options;
		var nav = o.navigator;
		var impl = instance.impl;
		if(o.type == 'text' || !nav) return;
		var target;
		if (nav.hasBox) {
			var navBox = jQuery('<div>').css(nav.style ||{})
			.css({
				position: 'absolute',
				overflow: 'hidden'
			})
			.addClass(o.id + '_nav')
			.appendTo('#' + o.id);
			target = navBox[0];
		}
		else
			target = jQuery('#' + o.id)[0];
		impl.bm.insert({
				src			: nav.previousImage || 'previous',
				ext			: nav.previousExt,
				attributes	: {title: nav.previousTitle || 'previous'},
				style		: nav.previousStyle || {},
				params		: {instance: instance, dir: impl.BACK},
				target		: target
			});
		impl.bm.insert({
				src			: nav.nextImage || 'next',
				ext			: nav.nextExt,
				attributes	: {title: nav.nextTitle || 'next'},
				style		: nav.nextStyle || {},
				params		: {instance: instance, dir: impl.AHEAD},
				target		: target
			});
		jQuery('<div><span></span>' + (nav.counterSeparator || '/') + 
			'<span>' + o.dataSource.length + '</span></div>')
			.css(nav.counterStyle || {})
			.addClass(o.id + '_c')
			.appendTo(target);
	},
	
	_buildStage: function(instance) {
		var o = instance.options;
		function setText(){
			instance.impl.lm.setRenderStyles(o.id, 'normal', o.textNormalStyle);
			instance.impl.lm.setRenderStyles(o.id, 'over', o.textOverStyle);
			instance.impl.lm.setRenderStyles(o.id, 'selected', o.textActiveStyle);
			instance.impl.lm.insert({
				renderSet: o.id,
				target: this,
				style: o.textStyle || {}
			})
		}
		
		
		var stage = jQuery('<div>')
			.css({margin: '0px', padding: '0px', border: '0px none'})
			.css(o.stageStyle || {})
			.addClass(o.id + 'stage')
			.css({
				position: 'absolute',
				overflow: 'hidden',
				width	: o.width + 'px',
				height	: o.height + 'px'
			})
			.appendTo('#' + o.id)
			.each(function() {
				
				var el = jQuery(this);
				var outerWidth = jQuery.vv_getOuterWidth(el);
				if(outerWidth > o.width)
					el.width(2*o.width - outerWidth);
				var outerHeight = jQuery.vv_getOuterHeight(el);
				if(outerHeight > o.height)
					el.height(2*o.height - outerHeight);
			});
		var stageWidth = parseInt(stage.css('width'));
		var stageHeight = parseInt(stage.css('height'))
		var scenes = jQuery('<div>').add('<div>')
			.css({margin: '0px', padding: '0px', border: '0px none'})
			.css(o.sceneStyle || {})
			.css({
				position: 'absolute',
				overflow: 'hidden'
			})
			.appendTo(stage[0])
			.width(stageWidth).height(stageHeight)
			.each(function() {
				var el = jQuery(this);
				var outerWidth = jQuery.vv_getOuterWidth(el);
				var width = stageWidth;
				if(outerWidth > width)
					el.width(2*width - outerWidth);
				var outerHeight = jQuery.vv_getOuterHeight(el);
				var height = stageHeight;
				if(outerHeight > height)
					el.height(2*height - outerHeight);
			});
		switch(o.type) {
			case "image-text":;
				var txtArea = jQuery('<div>').css(o.textAreaStyle || {})
					.addClass(o.id + '_ta')
					.css({position: 'absolute'})
					.appendTo('#' + o.id);
				if(o.linked)
					txtArea.each(setText);
				else 
					txtArea.append(jQuery('<div>').css( o.textStyle || {}));
			case "image":
				var img = jQuery('<img>').css({border: '0px none'});
				if(o.stretch)
					img.width(scenes.width()).height(scenes.height());
				scenes.append(img);
				break;
			case "text":
				if(o.linked)
					scenes.each(setText); 
				else 
					scenes.append(jQuery('<div>').css( o.textStyle || {}));
		}
		if(o.linked && o.type != 'text') {
			scenes.wrapInner('<a href="#" style="text-decoration: none"></a>');
		}		
	},
	
	_cacheImage: function(instance, index) {
		var o = instance.options;
		var ds = o.dataSource[index]; 
		if(ds) new Image().src = ds[o.src || 'src']; 
	},
	
	_cleanOptions: function(instance) {
		var o = instance.options;
		delete o.containerStyle;
		delete o.stageStyle;
		delete o.textStyle;
		delete o.textAreaStyle;
		delete o.textNormalStyle;
		delete o.textOverStyle;
		delete o.textActiveStyle;
		delete o.width;
		delete o.height;
		delete o.stretch;
		if(o.type != 'text' && o.navigator)
			o.navigator = true;
		else
			delete o.navigator;
	},
	
	_destroy: function(instance) {
		return function(){
			instance._nextAnimation = null;
			instance._endAnimation = null;
		}
	},
	
	_getEndAnimation: function(instance) {
		return function() {
			var id = instance.options.id;
			jQuery('#' + id + ' div.'+ id + 'stage div:first').hide();
		}
	},
	
	_getMouseoutHandler: function(instance) {
		return function(event){
			delete instance.over;
			if (!instance.nextTimeout) {
				var o = instance.options;		
				instance.nextTimeout = 
					setTimeout(instance._nextAnimation, o.animationSpeed + o.nextDelay);
			}
		}
	},
	
	_getMouseoverHandler: function(instance) {
		return function(event){
			if (instance.nextTimeout) {
				clearTimeout(instance.nextTimeout);
				delete instance.nextTimeout;
			}
			instance.over = true;
		}
	},
	
	_getNext: function(instance, direction) {
		var n = instance.current;
		var max = instance.options.dataSource.length - 1;
		switch(direction) {
			case instance.impl.BACK: return n > 0 ? n - 1 : max;
			case instance.impl.AHEAD: return n < max ? n + 1 : 0;
		}
	},
	
	_getNextAnimation: function(instance) {
		return function() {
			instance.impl._render(instance, instance.next);	
		}			
	},
	
	_navClick: function(btn) {
		var instance = btn.params.instance; 
		instance.impl._render(instance, instance.impl._getNext(instance, btn.params.dir));
	},
	
	_render: function(instance, index) {
		delete instance.nextTimeout;
		var o = instance.options;
		instance.current = index;
		var ds = o.dataSource[index];
		var animMethod;
		switch(o.type) {
			case 'image-text':
				if(o.linked)
					jQuery('#' + o.id + ' div.'+ o.id + '_ta a:first')
						.attr('href', ds[o.url || 'url'] || '')
						.html(ds[o.txt || 'txt'] || '');
				else
					jQuery('#' + o.id + ' div.'+ o.id + '_ta div:first')
						.html(ds[o.txt || 'txt'] || '');
			case 'image':
				jQuery('#' + o.id + ' div.'+ o.id + 'stage div:first img:first')
					.attr('src', ds[o.src || 'src'] || '');
				if(o.linked)
					jQuery('#' + o.id + ' div.'+ o.id + 'stage div:first a:first')
					.attr('href', ds[o.url || 'url'] || '');
				animMethod = 'fadeIn';
				if(o.navigator)
					jQuery('#' + o.id + ' div.'+ o.id + '_c span:first').html(index + 1);
				break;
			case 'text':
				if(o.linked)
					jQuery('#' + o.id + ' div.'+ o.id + 'stage div:first a:first')
						.attr('href', ds[o.url || 'url'] || '')
						.html(ds[o.txt || 'txt'] || '');
				else
					jQuery('#' + o.id + ' div.'+ o.id + 'stage div:first div:first').html(ds[o.txt || 'txt'] || '');
				animMethod = 'slideDown';
				break;
		}
		instance.next = instance.impl._getNext(instance, instance.impl.AHEAD);
		if(o.type != 'text')
			instance.impl._cacheImage(instance, instance.next);
		jQuery('#' + o.id + ' div.'+ o.id + 'stage div:first')
			.appendTo('#' + o.id + ' div.'+ o.id + 'stage')
			[animMethod](o.animationSpeed, instance._endAnimation);
		if(!instance.over)
			instance.nextTimeout = 
				setTimeout(instance._nextAnimation, o.animationSpeed + o.nextDelay);
	},
	
	_setOptions: function(instance, options){
		var o = instance.options = jQuery.extend(true, {
			id: 'rotator',
			type: 'image-text'
		}, options || {});
		o.animationSpeed = jQuery.vv_getInteger(o.animationSpeed, 1000);
		o.nextDelay = jQuery.vv_getInteger(o.nextDelay, 5000);
		o.width = jQuery.vv_getInteger(o.width, 200);
		o.height = jQuery.vv_getInteger(o.height, 200);
	}	
};

Vivieb._RotatorImpl.bm.onClick = Vivieb._RotatorImpl._navClick; 

Vivieb.Rotator = new Class(Vivieb._Rotator);
