/**
 * @author Jesse Chrestler
 * August 14, 2008
 * @version 0.021
 * Jquery Extension/Plugin Carousel
 */
(function(jQuery){
	//enable or disable to show logging
	var debugMode = false;
	//logger (checks to see if console is available)
	function debug(msg){
		if(typeof console != 'undefined' && window.console && typeof window.console.log != "unknown"){
			if(debugMode)console.log(msg);
		}else{
			console = {log:function(){}};
		}
	}
	function decode(o){
		var a = [];
		for(i in o)a.push(i + "=" + o[i]);
		return "?" + a.join("&");
	}
	jQuery.fn.extend({
		carousel: function(options){
			return this.each(function(){
				//create reference to self
				//build element specific options
				var opts = jQuery.meta ? jQuery.extend({}, options, this.data()) : options;
				//creates a new carousel and initalizes it.
				(new jQuery.carousel(this, opts)).init();
			});
		}
	})
	jQuery.extend({
		carousel: function(element, options){
			var opts = jQuery.extend({}, jQuery.fn.carousel.defaults, (options == undefined) ? {} : options);
			debug(jQuery.fn.carousel.defaults);
			this.element = $(element);
			this.config = opts;
		}
	});
	jQuery.fn.carousel.defaults = {
		 prevButton:'.carousel-prev', 
		 nextButton:'.carousel-next',
		 pageContainer:'.pages',
		 page:'.carousel-page-container li',
		 pageSelectedClass:'selected-button',
		 scroll:3,
		 minWidth:0,
		 loop:false,
		 cycle:true,
		 json:{
		 	root:'',
			autoLoad:false,
			url:'',
			getRecord:function(){},
			onLoad:function(){}
		 },
		 getPage:function(page){return "<li>Page" + page + "</li>";},
		 onBeforePage:function(){},
		 onPage:function(){},
		 onInit:function(){}
	};
	$carousel = jQuery.carousel;
	$carousel.fn = $carousel.prototype = {carousel:'0.21'};
	$carousel.fn.extend = $carousel.extend = jQuery.extend;
	$carousel.fn.extend({
		busy:false,
		direction:"none",
		lastPage:1,
		page:1,
		cache:[],
		direction:null,
		css:{
			child:'carousel-item',
			carousel:'carousel',
			mask:'carousel-mask',
			container:'carousel-container'
		},
		init:function(){
			debug('Initializing Carousel...');
			//adding class for carousel
			this.element.addClass(this.css.carousel);
			//wraping carousel with container
			this.container = this.element.wrap('<div class="' + this.css.container + '"></div>').parent();
			//wraping container with mask
			this.mask = this.container.wrap('<div class="' + this.css.mask + '"></div>').parent();
			var $this = this;
			if(this.config.pageContainer != null){
				window.carousel = this;
			}
			bind = {};
			bind[this.config.nextButton] = {afterSelect:function(){carousel.next();}};
			bind[this.config.prevButton] = {afterSelect:function(){carousel.prev();}};
			binding.add(bind);
			if(this.config.json.autoLoad){
				this.load();
			}else{
				this.refresh();
			}
			this.config.onInit(this);
		},
		clearCache:function(){
			for(i in this.cache){
				this.cache[i].remove();
			}
			if (this.rebind) {
				this.children.children("a").removeAttr("bind");
	  		binding.bind();
	  	}
			this.cache = [];
		},
		scrollToNextPosition:function(){
			debug('Scrolling to '+ this.direction + ' position.');
			var $this = this;
			var s = this.config.scroll;
			s = s * (this.page-1);
			this.children = this.element.children();
			if (this.config.cycle) {
				var cs = this.config.scroll;
				var diff = Math.abs(this.page - this.lastPage);
				if(this.pages > 3)
					if(this.lastPage > this.pages - 3 && this.page < 3){
						cs = ((this.pages - this.lastPage) + this.page) * cs;
					}else{
						cs = (diff == 0) ? 0 : diff*cs;
					}
				if((this.page == this.pages && this.direction == "prev") || (this.page == 1 && this.direction == "next")){
					cs -= (this.pages * this.config.scroll) - this.count;
				}
				var method, id, left, start = null;
				left = (-$(this.children[cs]).position().left);
				if(this.direction == "next"){
					method = "append";
					start = 0;
					index = 0;
				}else if (this.direction == "prev"){
					method = "prepend";
					index = this.count - 1;
					this.container
					start = left;
					left = 0;
				}
				
				if (this.direction != undefined && this.direction != "none") {
					for (var i = 0; i < cs; i++) {
						var el = $(this.children[Math.abs(index - i)]);
						var html = null;
						this.rebind = false;
						try{
							html = el.clone(true);
						}catch(e){
							html = el.clone();
							this.rebind = true;
						}
						this.element[method](html);
						this.cache.push(el);
					}
					
					this.container.css({
						left: start
					})
					this.container.animate({
						left: left
					}, function(){
						$this.clearCache();
						if ($this.direction == "next") 
							$this.container.css({
								left: start
							});
						$this.busy = false;
					});
				}else{
					this.busy = false;
				}
				this.config.onPage(this, this.direction, $(this.children[cs]), cs);
			}
			else {
				if (s < this.count) {
					debug('Firing onPage event.');
					this.config.onPage(this, this.direction, $(this.children[s]));
					debug('Scrolling to child [' + s + '] of [' + this.count +  '].');
					var pos = $(this.children[s]).position();
					var left = (-pos.left);
					this.container.animate({
						left: left
					}, function(){
						$this.busy = false;
					});
				}
			}
		},
		next:function(num){
			this.incrementCurrentPage((num != undefined) ? num : 1, "next");
		},
		prev:function(num){
			this.incrementCurrentPage((num != undefined) ? -num : -1, "prev");
		},
		refresh:function(){
			//debug('Refreshing carousel data.');
			this.updatePages();
			this.updateWidth();
			jQuery("a").bind("focus", function(){jQuery(this).blur();})
			if(this.pages < 2){
				$(this.config.prevButton).hide();
				$(this.config.nextButton).hide();
				$(this.config.page).hide();
			}else{
				this.setCurrentPage(1);
				$(this.config.prevButton).show();
				$(this.config.nextButton).show();
				$(this.config.page).show();
			}
		},
		empty:function(){
			debug('Empting carousel items...');
			this.element.children().remove();
			return this;
		},
		add:function(html){
			debug('Adding carousel item...');
			this.element.append(html);
			return this;
		},
		load:function(o){
			//o.root - root level for the resultset.
			o = $.extend({}, this.config.json, o);
			var params = $.extend({}, this.config.json.params, o.params);
			$this = this;
			$.getJSON(o.url + decode(params), function(data){
				$this.data = data;
				$this.empty();
				
				if(data.recordcount > 0){
					var root = o.root.split('.');
					var rs = data;
					for(var i in root)rs = rs[root[i]];
					debug("JSON data loaded. Found (" + rs.length + ") records.");
					for (var index = 0; index < rs.length; index++) {
						$this.add(o.getRecord($this, data, index, rs[index]));
					}
				}else{
					$this.add("<li><div style='width:300px;margin:60px 20px 20px 20px;'><h3>No results matched your search criteria.</h3></div></li>")
				}
				$this.refresh();
				$this.setCurrentPage(1);
				o.onLoad($this, data);
			});
		},
		incrementCurrentPage:function(num, dir){
			debug('Attempting to increment current from:' + this.page + " to:" + (this.page + num) + " direction:" + this.direction);
			this.setCurrentPage(this.page + num, dir);
		},
		setCurrentPage:function(page, dir){
			if ((page <= this.pages && page > 0) || this.config.loop || this.config.cycle) {
				if (!this.busy) {
					this.busy = true;
					this.lastPage = this.page;
					this.page = page % this.pages;
					this.page = (this.page == 0) ? this.pages : this.page;
					var pageIndex = this.page - 1;
					if(this.pageCarousel != undefined){
						var ps = this.page / this.pageCarousel.config.scroll;
						var ps = Math.round((ps > Math.round(ps)) ? ps + 1 : ps);
						if(this.pageCarousel.page != ps){
							this.pageCarousel.setCurrentPage(ps);
						}
						if(this.pageCarousel.config.cycle)
							pageIndex = (this.page-1) % this.pageCarousel.config.scroll;
					}
					$(this.config.pageContainer + " ." + this.config.pageSelectedClass).removeClass(this.config.pageSelectedClass);
					$($(this.config.page)[pageIndex]).addClass(this.config.pageSelectedClass);
					if (dir == undefined) {
						if (this.lastPage > this.page) 
							this.direction = "prev";
						else if (this.lastPage < this.page) 
							this.direction = "next";
						else 
							this.direction = "none";
							
					}else{
						this.direction = dir;
					}
					debug('Current Page:' + this.page);
					this.scrollToNextPosition();
				}else{
					debug('Carousel is too busy to complete request.')
				}
			}else {
				debug('Failed to scroll ' + this.direction + ', page out of bounds.');
			}
			debug("page:"+this.page);
		},
		findChildren:function(){
			debug('Counting Children...');
			this.children = this.element.children();
			this.children.addClass(this.css.child)
			this.children.each(function(index){
				$(this).attr({item:index+1})
			})
			this.count = this.children.length;
			debug('Found ' + this.count + ' children!')
		},
		updatePages: function(){
			debug('Counting Pages...');
			this.page = 1;
			this.findChildren();
			var $this = this;
			this.pages = this.count / this.config.scroll;
			this.pages = Math.round((Math.round(this.pages) < this.pages) ? this.pages + 1 : this.pages);
			if (this.config.pageContainer != null) {
				$(this.config.pageContainer).empty();
				$(this.config.pageContainer).append("<ul class='carousel-page-container'></ul>");
				var pc = $(this.config.pageContainer).children(".carousel-page-container");
				for (var page = 0; page < this.pages; page++) {
					pc.append(this.config.getPage(page+1));
					$(pc.children()[page]).attr({page:page+1});
				}
				pc.carousel({
					scroll: 5,
					cycle:false,
					loop:false,
					pageContainer: null,
					onInit:function(carousel){
						$this.pageCarousel = carousel;
						$($this.config.page).bind('click', function(){
							$this.pageClick(this);
							return false;
						});
					}
				});
			}
			debug('Found ' + this.pages + " pages!");
		},
		pageClick:function(el){
			var page = $(el).attr("page");
			this.setCurrentPage(page);
		},
		updateWidth:function(){
			debug('Updating Carousel Width....');
			var width = 0;
			this.children.each(function(){
				width += $(this).outerWidth();
			})
			this.width = Math.round(width * 2);
			if(this.width < this.config.minWidth){
				this.width = this.config.minWidth;
			}
			this.container.css({width:this.width});
			debug('Setting Carousel Width to ' + this.width + "px.");
			return this.width;
		}
	})
})(jQuery);