/*
Transitions

Version: 1.0 alpha

License:
	MIT-style license. (http://opensource.org/licenses/mit-license.php)

Copyright:
	Copyright (c) 2008 [Nathan White](http://nwhite.net/).

Author:
	Nathan White
	
Redistributions of source code must retain the above license, copyright and author notice
*/

Array.implement({
	shuffle : function() {
          for (var i = 0; i < this.length; i++) {
              var j = i;
              while (j == i) {
                  j = Math.floor(Math.random() * this.length);
              }
              var contents = this[i];
              this[i]    = this[j];
              this[j]    = contents;
          }  
          return this;
      }
});


var Transitions = new Class({
	
	Implements : [Options],
	
	keys : [], 
	elements : [],
	current : [],
	boxes : [],
	stop : false,
	
	rows : 0,
	columns : 0,
	
	changed : {
		fade : false,
		duration : false
	},

	options : {
		autoStart : true,
		width : 20,
		height : 20,
		duration : 1000,
		delay : 40, // also know as fps
		images : [], // images
		wait : 1000,
		fade : false,
		index : 0,
		gobble : 'random',
		sweep : [ 'random', 'random'],
		blinds : 'random',
		effect : 'random',
		disolve : 'random'
	},
	
	initialize : function(el,options){
		this.container = $(el);
		
		this.setOptions(options);
		
		new Asset.images(this.options.images);
		
		this.container.setStyle('background', 'url('+this.options.images[this.options.index]+')');
		
		var size = this.container.getSize();
		this.rows = Math.ceil(size.y / this.options.height);
		this.columns = Math.ceil(size.x / this.options.width);
		
		this.index = this.options.index + 1;
		this.fade = this.changed.fade = this.options.fade;
		this.changed.duration = this.options.duration;
		
		var opacity = (this.fade) ? 0 : 1;
		var display = (this.fade) ? 'block' : 'none';
		
		var idx = 0;
		(this.rows).times(function(row){
			this.boxes[row] = [];
			(this.columns).times(function(col){
				this.boxes[row][col] = idx;
				this.elements.push(new Element('div',{
					'activated' : 'false',
					'styles' : {
						'position' : 'absolute',
						'overflow' : 'hidden',
						'top' : this.options.height * row,
						'left' : this.options.width * col,
						'width' : this.options.width,
						'height' : this.options.height,
						'background-image' : 'url('+this.options.images[this.index]+')',
						'background-position' : '-'+(this.options.width * col)+'px -'+(this.options.height * row)+'px',
						'padding' : '0px 0px 0px 0px',
						'margin' : '0px 0px 0px 0px',
						'opacity' : opacity,
						'display' : display
					}
				}).set('morph',{ duration : this.options.duration }).inject(this.container));


				this.keys.push(idx);
				idx++;
			},this);
		},this);
		
		if(this.options.autoStart)	this.start.delay(this.options.wait,this);
	},
	
	start : function(){
		this.stop = false;
		if(this.options.effect == 'random') var effect = ['disolve','sweep','blinds','gobble'][Math.floor(Math.random()*4)];
		else effect = this.options.effect;
		switch(effect){
			case 'disolve' :
				this.current = this.keys.shuffle();
				var disolveCount = (this.options.disolve == 'random') ? [3,5,7,10][Math.floor(Math.random()*4)] : this.options.disolve;
				this.intervalID = this.disolve.periodical(this.options.delay,this,disolveCount);
				break;
			case 'sweep' :
				var sweep = this.options.sweep;
				if(sweep[0] == 'random') sweep[0] = Math.floor(Math.random()*this.rows);
				if(sweep[1] == 'random') sweep[1] = Math.floor(Math.random()*this.columns);
				this.totalTiles = this.rows * this.columns;
				this.sweepCount = 0;
				this.sweep(sweep[0],sweep[1]);
				break;
			case 'blinds' :
				this.blindState = { t : 0, l : 0, r: this.columns-1, b : this.rows-1 };
				if(this.options.blinds == 'random') dir = ['l','r','t','b'][Math.floor(Math.random()*4)];
				else dir = this.options.blinds;
				this.blinds(dir);
				break;
			case 'gobble' :
				if(this.options.gobble == 'random') size = [2,4][Math.floor(Math.random()*2)];
				else size = this.options.gobble;
				this.gobble('+',0,0,size);
				break;
		}
	},
	
	stop : function(){
		this.stop = true;
	},
	
	finished : function(){
		this.container.setStyle('background', 'url('+this.options.images[this.index]+')');		
		this.index = (this.index+1 < this.options.images.length) ? this.index+1 : 0;
		
		if(this.fade != this.changed.fade) this.fade = this.changed.fade;
		var opacity = (this.fade) ? 0 : 1;
		var display = (this.fade) ? 'block' : 'none';
		
		this.elements.each(function(el){
			el.set({
				'activated' : 'false',
				'styles' : {
					'opacity' : opacity,
					'display' : display,
					'background-image' : 'url('+this.options.images[this.index]+')'
				}
			});
			if(this.options.duration != this.changed.duration) el.set('morph',{ duration : this.changed.duration });
		},this);
		
		if(this.options.duration != this.changed.duration) this.options.duration = this.changed.duration;
		if(!this.stop) this.start.delay(this.options.wait,this);
	},
	
	setFade : function(value){
		if(this.fade != value) {
			this.changed.fade = value;
		}
	},
	
	setDuration : function(value){
		if(this.options.duration != value) this.changed.duration = value;
	},
	
	disolve : function(disolveCount){
		var len = this.current.length;
		if(len == 0){
			clearInterval(this.intervalID);
			this.finished.delay(this.options.duration+100,this);
			return false;
		}
		var cnt = (len >= disolveCount) ? disolveCount : len;

		for(i = 1; i<= cnt; i++){
			var idx = this.current.shift();
			var el = this.elements[idx];

			if(this.fade) el.morph({ 'opacity' : 1 });
			else el.setStyle('display', 'block');
		}
	},
	
	sweep : function(row,col){
		var el = this.elements[ this.boxes[row][col] ];
		if( el.get('activated') == 'true' ) return false;
		el.set('activated', 'true');
		if(this.fade) el.morph({ 'opacity' : 1 });
		else el.setStyle('display', 'block');
		this.sweepCount += 1;
		
		[{ r : row-1 , c : col }, { r : row , c : col-1 }, { r : row , c : col+1 }, { r : row+1 , c : col }].each(function(sibling){
			if( sibling.r < 0 || sibling.r >= this.rows || sibling.c < 0 || sibling.c >= this.columns) return false;
			if(this.elements[this.boxes[sibling.r][sibling.c]].get('activated') != 'true' ) this.sweep.delay(this.options.delay,this,[sibling.r, sibling.c]);
		},this);
		
		if( this.sweepCount >= this.totalTiles ) this.finished.delay(this.options.duration+100,this);
	},
	
	blinds : function(dir){
		if(dir == 't' || dir == 'b'){
			for(i = this.blindState.l; i <= this.blindState.r; i++ ){
				var el = this.elements[ this.boxes[this.blindState[dir]][i] ];
				if(this.fade) el.morph({ 'opacity' : 1 });
				else el.setStyle('display', 'block');
			}
			if(dir == 't') this.blindState.t++;
			else this.blindState.b--;
		}
		
		if(dir == 'l' || dir == 'r'){
			for(i = this.blindState.t; i <= this.blindState.b; i++){
				var el =this.elements[ this.boxes[i][this.blindState[dir]] ];
				if(this.fade) el.morph({ 'opacity' : 1 });
				else el.setStyle('display', 'block');
			}
			if(dir == 'l') this.blindState.l++;
			else this.blindState.r--;
		}
		
		if( ((this.blindState.b - this.blindState.t) >= 0) && ((this.blindState.r - this.blindState.l) >= 0) )
			this.blinds.delay(this.options.delay,this,dir);
		else this.finished.delay(this.options.duration+100,this);
		
	},
	
	gobble : function(sign,row,col,size){

		for(r = 0; r < size; r++){
			for(c = 0; c < size; c++){
				if( ( (row+r) < this.rows) && ( (col+c) < this.columns) ){
					var el = this.elements[ this.boxes[row+r][col+c] ];
					if(this.fade) el.morph({ 'opacity' : 1 });
					else el.setStyle('display', 'block');
				}
			}
		}

		if( sign == '+' ){
			col += size;
			
			if ( col >= this.columns ){
				col -= size;
				sign = '-';
				row += size;
			}
		} 

		else {
			col -= size;
			if( col < 0 ){
				col += size;
				sign = '+';
				row += size;
			}
		}

		if ( row < this.rows ) this.gobble.delay(this.options.delay,this,[sign,row,col,size]);
		else this.finished.delay(this.options.duration+100,this);
	
	}
	
});

