function $splat(obj){
	var type = $type(obj);
	return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
};

function $lambda(value){
	return (typeof value == 'function') ? value : function(){
		return value;
	};
};

Fx.Morph = Fx.Styles.extend({
 
	start: function(to){	
		return this.parent(to);
	}
 
});






Element.extend({
	
	morph: function(props, option){
		
		if(!this.fxmorph){
			this.fxmorph = new Fx.Morph(this, $merge({wait: true}, option) );
		}
		this.fxmorph.start(props);
		
	},
	
	fade: function(how){
		var fade = this.get('tween'), o = 'opacity', toggle;
		how = $pick(how, 'toggle');
		switch (how){
			case 'in': fade.start(o, 1); break;
			case 'out': fade.start(o, 0); break;
			case 'show': fade.set(o, 1); break;
			case 'hide': fade.set(o, 0); break;
			case 'toggle':
				var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
				fade.start(o, (flag) ? 0 : 1);
				this.store('fade:flag', !flag);
				toggle = true;
			break;
			default: fade.start(o, arguments);
		}
		if (!toggle) this.eliminate('fade:flag');
		return this;
	},
	
	destroy: function(){
		Element.empty(this);
		Element.dispose(this);
	//	clean(this, true);
		return null;
	},
	dispose: function(){
		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
	}
	


	
	
});

var Asset = {

	javascript: function(source, properties){
		properties = $extend({
			onload: $empty,
			document: document,
			check: $lambda(true)
		}, properties);

		var script = new Element('script', {src: source, type: 'text/javascript'});

		var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
		delete properties.onload; delete properties.check; delete properties.document;

		script.addEvents({
			load: load,
			readystatechange: function(){
				if (['loaded', 'complete'].contains(this.readyState)) load();
			}
		}).set(properties);

		if (Browser.Engine.webkit419) var checker = (function(){
			if (!$try(check)) return;
			$clear(checker);
			load();
		}).periodical(50);

		return script.inject(doc.head);
	},

	css: function(source, properties){
		return new Element('link', $merge({
			rel: 'stylesheet', media: 'screen', type: 'text/css', href: source
		}, properties)).inject(document.head);
	},

	image: function(source, properties){
		properties = $merge({
			onload: Class.empty,
			onabort: Class.empty,
			onerror: Class.empty
		}, properties);
		var image = new Image();
		var element = $(image) || new Element('img');
		['load', 'abort', 'error'].each(function(name){
			var type = 'on' + name;
			var event = properties[type];
			delete properties[type];
			image[type] = function(){
				if (!image) return;
				if (!element.parentNode){
					element.width = image.width;
					element.height = image.height;
				}
				image = image.onload = image.onabort = image.onerror = null;
				event.delay(1, element, element);
				element.fireEvent(name, element, 1);
			};
		});
		
		image.src = element.src = source;
		
		if (image && image.complete) image.onload.delay(1);
		return element.set(properties);
	},

	images: function(sources, options){
		options = $merge({
			onComplete: Class.empty,
			onProgress: Class.empty,
			onError: 	Class.empty
		}, options);
				
		sources = $splat(sources);
		
		
		var images = [];
		var counter = 0;
		
		return new Elements(sources.map(function(source){
			return Asset.image(source, {
				onload: function(){
					options.onProgress.call(this, counter, sources.indexOf(source));
					counter++;
					if (counter == sources.length) options.onComplete();
				},
				onerror: function(){
					options.onError.call(this, counter, sources.indexOf(source));
					counter++;
					if (counter == sources.length) options.onComplete();
				}
			});
		}));
	}

};	



var Floom = new Class({
	
	Implements: [Events, Options],
		
	options: {
		prefix: 		'floom_',
		amount: 		30,
		animation: 		70,
		interval: 		8000,
		axis: 			'vertical',
		progressbar: 	true,
		captions: 		true,
		captionsFxOut: 	'',
		captionsFxIn: 	'',
		slidesBase: 	'',
		sliceFxIn: 		{			
			'opacity'	: 1
		}
	//	onSlideChange: 	Class.empty,
	//	onPreload: 		Class.empty
	},
	
	initialize: function(wrapper, slides, options){
	
		this.options = $merge(this.options, options);
		
		wrapper = $(wrapper);
		
		this.slides = this.driver(slides);
		
		
		
		
		
		this.wrapper = {
			el: 	wrapper,
			width: 	wrapper.getSize().size.x,
			height: wrapper.getSize().size.y
		};
		
		this.slices = {
			els: [],
			width: (this.options.axis == 'vertical' ? this.wrapper.width / this.options.amount : this.wrapper.width),
			height: (this.options.axis == 'vertical' ? this.wrapper.height : this.wrapper.height / this.options.amount)
		};
		
		this.current = {
			slide: -1,
			overlay: 0,
			counter: 0
		};
		
		this.preloadImgs = [];
		
		this.createStructure();	
		
	},
	
	driver: function(slides){		
		// build the options object from a set of elements
		
		if ($type(slides[0]).contains('element')) {			
			this.slidesEl = [];
			
			// assign caption and the filename/url
			slides.each(function(slide){
				this.slidesEl.push({
					image: slide.get('src'),
					caption: slide.get('title')
				});
			}, this);
			
			// remove redundant elements
			slides.destroy().empty();
			
			// assign the new object
			slides = this.slidesEl;			
		}
		
		return slides;
	},
	
	createStructure: function(){
		this.container = new Element('div', {
			'class': this.options.prefix + 'container',
			'styles': {
				'height': this.wrapper.height,
				'width': this.wrapper.width
			}
		});
				
		this.container.inject(this.wrapper.el);
		
		// create the progress bar
		if (this.options.progressbar) this.createProgressbar();
		
		// create the caption container
		if (this.options.captions) this.createCaptions();
		
		// preload images and start up the slider
		this.preload();
		
	},
	
	createProgressbar: function(){
		this.progressbar = new Element('div', {
			'class': this.options.prefix + 'progressbar'
			
		});
		
		this.progressbar.inject(this.wrapper.el);
	},
	
	createCaptions: function(){
		this.captions = new Element('div', {
			'class': this.options.prefix + 'caption',
			'html': 'caption',
			'styles': {
				'opacity': 0
			}
		});
		
		this.captions.inject(this.wrapper.el);
	},
	
	preload: function(){		
		// build the images array
		this.slides.each(function(o){
			this.preloadImgs.push(this.options.slidesBase + o.image);
		}, this);
		
		// preload all and activate when done
		new Asset.images(this.preloadImgs, {
			onComplete: this.onPreload.bind(this)
		});
		
		
	},
	
	onPreload: function(){
		
		
		this.timer_ami = this.animateBlinds().periodical(this.options.interval, this);
		
		//return;
		//this.fireEvent('onPreload', this.slides[this.current.slide]);
	},

	animateBlinds: function(){
		this.current.slide++;
		
		// go back to the first one when at the end
		if (this.current.slide == this.slides.length) this.current.slide = 0;
		
		// create blinds
		for (var idx = 0; idx < this.options.amount; idx++) {
			
			this.createBlinds.delay(this.options.animation * idx, this, idx);
		}
		
	
		
		if (this.options.captions) {
			
			// apply the animation
			this.captions.morph($merge({
				'opacity': 0
			}, this.options.captionsFxOut), {});
		}
		
		
		
		return this.animateBlinds;
	},
		
	
	createBlinds: function(idx){
		
		// update the global counter
		this.current.counter = idx;
		
		
		
		// create the slices
		this.slices.els[idx] = new Element('div', {
			'class': this.options.prefix + 'slice ' + this.options.prefix + this.options.axis,
			
			'styles': $merge({
				'opacity': 0,
				'width': this.slices.width,
				'height': this.slices.height,
				'background-image': 'url(' + this.options.slidesBase + this.slides[this.current.slide].image + ')'
			}, this[this.options.axis]())
		}).inject(this.container);		
				
		// animate the slide
		this.slices.els[idx].morph(this.options.sliceFxIn, {}); 
	
		if (idx == this.options.amount-1) {
			if(this.timer_step){
				$clear(this.timer_step);
			}
			
			this.timer_step = this.step.delay(this.options.animation, this);
			
			
		}
	},
	
	horizontal: function(){
		return {
			'background-position': '0 -' + (this.slices.height * this.current.counter) + 'px'
		};	
	},

	vertical: function(){
		
		return {
			'background-position': '-' + (this.slices.width * this.current.counter) + 'px 0'
		};
	},
	
	step: function(){		
		
		
		// destory slices when animations finishes
		
		this.slices.els.each(function(slice){
			slice.destroy();
		});
		
		
		this.container.setStyles({
			'background-image': 'url(' + this.options.slidesBase + this.slides[this.current.slide].image + ')'
		});
		
		

		// prepeare and animate the progressbar
		if (this.options.progressbar) {
			
			// calculate the width of the progressbar including margins
			
			var calculatedWidth = this.container.getSize().size.x - (parseInt( this.progressbar.getStyles('margin-left')['margin-left'] ) * 2);
			
		
			$(this.progressbar).morph({
				'width': [0, calculatedWidth]
				
			}, {
				'duration': this.options.interval - (this.options.animation * this.options.amount)
				
			});
			
		}
		
		
		
		// update and animate the caption
		if (this.options.captions) {
			
			// update the copy
			
			
			this.captions.setHTML(this.slides[this.current.slide].caption);
			
			// animate the caption
			this.captions.morph($merge({
				'opacity': 1
			}, this.options.captionsFxIn), {});
		}
		
	//	this.fireEvent('onSlideChange', this.slides[this.current.slide]);
	}
});


/*



*/
