/**
 * royalGallery for jQuery - varsion 1.0
 * written by Krtolica Vujadin 
 *     http://blog.fractalbrain.net/?p=7
 *	   http://www.fractalbrain.net
 
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
**/

(function($){
	$.fn.royalGallery = function(options) {

		var defaults = {
			slices: 11,
			effectPause: 4000
		};
		
		var options = $.extend(defaults, options);
		options.slices <= 3 ? options.slices = 3 : (options.slices%2 === 0 ? --options.slices : options.slices);  // min 3 slices and number must be odd
	  
		// effects builder functions ...
		var buildSpiral = function(n)
		{
			var buildRow = function(N,row)
			{
				var ret = "";
				var N2 = N*N; 
				if (N==0) return;
				if (row == 0) for (var i=0; i<N; ++i) ret += (N2-i-1) + ',';
				else if (row == N-1) for (var i=0; i<N; ++i) ret += (N2-3*(N-1)+i-1) + ',';
				else{
					ret += (N2-4*(N-1)+row-1) + ',';
					ret += buildRow(N-2, row-1);
					ret += (N2-(N-1)-row-1) + ',';
				}		
				return ret;
			};
			var N = n;	
			var _array = [];
			var arrayString = "";
			
			for (var row=0; row<N; ++row){
				arrayString += buildRow(N, row);
			}
			
			var newArray = arrayString.split(',');
			for(var i = 0; i < N*N; i++){
				_array[i] = $.trim(newArray[i]);
			}

			return _array;
		};
		
		function buildTopRightDiagonal(N){
			var _array = [];
			for(var m = N, p = 0; m <= N+N; m++)
				for(var n = 0; n < N; n++)
					_array[p++] = m-n;
			return _array;
		}
		
		function buildTopLeftDiagonal(N){
			var _array = [];
			for(var m = 1, p = 0; m <= N; m++)
				for(var n = 0; n < N; n++)
					_array[p++] = m+n;
			return _array;
		}
		
		function buildHorizontalBars(N){
			var _array = [], p = 1;
			for(var m = 0; m < N*N; m += 2*N, p++)
				for(var n = 0; n < N; n++)
					_array[m+n] = p; 
			for(var m = N*N-(2*N); m > 0; m -= 2*N, p++)
				for(var n = 0; n < N; n++)
					_array[m+n] = p; 
			return _array;
		}
		
		function buildJawBars(N){
			var _array = [], p = 1;
			for(var m = 0; m < N*N; m+=2*N, p = 1)
				for(var n = 0; n < N; n++)
					_array[m+n] = p++;
			p = N;
			for(var m = N*N-(2*N); m > 0; m -= 2*N, p = N)
				for(var n = 0; n < N; n++)
					_array[m+n] = p--;
			return _array;
		}
		
		function buildTopLeftDiaMirror(N){
			var _array = [];
			for(var m = 1, p = 0; m <= N+N; m++)
				for(var n = 0, t = N; n < N; n++)
					_array[p++] = (m+n >= t + 1) ? --t : m+n;
			return _array;
		}
		
		function buildDigonalFullMirror(N){
			var _array = [], middle = parseInt(N/2) + 1, p = 0;
			for(var m = 1; m < parseInt(N/2) + 1; m++, ++middle)
				for(var n = 0, t = middle; n < N; n++)
					_array[p++] = (m+n >= t + 1) ? --t : m+n;
			
			for(var m = parseInt(N/2)+1; m > 0; m--,--middle)
				for(var n = 0, t = middle; n < N; n++)
					_array[p++] = (m+n >= t + 1) ? --t : m+n;
			
			return _array;			
		}
		
		function buildSnakes(N){
			var a = [], i=0;
			for(var m = 0; m < N; m++)
				for(var n = 0; n < N*N; n+=N)
					a[m+n] = i++;
			return a;
		}
		
		
		function buildBigSnake(N){
			var a = []; t=0;
			for(var m = 0; m < N*N; m+=N, (m%2===0)?t+=N+1:t+=N-1)
				for(var n = 0; n < N; n++, (m%2===0)?t++:t--)
					a[m+n] = t;
			return a;
		}
		
		
		// help functions
		function buildPositions(N,w,h){
			var retArray = [];
			for(var it = 0; it < N*N; it+=N){
				for(var jt = 0; jt < N; jt++){
					retArray[it+jt] = h*it/N + "," + w*jt;
				}
			}
			return retArray;
		}

		function fisherYates( myArray ) {    // array randomizer
		  var i = myArray.length;
		  if ( i === 0 ) return false;
		  while ( --i ) {
			 var j = rand(i);
			 var tempi = myArray[i];
			 var tempj = myArray[j];
			 myArray[i] = tempj;
			 myArray[j] = tempi;
		   }
		}
		
		function rand(n)
		{
		  return (Math.floor(Math.random() * n + 1));
		}
		
		return this.each(function() {
			
			$b = $(this); 		
			
			var _imgArray = $b.children('img');
			var pictures = new Array(_imgArray.size());
			var imageNumber = pictures.length;
			for(var m = 0; m < imageNumber; m++){
				pictures[m] = _imgArray[m].src;
			}
			$('img', $b).remove();	// we don't need them anymore
			
			var height = _imgArray[0].height, width = _imgArray[0].width;	// get picture dimensions			
			
			var d = document.createElement("div");
			$b.css({width: width, height: height, overflow: 'hidden'});
			$(d).css({width: width, height: height, position: 'relative', overflow:'hidden'});
			$b.append(d); var $base = $('div', $b);			
			_imgArray = null;	// we don't need this array anymore		
			
			var height = Math.round((height / options.slices)*10) / 10;		// get dimension of single block for effect ...
			var width  = Math.round((width / options.slices)*10) / 10;			

			var effC = [];
			effC[0] = buildDigonalFullMirror(options.slices);			// i = N+1 to -1
			effC[1] = buildTopLeftDiagonal(options.slices);				// i = N+N to 0
			effC[2] = buildTopRightDiagonal(options.slices);			// i = N+N to 0(i<1)
			effC[3] = buildJawBars(options.slices);						// i = N+1 to -1
			effC[4] = buildTopLeftDiaMirror(options.slices);			// i = N+1 to -1
			effC[5] = buildSpiral(options.slices);						// i = N*N to -1
			effC[6] = effC[5].slice();
			fisherYates(effC[6]);
			effC[7] = buildHorizontalBars(options.slices);
			effC[8] = buildSnakes(options.slices);
			effC[9] = buildBigSnake(options.slices);
				
			var effDesc = [];
			effDesc[0] = [ options.slices+1, 250 ];		// slices number, animation time
			effDesc[1] = [ options.slices+options.slices, 150 ];
			effDesc[2] = [ options.slices+options.slices, 150 ];
			effDesc[3] = [ options.slices+1, 250 ];
			effDesc[4] = [ options.slices+1, 250 ];
			effDesc[5] = [ options.slices*options.slices, 1 ];
			effDesc[6] = [ options.slices*options.slices, 1 ];
			effDesc[7] = [ options.slices+1, 250 ];
			effDesc[8] = [ options.slices*options.slices, 40 ];
			effDesc[9] = [ options.slices*options.slices, 30 ];
			
			var positionsTable = buildPositions(options.slices,width,height);			
			
			// build layers for each effect
			for(var m = 0; m < effC.length; m++){
				var _div = document.createElement("div");
				$(_div).css({position:'absolute',top:0,left:0,width:'100%', height:'100%',display:'block'});
				$(_div).attr('id','rgeff' + m).addClass('rgeff');
				
				for(var i = 0; i < options.slices*options.slices; i++){
					// generate inner divs for each effect and set indexes for iterating
					var _div1 = document.createElement("div");
					var _pos = (positionsTable[i] + "").split(",");
					
					$(_div1).css({position: 'absolute', display: 'block', top: _pos[0] + "px", left: _pos[1] + "px", backgroundImage: "url(" + pictures[0] + ")", backgroundPosition: "-" + _pos[1] + "px -" + _pos[0] + "px", width: width + 1, height: height + 2});			
					$(_div1).addClass(effC[m][i] + "");
					$(_div).append(_div1);
				}
				
				$base.append(_div);
			}

			var effectIterator = 0, iteratoR;
			var animation_ , eD; 	
			function animateEffect(){		//   t1 = opacity animation, t2 = effect duration, t3 = pause between animations
				var $effectDiv = $('#rgeff' + effectIterator);
				eD = rand(2);
				eD === 1 ? iteratoR = -1 : iteratoR = effDesc[effectIterator][0];
				
				return setInterval(function(){	
					eD === 1 ? iteratoR++ : iteratoR--;
					$effectDiv.children('div.' + iteratoR).animate({opacity: 1}, 600);//.css('display','block');
					
					if(eD === 2 && iteratoR < 0) {
						clearInterval(animation_);											
						SetUpNextEffect();
					}
					else if((eD === 1) && iteratoR > effDesc[effectIterator][0]) {
						clearInterval(animation_);											
						SetUpNextEffect();
					}

				}, effDesc[effectIterator][1]);
			}
			
			function SetUpNextEffect(){	
				
				setTimeout(
					function() { 					
						if(--imageNumber === -1) imageNumber = pictures.length - 1;
						$base.children('div.rgeff').css('z-index',1);
						effectIterator = rand(effC.length) - 1;
						$('#rgeff' + (effectIterator - 1 < 0 ? effC.length-1 : effectIterator - 1))
							.css('zIndex', 4).children()
							.css({backgroundImage: 'url(' + pictures[imageNumber + 1 === pictures.length ? 0 : imageNumber + 1] + ')'});								
						$('#rgeff' + effectIterator).css('z-index', 5).children()
							.css({opacity:0,backgroundImage: 'url(' + pictures[imageNumber] + ')'});				 
						iteratoR=effDesc[effectIterator][0];						
						
						animation_ = animateEffect();
					}, 
					options.effectPause
				);
			}
			SetUpNextEffect();
		});
    };
})(jQuery);
