/*jslint eqeqeq: true, undef: true */
/*global $, jQuery, window, document */

$.fn.infiniteCarousel = function (options) {
	function repeat(str, n) {
		return new Array( n + 1 ).join(str);
	}

	// creating the HTML for the carousel - only touching the DOM once = performance win
	function create_controls(type, itrs, parent) {
		// also adding in ARIA attrs in here
		var controls_html = "<div role='toolbar' class='controls " + type + "'>",
			i = null,
			len = null;
		controls_html += "<a class='prev' href='#'><span>Previous promo</span></a>";
		if (type === "steps") {
			controls_html += "<ol>";
			for (i = 1, len = itrs; i <= len; i = i + 1) {
				controls_html += "<li><a href='#' role='button' aria-controls=" + parent + ">Go to promo " + i + "</a></li>";
			}
			controls_html += "</ol>";
		}
		controls_html += "<a class='next' href='#'><span>Next promo</a></span></div>";
		
		return controls_html;
	}
	
	// plugin defaults
	$.fn.infiniteCarousel.defaults = {
		timeout: false,
		control_style: "steps",
		fade_in_controls: false
	};
	
	return this.each(function () {
		// collecting variables for use through-out the script
		var $wrapper = $(this).closest('div').css('overflow', 'hidden'),
			// adding ARIA attrs for added accessibility on those UA that support it (IE8 and FF3+)
			$slider = $wrapper.children('ul:first').width(9999).attr('role', 'listbox'),
			$items = $slider.children('li').attr('role', 'option'),
			$single = $items.filter(':first'),
			// variables
			opts = $.extend({}, $.fn.infiniteCarousel.defaults, options),
			id = $(this).attr('id'),
			singleWidth = $single.outerWidth(),
			visible = Math.ceil($wrapper.width() / singleWidth),
			currentPage = 1,
			pages = Math.ceil($items.length / visible),
			current_text = "<em> (current panel)</em>",
			$step_controls = null,
			scroll_me = true;
			
		// 1. pad the pages with empty element if required
		if ($items.length % visible !== 0) {
			// pad
			$slider.append(repeat('<li class="empty" />', visible - ($items.length % visible)));
			$items = $slider.find('> li');
		}
		
		// 2. create the carousel padding on left and right (cloned)
		$items.filter(':first').before($items.slice(-visible).clone().addClass('cloned'));
		$items.filter(':last').after($items.slice(0, visible).clone().addClass('cloned'));
		$items = $slider.find('> li');
		
		// 3. reset the scroll now we've got the dupes
		$wrapper.scrollLeft(singleWidth * visible);
		
		// 4. paging function
		function gotoPage(page) {
		
			var dir = page < currentPage ? -1 : 1,
				n = Math.abs(currentPage - page),
				left = singleWidth * dir * visible * n;
			
			$wrapper.filter(':not(:animated)').animate({
				scrollLeft : '+=' + left
			}, 500, function () {
				// if page == last page - then reset position
				if (page > pages) {
					$wrapper.scrollLeft(singleWidth * visible);
					page = 1;
				} else if (page === 0) {
					page = pages;
					$wrapper.scrollLeft(singleWidth * visible * pages);
				}
				
				currentPage = page;

				// styling up the current step if steps are being used 
				if (opts.control_style === "steps") {
					$wrapper.parent().find('ol a.current').removeClass('current').find('em').remove();
					$wrapper.parent().find('ol a:eq(' + (page - 1) + ')').addClass('current').append(current_text);
				}
			});
		}
		
		// 5. insert the back and forward link
		$wrapper.before(create_controls(opts.control_style, pages, id));

		// 6. binding the controls
		$wrapper.parent().find('.controls .prev').click(function () {
			gotoPage(currentPage - 1);
			return false;
		});
		
		$wrapper.parent().find('.controls .next').click(function () {
			gotoPage(currentPage + 1);
			return false;
		});
				
		// setting up the step controls when they are required
		if (opts.control_style === "steps") {
			$step_controls = $('.steps ol a');
			$step_controls.filter(':first').addClass('current').append(current_text);
			$step_controls.click(function () {
				var index = $step_controls.index(this) + 1;
				gotoPage(index);
				return false;
			});
		}
		
		// if we want to fade them in we'll hide them now
		if (opts.fade_in_controls) {
			$wrapper.parent().find('.controls').hide().fadeIn('slow');
		}
			
		// 7. Set up the auto scroll - if it's been requested
		if (opts.timeout) {
			// boolean to assign whether we move the carousel in the interval or not
			// if we're interacting with the carousel we leave the hell alone
			$wrapper.parent().hover(
				function () {
					scroll_me = false;
				},
				function () {
					scroll_me = true;
				}
			);
			
			window.setInterval(function () {
				if (scroll_me) {
					gotoPage(currentPage + 1);
				}
			}, opts.timeout);
		}
	});
};

