/* ------------------
| NAMESPACE
------------------ */

Blockster = function(params, indexOfSlideToTurnOn) { 
	
	
	/* ------------------
	| PREP & CONFIG DEFAULTS
	------------------ */
	
	var thiss = this;
	this.conf = {}; //config
	this.conf.blocks = {rows: 10, cols: 10} //how many blocks do you want involved in the transition? (default 3 * 4, = 12)
	this.conf.pause = 4000; //miliseconds between slide transitions
	this.conf.animType = 'fade'; //how the blocks appear - 'simple' or 'fade'
	this.conf.blockAnimSpeed = 50; //miliseconds between each block transition
	this.conf.direction = 'down'; //'up' or 'down'
	this.conf.delay = 0; //'up' or 'down'
	this.conf.added = 0;
	this.conf.loop = -1; //number of time through the loop (or -1, loop forever)
	this.conf.rainfall = false; //number of time through the loop (or -1, loop forever)
	this.conf.shrinking = false;
	this.params = params;
	this.clickActionQueueInt = false;
	
	
	/* ------------------
	| validate params. If anything not set, use defaults. If no or invalid holder, die.
	------------------ */
	
	if (!this.params.holder || $(this.params.holder).length != 1) {
		return false;
	}
	this.params.holder = $(this.params.holder);
	if (!this.params.pause || !parseInt(this.params.pause)) this.params.pause = this.conf.pause;
	if (!this.params.blockAnimSpeed || !parseInt(this.params.blockAnimSpeed)) this.params.blockAnimSpeed = this.conf.blockAnimSpeed;
	if (!this.params.rows || !parseInt(this.params.rows)) this.params.rows = this.conf.blocks.rows;
	if (!this.params.cols || !parseInt(this.params.cols)) this.params.cols = this.conf.blocks.cols;
	if (!this.params.direction ) this.params.direction = this.conf.blocks.direction;
	if (!this.params.loop || !parseInt(this.params.loop)) this.params.loop = this.conf.loop;
	if (!this.params.rainfall) this.params.rainfall = this.conf.rainfall;
	if (!this.params.rainfall) this.params.shrinking = this.conf.shrinking;
	if (!this.params.delay || !parseInt(this.params.delay)) this.params.delay = this.conf.delay;
	if (!this.params.added || !parseInt(this.params.added)) this.params.added = this.conf.added;
	
	
	/* ------------------
	| PREP
	------------------ */

	this.params.holder.dimensions = {
		width: Math.floor(this.params.holder.width()),
		height: Math.floor(this.params.holder.height())
	};

	if(this.params.shrinking)
	{
		this.params.holder.dimensions = {
			width: Math.floor(this.params.holder.width() - 37.5 * (this.params.delay + this.params.blockAnimSpeed * this.params.rows * this.params.cols) / 1000 - this.params.added * this.params.cols) ,
			height: Math.floor(this.params.holder.height()  - 100 * (this.params.delay + this.params.blockAnimSpeed * this.params.rows * this.params.cols) / 1000  - this.params.added * this.params.rows)
		};
	}
	this.params.dimensions = {
		width: Math.ceil(this.params.holder.dimensions.width / this.params.cols) - this.params.added,
		height: Math.ceil(this.params.holder.dimensions.height  / this.params.rows) - this.params.added
	};
	
	
	if (!indexOfSlideToTurnOn) this.params.holder.children('.slides').not(':first-child').hide(); //to start with, hide all slides but first
	

	/* ------------------
	| if params OK, start interval
	------------------ */		
	
	setTimeout(function() {
		if (!indexOfSlideToTurnOn)
		{
			thiss.func();
			thiss.autoInt = setInterval(function() {
				thiss.params.loop -= 1;
				if(thiss.params.loop != 0)
					thiss.func();
				else
					clearInterval(thiss.autoInt);
				
				$(".done").removeClass("oldslide");
	
			},thiss.params.pause + thiss.params.blockAnimSpeed * thiss.params.rows * thiss.params.cols);		
			$(".done").removeClass("oldslide");
		}
		else
			thiss.func(indexOfSlideToTurnOn);
	},this.params.delay);
		
};


/* ------------------
| MAIN FUNC
------------------ */

Blockster.prototype.func = function(indexOfSlideToTurnOn) {


	/* ------------------
	| prep
	------------------ */
	var thiss = this;


	/* ------------------
	| ascertain current and next slides (find :visible <div>, then next is either is prev sibling or, if none, parent's last child)
	------------------ */
	
	var currentSlide = this.params.holder.children('.slides:visible');
	var nextSlide = !indexOfSlideToTurnOn ? currentSlide.nextAll(".slides").length != 0 ? currentSlide.next(".slides") : this.params.holder.children('.slides').first() : this.params.holder.children('.slides').eq(indexOfSlideToTurnOn);

	
	/* ------------------
	| Iterate over number of required blocks to be built. For each...
	------------------ */
	
	for(var u=0; u<(this.params.rows * this.params.cols); u++) {
		
		
		/* ------------------
		| ...DOM-script the block and style/position it (position depends on number of rows/cols and block dimensions, worked out at top)
		| Remember to start each block as hidden, so we can bring them in randomly when all are ready.
		------------------ */
		
		var block = document.createElement('div');
		var left, top;
		$(block)
			.css({
				width: this.params.dimensions.width,
				height: this.params.dimensions.height,
				left: (left = (u % this.params.cols) * this.params.dimensions.width),
				zIndex: 1000,
				top: (top = Math.floor(u / this.params.cols) * this.params.dimensions.height) - .5 * this.params.dimensions.height,
				overflow: 'hidden',
				position: 'absolute'
			})
			.hide()
			.addClass('block');	
		$(block).attr("_top", top);
		
		if(this.params.rainfall)
		{
			$(block).css("top", 0);
			if(thiss.params.direction == 'up')
				$(block).css("top", this.params.holder.dimensions.height);
		}
		else
		{
			$(block).css("top", (top = Math.floor(u / this.params.cols) * this.params.dimensions.height));
		}
			


		/* ------------------
		| ...clone next slide and append it to the block, positioning it so only the right part of it shows (hence overflow:hidden on blocks)
		| Remember to unhide it!
		------------------ */
		
		nextSlide_clone = nextSlide.get(0).cloneNode(true);
		
		$(nextSlide_clone)
			.show()
			.css({
				height: this.params.holder.dimensions.height,
				width: this.params.holder.dimensions.width,
				position: 'relative',
				left: -left,
				top: -(Math.floor(u / this.params.cols) * this.params.dimensions.height)
			});
		$(nextSlide_clone).children("div").children("img")
			.css({
				height: this.params.holder.dimensions.height,
				width: this.params.holder.dimensions.width
			});
		
		$(block).append(nextSlide_clone);
			
		
		/* ------------------
		| ...append it to holder
		------------------ */
		
		this.params.holder.append($(block));
		
		
	}
	
	
	
	
	
	
	
	
	
	/* ------------------
	| ANIMATION - with all blocks built, set an interval to turn them all on, one by one.
	| When all blocks in position, and all have finished anim (if fade rather than simple)
	|	- kill int
	|	- shuffle slides so the one our blocks contain parts of is genuinely topmost
	|	- remove blocks
	------------------ */
	currentSlide.addClass("oldslide");
	this.pad = 0;
	var animInt = setInterval(function() {
	$(".done").removeClass("oldslide");
		if(thiss.params.holder.children('.block:not(:visible)').length > 0) {
			$(".done").removeClass("oldslide");
			var blocks = thiss.params.holder.children('.block:not(:visible)');
			if(thiss.params.rainfall)
			{
				nb = $(blocks.get(!thiss.params.random ? ( thiss.params.direction != 'down' ? 0 : thiss.params.rows * thiss.params.cols - thiss.params.holder.children('.block:visible').length - 1) : Math.floor(Math.random() * blocks.length)));
				nb.hide();
				with(nb) thiss.params.animType == 'simple' ? show() : css("display","block").animate({"top":attr("_top")},thiss.params.blockAnimSpeed);
				nb.children("div").children("img").animate({opacity: 0},1).animate({opacity: 1}, 300)
			}
			else
			{
				with($(blocks.get(!thiss.params.random ? ( thiss.params.direction == 'down' ? 0 : thiss.params.rows * thiss.params.cols - thiss.params.holder.children('.block:visible').length - 1) : Math.floor(Math.random() * blocks.length)))) thiss.params.animType == 'simple' ? show() : fadeIn();
			}
		}
		else if (thiss.params.holder.children('.block:animated').length == 0) {
			clearInterval(animInt);
			setTimeout(function(){
			nextSlide.siblings(".slides").hide();
			nextSlide.show();
			nextSlide.addClass("done");
			thiss.params.holder.children('.block').remove();
			}, 400);
		}
		
	}, thiss.params.blockAnimSpeed);
		
}


/* ------------------
| JUMP - allow user to control Blockster by interrupting auto-int and forcing a particular slide to load. Kills auto-int. If transition currently
| in progress, wait until after finished before acting. If another click logged while waiting, forget previous click (cancel its wait queue)
------------------ */

Blockster.prototype.jump = function(slideIndex) {
	if (this.clickActionQueueInt) { clearInterval(thiss.clickActionQueueInt); thiss.clickActionQueueInt = false; }
	var thiss = this;
	this.clickActionQueueInt = setInterval(function() {
		if (thiss.params.holder.children('.block').length == 0) {
			clearInterval(this.autoInt);
			thiss.func(slideIndex);
			clearInterval(thiss.clickActionQueueInt);
			thiss.clickActionQueueInt = false;
		}
	}, 10);
}

