(function($){

function Listeners() {
	this._listeners = [];
}

Listeners.prototype = {
	add: function( listener ) {
		this._listeners.push( listener );
	},
	remove: function( listener ) {
		var listeners = this._listeners;
		for ( var i = 0, l = listeners.length; i < l; ++i ) {
			if ( listener === listeners[i] ) {
				this._listeners = listeners.concat();
				this._listeners.splice( i, 1 );
				return true;
			}
		}
		return false;
	},
	dispatch: function( that ) {
		if ( this._listeners.length > 0 ) {
			var listeners = this._listeners,
				args = listeners.slice.call( arguments, 1 );
				
			for ( var i = 0, l = listeners.length; i < l; ++i ) {
				listeners[i].apply( that, args );
			}
		}
	}
};

function ImagesLoader( images ) {
	this._images = [];
	this._loading = false;
	this._nLoaded = 0;
	this.onLoad = new Listeners();
	
	var that = this;
	this._onLoad = function() {
		var hasMore = ++that._nLoaded < that._images.length;
		
		that.onLoad.dispatch( that, this.src, hasMore );
		
		if ( hasMore ) {
			that._loadNext();
		} else {
			that._loading = false
		}
	}
	
	if ( images ) {
		this.push.apply( this, images );
	}
	
}

ImagesLoader.prototype = {
	
	_loadNext: function() {
		var i = this._nLoaded;
		var src = this._images[i];
		var img = new Image();
		this._images[i] = img;
		this._loading = true;
		img.onload = this._onLoad;
		img.src = src;
	},
	
	push: function() {
		this._images.push.apply( this._images, arguments );
		
		if ( this._images.length > this._nLoaded && !this._loading ) {
			this._loadNext();
		}
		
		return this;
	},
	
	circularGetter: function() {
		var that = this,
			stack = [],
			index = 0,
			waiting = false;
		
		var listener = function( src ) {
			if ( index >= that._images.length ) {
				index = 0;
			}
			
			if ( stack.length && this._nLoaded > index ) {
				var cb = stack.shift();
				
				if ( stack.length === 0 ) {
					this.onLoad.remove( listener );
					waiting = false;
				}
				
				cb( that._images[ index++ ].src );
			}
		};
		
		return function( cb ) {
			if ( index >= that._images.length ) {
				index = 0;
			}
		
			if ( index < that._nLoaded && stack.length === 0 ) {
				cb( that._images[ index++ ].src );
			
			} else {
				stack.push( cb );
				if ( !waiting ) {
					that.onLoad.add( listener );
					waiting = true;
				}
			}
		}
	}
	
};

	function getImgGetter( elem ) {
		var srcs = $(elem).attr('rel').split(' ');
		return new ImagesLoader( srcs ).circularGetter();
	}

	$.fn.imgSlider = function( options ) {
	
		options = $.extend({
			slices: 25,
			animSpead: 500,
			pauseTime: 3000,
			getNextImageGetter: getImgGetter
		}, options);
		
		return this.each(function() {
			var getNextImage = options.getNextImageGetter( this ),
				element = $(this);
			
			getNextImage(function( src ) {
				var sliderWidth = element.width(),
					nSlices = options.slices,
					sliceWidth = Math.round( sliderWidth / nSlices );
			
				var slider = $('<div class="img-slider"/>')
					.css({
						position: 'relative',
						width : sliderWidth,
						height: element.height(),
						background: 'url(' + src + ') no-repeat'
					});
					
				for ( var i = 0; i < nSlices; ++i ) {
					$('<div class="img-slice"/>').css({
						opacity: 0,
						display: 'block',
						position: 'absolute',
						zIndex: 50,
						height: '100%',
						left: sliceWidth * i,
						width: i === nSlices - 1 ? sliderWidth - sliceWidth * i : sliceWidth
					})
					.appendTo( slider );
				}
				
				var slices = slider.find('.img-slice');
				
				element.replaceWith( slider );
				
				setTimeout(function() {
					getNextImage(function next( src ) {
						var time = 0, nToGo = nSlices;
					
						slices.css({
							height: 0,
							opacity: 0
						})
						.each(function( i ) {
							$(this)
								.css( i % 2 ? 'bottom' : 'top', 0 )
								.css( 'background', 'url(' + src + ') no-repeat -' + (i * sliceWidth) + 'px 0%' )
								.delay( 100 + time )
								.animate( { height: '100%', opacity: 1 }, options.animSpead, ondone );
								
							time += 50;
						});
						
						function ondone() {
							if ( --nToGo === 0 ) {
								slider.css( 'background', 'url(' + src + ') no-repeat' );
								slices.hide();
								setTimeout(function() {
									getNextImage( next );
								}, options.pauseTime);
							}
						}
					});
				}, options.pauseTime );
			});
		});
	
	};
	
})(jQuery);
