﻿var Carousel = Class.create();
Carousel.prototype = {
	initialize: function(elem, opts){
		this.elem = $(elem);
		//Element.setStyle(this.elem, 'overflow:hidden;padding-top:100%;')
		this.opts = {
			bufferRequest: 20,
			url: location.href + '?page={0}',
			rows: 1,
			instant: false
		};
		Object.extend(this.opts, opts || {});
		this.currentPage = -1;
		this.highScore = -1;
		this.items = this.elem.getElementsByTagName('li');
		if (this.items.length < this.opts.bufferRequest || this.opts.nomore)
			this.nomore = true;
		this.currentBufferPage = 0;
		this.pageSizes = [];
		this.pageStarts = [];
		this.shifter = new Transition();
		this.instant = true;
		if (this.opts.spinner){
			this.spinner = document.createElement('img');
			this.spinner.src = this.opts.spinner;
			Element.setStyle(this.spinner, 'position:absolute;top:50%;left:50%;margin:-8px 0 0 -8px');
			this.elem.parentNode.style.position = 'relative';
			this.elem.parentNode.appendChild(this.spinner);
		}
		//this.whenLoaded = this.next.bind(this);
		//this.checkImages();
		Event.observe(window, 'load', this.next.bind(this));
	},
	reloadFromGroupId: function(gid){
		if (this.currentPage >= 0){
			this.shiftOver(this.pageStarts[this.currentPage], this.pageStarts[this.currentPage]+this.pageSizes[this.currentPage], -1);
			var removeItems = [];
			for (var i=0;i<this.items.length;i++)
				removeItems.push({
					instant:true,
					elem: this.items[i],
					action: 'remove'
				});
			removeItems.push({instant:true, elem:this.spinner.style, attrib:'display', end:'', suffix: ''})
			this.shifter.doTransition(removeItems);
		}
		this.items = [];
		this.groupId = gid;
		this.currentPage = -1;
		this.highScore = -1;
		this.currentBufferPage = -1;
		this.pageSizes = [];
		this.pageStarts = [];
		this.nomore = false;
		this.whenLoaded = this.nextTry.bind(this);
		this.loading = false;
		this.moreBuffer(true);
		this.working = true;
	},
	nextTry: function(){
		if (this.shifter.moving)
			setTimeout(this.nextTry.bind(this), 200);
		else{
			this.working = false;
			this.next();
		}
	},
	next: function(){
		if (this.shifter.moving || this.working)
			return;

		var nextItem;
		var firstItem = this.currentPage >= 0 ? this.pageStarts[this.currentPage] : 0;
		
		//if we're going into unexplored territory
		if (this.highScore == this.currentPage) {
			var width = [];
			var rowStart = [];
			var start = this.currentPage >= 0 ? this.pageStarts[this.currentPage]+this.pageSizes[this.currentPage] : 0;
			nextItem = start;

			var r;
			//find how many items will be displayed on the next page
			for (r=0;r<this.opts.rows; r++){
				rowStart.push(nextItem);
				for (width.push(0); nextItem < this.items.length ? width[r] + this.items[nextItem].offsetWidth < this.elem.clientWidth : false;)
					width[r] += this.items[nextItem++].offsetWidth;
			}
			rowStart.push(nextItem);
			if (start == nextItem && this.currentPage != -1)
				return;

			if (nextItem >= this.items.length && !this.nomore){
				this.whenLoaded = this.next.bind(this);
				Element.setStyle(this.elem, 'opacity:0.5');
				this.spinner.style.display = 'block';
				this.moreBuffer();
				return;
			}
			
			if (!this.opts.minBuffer)
				this.opts.minBuffer = this.opts.bufferRequest - nextItem;

			//check the buffer's big enough, if not bring some more in
			if (nextItem + this.opts.minBuffer > (this.currentBufferPage + 1) * this.opts.bufferRequest && this.currentPage != -1)
				this.moreBuffer();
			
			//remember the page size and start position for the page we're about to display
			this.pageSizes.push(nextItem - start);
			this.pageStarts.push(start);
			
			//set the spacing for each item that's to be displayed
			var marg = [];
			for (r=0;r<width.length;r++){
				marg.push(this.elem.clientWidth - width[r]);
				marg[r] = marg[r] / ((rowStart[r+1]-rowStart[r]) * 2);
			}
			r = 0;
			var sofar = marg[0];
			//if we're ready for it, get the 
			for (var i=start; i < nextItem; i++) {
				if (i == rowStart[r+1]){
					r++;
					sofar = marg[r];
				}
				if (this.highScore == this.currentPage) {
					this.items[i].style.position = 'absolute';
					this.items[i].style.top = (((this.elem.clientHeight / width.length) - this.items[i].offsetHeight) /2)+(this.elem.clientHeight * r / width.length)+'px';
					this.items[i].style.left = this.elem.clientWidth + sofar + 'px';
				}
				sofar += (marg[r] * 2) + this.items[i].offsetWidth;
			}
		}else
			nextItem = this.pageStarts[this.currentPage+1]+this.pageSizes[this.currentPage+1];

		this.currentPage++;
		this.highScore = Math.max(this.highScore, this.currentPage);

		//start the transition to move each of the items displayed and each that are coming in
		this.spinner.style.display = 'none';
		this.shiftOver(firstItem, nextItem, -1, this.instant);
		this.instant = false;
	},
	previous: function(){
		if (this.shifter.moving)
			return;
		//this should be easier than next
		//all the items should be correctly spaced, we just need to shift them over
		if (this.currentPage > 0) {
			var last = this.pageStarts[this.currentPage] + this.pageSizes[this.currentPage];
			this.currentPage--;
			this.shiftOver(this.pageStarts[this.currentPage], last, 1);
		}
	},
	shiftOver: function(firstItem, lastItem, dir, instant){
		//build the transition array for all the items that are moving - everything moves to the left or right the same amount
		var shifts = [];
		for (var i = firstItem; i < lastItem; i++) 
			shifts.push({
				instant:this.opts.instant || instant,
				elem: this.items[i].style,
				attrib: 'left',
				end: parseInt(this.items[i].style.left) + (this.elem.clientWidth * dir),
				suffix: 'px'
			});
		this.shifter.doTransition(shifts);
	},
	moreBuffer: function(cancelPrev){
		if (this.loading)
			return;
		//trigger the ajax request
		this.currentBufferPage++;
		this.loading = true;
		var cbUrl = this.opts.url.replace('{0}', this.currentBufferPage+1) + (this.groupId ? '&groupId='+this.groupId : '');
		if (this.currentAjaxRequest && cancelPrev){
			this.currentAjaxRequest.options.onSuccess = null;
			this.currentAjaxRequest.transport.abort();
		}
		this.currentAjaxRequest = new Ajax.Request(cbUrl, {
				onSuccess: this.moreBufferComplete.bind(this)
			}
		);
	},
	moreBufferComplete: function(res){
		var temp = document.createElement(this.elem.tagName);
		temp.innerHTML = res.responseText;
		if (temp.childNodes.length < this.opts.bufferRequest)
			this.nomore = true;
		for (;temp.childNodes.length > 0;)
			this.elem.appendChild(temp.childNodes[0]);
		this.items = this.elem.getElementsByTagName('li');
		this.checkImages();
		this.currentAjaxRequest = null;
	},
	checkImages: function(){
		var alldone = true;
		var imgs = this.elem.getElementsByTagName('img');
		for (var i=0;i<imgs.length;i++)
      		alldone = alldone && imgs[i].complete;
		if (alldone){
			this.loading = false;
			if (this.whenLoaded){
				Element.setStyle(this.elem, 'opacity:1');
				this.whenLoaded();
				this.whenLoaded = null;
			}
		} else {
			setTimeout(this.checkImages.bind(this), 200);
		}
	}
}