/*
 * KAKART
 * JQuery Carosel
 *
 *
 *
 * Based Upon:
 *
 * Just Another Carousel v1.0
 * http://intrepidstudios.com/projects/jquery-just-another-carousel/
 *
 * Copyright (c) 2009 Kamran Ayub
 *
 * Licensed under the GPL license.
 * http://intrepidstudios.com/projects/jquery-just-another-carousel/#license
 *
**/
(function($) {

	$.fn.jac = function (options) {          
    
		//
		// build main options before element iteration
		//
		var opts = $.extend({}, $.fn.jac.defaults, options);
		
		//
		// iterate and construct the carousel
		//
	    return this.each(function() {
    	
			// the viwport
			var $vp = $(this);
			
			var $vpw = Math.round($vp.width()); 
			
			
			var $c = $vp.children("ul:only-child");
			
			// Globalise
			$.fn.jac.carosel = $c;
			$.fn.jac.vp = $vp;
			$.fn.jac.vpw = $vpw;
			
			// Only support one carousel per viewport
			if($c.length > 1) return;
			
			// element specific options
			var settings = $.meta ? $.extend({}, opts, $vp.data()) : opts;
			// selectors
	    	var sel = {
		        carouselSelector: "." + settings.carouselSelector,
		        childSelector: "." + settings.childSelector,
		        leftArrowSelector: "." + settings.leftArrowSelector,
		        rightArrowSelector: "." + settings.rightArrowSelector
		    };
			
			$(this).extend({ opts: settings });
			
			$.fn.jac.sel = sel;
		
			//
			// Setup CSS
			//
		    $vp
			.addClass("jac")
			.children("ul")
	        .addClass(settings.carouselSelector)
	        .children("li")
	        .addClass(settings.childSelector);
			// carousel wrapper
       		$c.wrapAll(
		        $("<div class='carousel-wrapper'></div>")
		        .css({
		            "overflow":"hidden",
		            "width" : $vpw,
		            "height" : $vp.height(),
		            "position" : "relative"
		        })
		    );
			
			$c.css("width", getCarouselWidth() + "px");							                   						
			
			// Don't bother letting users move stuff if you can see everything
			if(getCarouselWidth() <= $vpw) return;  
			
			// less processing to store a fixed width
			var childWidth = $c.find(sel.childSelector + ":eq(0)").outerWidth(true);
			
			//parseInt($c.find(sel.childSelector + ":eq(0)").css('marginRight'))
			//console.log(childWidth + " is the width. Also: " + $c.find(sel.childSelector + ":eq(0)").outerWidth(true));
		    var kidsPerView = Math.floor($vpw / childWidth); 		   		      

			// 
			// Moves content to center of mouse cursor (where it entered hover)
			//
		    $c
		    .find(sel.childSelector)    
		    .hover(
		        function(e) {            
		            if(!settings.enableMouse) return;            
			
		            // my abs pos to carousel
		            var myAbs;

					if(settings.childSizeFixed) {
						myAbs = $c.children().index(this) * childWidth;
					} else {
						myAbs = Math.round($(this).position().left);
					}

		            // my rel pos to viewport
		            var myRel = myAbs + getCarouselPos();
            
		            // my abs pos to carousel - centered
		            var myAbsC = myAbs + Math.round($(this).width() / 2);

		            // mouse rel to viewport
		            var mouseRelV = Math.round(e.pageX) - $vp.offset().left;

		            // mouse rel to my center
		            var mouseRelC = mouseRelV - myAbsC;

		            // new position to move to
		            var newPos = myAbsC - mouseRelV;
            
		            // new rel position, left
		            var newRelPosL = myAbs - newPos;
		            var newRelPosR = $(this).width() + myAbs - newPos;
			
					// compensate for $'s -1 off
					if(!settings.childFixedSize) newRelPosR += 1;

		            // Keep from going outside the viewport
		            if (newRelPosL <= 0) newPos = newPos + newRelPosL;
		            if (newRelPosR >= $vpw) newPos = newPos + (newRelPosR - $vpw);
			
		            // Stop all animations (smoothly)
		            $c.stop().animate({
		                "left": -newPos
		            }, settings.childSlideSpeed, settings.easingStyle, onMoveFinished);
		        },
		        function(e) {            
		            if(!settings.enableMouse) return;
            
		            $c.stop();
		        });			
    
			// 
			// Handle navigation
			//
		    if(getCarouselWidth() > $vpw) {                                                		
				
				$vp.prepend("<span class='"+settings.leftArrowSelector+"'></span>");
		        $vp.append("<span class='"+settings.rightArrowSelector+"'></span>");
		
		        // left arrow    
		        $vp.find(sel.leftArrowSelector)
		        .html("<a href='javascript:void(0)' title='"+settings.leftText+"'>"+settings.leftText+"</a>")
		        .css("opacity", 1)
		        .find("a")
		        .hover(function() { $(this).parent().fadeTo(settings.fadeSpeed,1); }, function(){ $(this).parent().fadeTo(settings.fadeSpeed,1); })                    
		        .click(function() {
		            var movePos, newPos;
		            newPos = getCarouselPos() + $vpw;
            
		            if(checkReachedEdge("left")) {onMoveFinished(); return;}
            
		            if(!settings.enableMouse && settings.childSizeFixed) {
						// move so we show equal amounts of kids at once
						// if the mouse is enabled, this wouldn't make sense
		                movePos = kidsPerView * childWidth;
		            } else {
						// children are variable, so move viewport width
		                movePos = $vpw;
		            }                    
            	
		            if (newPos > 0) {				
		                movePos = -getCarouselPos();
		            }

				    movePos = movePos + getCarouselPos();

		            $c.stop();
		            $c.animate({
		                "left": movePos
		            }, settings.parentSlideSpeed, settings.easingStyle, onMoveFinished);
		        }); // left
        
		        // right arrow
		        $vp.find(sel.rightArrowSelector)
		        .html("<a href='javascript:void(0)' title='"+settings.rightText+"'>"+settings.rightText+"</a>")
		        .css("opacity", 1)
		        .find("a")       
		        .hover(function() { $(this).parent().fadeTo(settings.fadeSpeed,1); }, function(){ $(this).parent().fadeTo(settings.fadeSpeed,1); })             
		        .click(function() {
		            var movePos, newPos;
		            newPos = -getCarouselPos() + $vpw;

		            if(!settings.enableMouse && settings.childSizeFixed) {
		                movePos = kidsPerView * childWidth;
		            } else {
		                movePos = $vpw;
		            }

			
		            if (newPos >= getCarouselWidth() - $vpw) {
		                movePos = (getCarouselWidth() - $vpw) + getCarouselPos();
		            }			
			
					movePos = movePos - getCarouselPos();
			
		            $c.stop();
		            $c.animate({
		                "left": -movePos
		            }, settings.parentSlideSpeed, settings.easingStyle, onMoveFinished);                
		        }); // next

				// In case its decided that the carousel has a different
				// initial pos
				if(checkReachedEdge("left")) {
					$vp.find(sel.leftArrowSelector).hide();
				}
		
				if(checkReachedEdge("right")) {
					$vp.find(sel.rightArrowSelector).hide();
				}
        
		    } // navigation
		
			// Get the carousel width
			function getCarouselWidth() {
				var w = 0;

				// Get carousel width
				if(settings.childFixedSize) {
					w = $c.find(sel.childSelector).length * childWidth;
				} else {

					$c.find(sel.childSelector).each(function(i) {
						w += parseInt($(this).outerWidth(true));
					});
					
					return w;
				}
				
				// @TODO
				// For some reason, -3 here seems to fix stuff.  Crappy hack				
				//alert('got here');
				return w - 3;
			}
	
			// When animation finishes, checks to see if carousel reached edge,
			// and hides/shows arrows
			var onMoveFinished = function() {
		
				if(checkReachedEdge("left")) {
					$vp.find(sel.leftArrowSelector).hide();
				} else {
					$vp.find(sel.leftArrowSelector).show("normal");
				}
		
				if(checkReachedEdge("right")) {
					$vp.find(sel.rightArrowSelector).hide();
				} else {
					$vp.find(sel.rightArrowSelector).show("normal");
				}
			}
			
			$.fn.jac.onMoveFinished = onMoveFinished;
	
			// Checks to see if the carousel
			// has reached an edge
			function checkReachedEdge(side) {
				switch(side) {
					case "left":
						if(Math.round(getCarouselPos()) >= 0) return true;				
						break;
					case "right":
						var rightEdge = Math.round((getCarouselWidth() - $vpw) + getCarouselPos());

						if(rightEdge <= 0) return true;	
							
						break;			
				}
		
				return false;
			}			
	
			// For some reason, $'s position().left
			// is off by 1 pixel. So we get it from the CSS instead since that's
			// what we're manipulating.
			function getCarouselPos() {
				return getCssPos($c);
			}
    
	    }); // return this.each
	
	}; // jac
	
	//
	// Returns a number for the CSS left position
	//
	function getCssPos(el) {
		var cssPos = $(el).css("left");
		if(cssPos === undefined) {
			return 0;
		}
		
		return Math.round(parseInt(cssPos.replace("px","")));
	}
	
	
	// Extend!
	$.fn.extend({
		get_width: function() {
			
			width = 0;
			$(this).find('ul:only-child .jac-content').each(function() {
				width += parseInt($(this).outerWidth(true));
			});
			
			return width;			
		},
		center: function(identifier, return_function) {
			
			// get carousel
			var $vp = $(this);
			var $vpw = Math.round($vp.width()); 
			var $c = $vp.find('ul:only-child');
			
			if($c.length < 1)
				return;
			
			// work out widths
			var child_selector = '.jac-content';
			var current_position = getCssPos($c);
			var children_width = $c.find(child_selector + ":eq(0)").outerWidth(true);
			var children_per_view = Math.ceil($vpw / children_width);
			var carosel_width = $vp.get_width();
			var carosel_max_right = children_width * ($c.find(child_selector).length - (children_per_view));

			//console.log("max right: %d, cpv: %d, multiple: %d", carosel_max_right, children_per_view, $c.find(child_selector).length - children_per_view);
			//console.log("Carosel Position: %dpx (of %dpx). Children in viewport: %d. Child width: %dpx", current_position, carosel_width, children_per_view, children_width);

			var element_pos = $c.find('li a.' + identifier).parent().position().left;
			var element_order = (element_pos / children_width) + 1;
			//console.log("Left edge of child: %dpx (Position in Carosel: %d)", element_pos, element_order);

			// Now, we have the left edge.  If we move to this pos, it'll be leftmost.  We need to pad it by 1 child.
			padded_left_position = element_pos - children_width;

			// Don't allow the Carosel to move passed 0.
			if(padded_left_position < 0)
				padded_left_position = 0;

			if(padded_left_position > carosel_max_right)
				padded_left_position = carosel_max_right;

			//console.log(padded_left_position, carosel_max_right);

			// Animate the Carousel		
			$c.animate({
				'left': -padded_left_position
			}, $.fn.jac.sel.parentSlideSpeed, $.fn.jac.sel.easingStyle, function() {
				$.fn.jac.onMoveFinished;
				
				// Check Arrows.
				$c.arrows();
				
				// Callback.
				if(typeof return_function == 'function')
					return_function($(this));
			});
		},
		arrows: function() {
			
			$container = $(this).parents('.jac');
			
			if($(this).go_futher('left')) {
				$container.find('.prev').show('normal');
			} else {
				$container.find('.prev').hide();
			}
			
			if($(this).go_futher('right')) {
				$container.find('.next').show('normal');
			} else {
				$container.find('.next').hide();
			}
		},
		go_futher: function(direction) {
			
			// this is the UL
			parent_width = $(this).parents('.jac').width();
			current_width = $(this).width();
			current_position = parseInt($(this).css('left'));
			child_width = $(this).find('.jac-content:eq(0)').outerWidth(true);
			absolute_postion = Math.abs(current_position / child_width);
			
			// How many items are here?  How far can we go?
			expected_children = Math.ceil(parent_width / child_width);
			expected_furthest = $(this).find('.jac-content').length - expected_children;
			
			switch(direction) {
				case 'left': {
					return absolute_postion > 0;
				}
				case 'right': {
					return absolute_postion < expected_furthest;
				}
				default: {
					return true;
				}
			}
		}
	});
	
	
	
	/**
	 * kawebCentreOnElement
	 * Allow us to center on a 'child' of the carosel: this will
	 * only really work nicely when the number of children in the
	 * viewport is an odd number (ideally 3 or 5).
	 *
	 * @author jon@kaweb.co.uk
	 */
	$.fn.jac.kawebCentreOnElement = function(element_name, slider) {
		
		// We don't need to centre if there are 3 or less elements in the viewport.
		if($.fn.jac.carosel.find($.fn.jac.sel.childSelector).length <= 3)
			return;
		
		// Get the current location.
		var current_position = getCssPos($.fn.jac.carosel);
		var children_width = $.fn.jac.carosel.find($.fn.jac.sel.childSelector + ":eq(0)").width();
		var children_per_view = Math.floor($.fn.jac.vpw / children_width);
		var carosel_width = $.fn.jac.carosel.find($.fn.jac.sel.childSelector).length * children_width;
		var carosel_max_right = children_width * ($.fn.jac.carosel.find($.fn.jac.sel.childSelector).length - (children_per_view));
		//console.log("Carosel Position: %dpx (of %dpx). Children in viewport: %d. Child width: %dpx", current_position, carosel_width, children_per_view, children_width);
		
		// Work out the position of the element we need.
		var element_pos = $('.carousel-wrapper ul li a.' + element_name).parent().position().left;
		var element_order = (element_pos / children_width) + 1;
		//console.log("Left edge of child: %dpx (Position in Carosel: %d)", element_pos, element_order);
		
		// Now, we have the left edge.  If we move to this pos, it'll be leftmost.  We need to pad it by 1 child.
		padded_left_position = element_pos - children_width;
		
		// Don't allow the Carosel to move passed 0.
		if(padded_left_position < 0)
			padded_left_position = 0;
			
		if(padded_left_position > carosel_max_right)
			padded_left_position = carosel_max_right;
		
		//console.log(padded_left_position, carosel_max_right);
		
		// Animate
		$.fn.jac.carosel.animate({
            "left": -padded_left_position
        }, $.fn.jac.sel.parentSlideSpeed, $.fn.jac.sel.easingStyle, $.fn.jac.onMoveFinished);
	}
	
	//
	// plugin defaults
	//
	$.fn.jac.defaults = {
        carouselSelector: "carousel",
        childSelector: "jac-content",
        leftArrowSelector: "arrow-left",
        rightArrowSelector: "arrow-right",
        easingStyle: "swing",
   		rightText: "Next",
		leftText: "Previous",
   		childSizeFixed: true,
        childSlideSpeed: 300,        
        parentSlideSpeed: 600,
        fadeSpeed: 300,
        enableMouse: false
    }; // defaults
	
//
// end of closure
//
})(jQuery);