/**
 * starScroller v1.4 - jQuery plugin for rotating panels on a page
 *
 * Copyright (c) 2011 Shaun Simmons
 *
 * Dependencies:
 * - jQuery >= 1.6
 * - jQuery pause plugin - allows animations to be paused and resumed
 * - jQuery backgroundPosition plugin - allows background-position to be animated
 */
(function($) {

function Timer(callback, delay) {
    var timerId, start, remaining = delay;

    this.pause = function() {
        window.clearTimeout(timerId);
        remaining -= new Date() - start;
    };

    this.resume = function() {
        start = new Date();
        timerId = window.setTimeout(callback, remaining);
    };

    this.resume();
}

$.extend($.fn, {
	starScroller: function( options ) {
		if (!this.length) {
			options && options.debug && window.console && console.warn("Nothing selected; can't initialize scroller, returning nothing" );
			return;
		}

		// Check if a scroller was already created
		var scroller = $.data(this[0], 'Star.Scroller');
		if (scroller) {
			return scroller;
		}

		scroller = new $.starScroller(options, this[0]);
		$.data(this[0], 'Star.Scroller', scroller);

		return scroller;
	}
	});

$.starScroller = function( options, scroller ) {
	this.settings = $.extend( true, {}, $.starScroller.defaults, options );
	this.currentScroller = scroller;
	this.init();
};

$.extend($.starScroller, {
	defaults: {
		panels: '.panels',              // Selector for element that encompasses panels.

		panelElement: 'div',            // Selector for each panel element.
		panelClass: 'panel',            // Selector for each panel element.

		caption: true,                  // Accepts a jQuery selector or boolean. If boolean true,
		                                // div.caption is appended to the scroller container.

		markers: true,
		markerElement: 'div',
		markerClass: 'marker',
		animateMarker: true,            // Enables marker slide effect for duration of panel.
		halfwayClass: 'halfway',        // Class applied to marker when animation is halfway complete.
		                                // Useful for changing colors.
		activeMarkerClass: 'active',    // Class applied to marker when it becomes active.

		hoverPause: true,				// If enabled, panel rotation is paused so long as
		                                // the mouse is hovered over a panel.

		autoPlay: true,                 // Whether or not to auto-start panel rotation.
		duration: 3,                    // Time in seconds that panels are visible for.
		speed: 400,                     // Speed of animation when moving to next panel.
		debug: false,                   // Enables console debugging.

		// Controls how a panel is transitioned in to view.
		transition: function($panel) {
			var plugin = this;

			plugin.panels.animate({
				left: -$panel.position().left +'px'
			}, {
				queue: false,
				duration: plugin.settings.speed
			});
		}
	},

	prototype: {
		init: function() {
			var plugin = this;
			plugin.isInitializing = true;

			var $currentScroller = $(plugin.currentScroller);

			plugin.panels = $currentScroller.find(plugin.settings.panels);
			plugin.caption = plugin.settings.caption;

			plugin.panelTimer = null;
			plugin.paused = false;  // Attempt at alleviating premature "resume", a seemingly random bug encountered in IE.
			plugin.stopped = null;
			plugin.index = 0;

			// If caption != false and is not an object:
			if (!plugin.caption.length && plugin.caption) {
				plugin.caption = $('<div class="caption"></div>').appendTo($currentScroller);
			}

			if (plugin.settings.markers) {
				var $markers = plugin.getMarkers();
				plugin.markerWidth = $markers.length && $markers.eq(0).width();

				// Marker click:
				$markers.click(function(e) {
					e.preventDefault();
					plugin.settings.debug && window.console && console.info('Clicked marker index '+ $markers.index($(this)));
					plugin.stop();
					plugin.seek($markers.index($(this)), true);
				});
			}

			// Start at first slide & trigger auto-play.
			if (plugin.getPanelCount()) {
				plugin.seek(0);
			}

			if (plugin.settings.autoPlay && plugin.getPanelCount() > 1) {
				plugin._set_timer(0);

				if (plugin.settings.hoverPause) {
					// Pause on mousehover.
					plugin.panels.hover(function() {
						plugin.paused = true;
						plugin.pause();
					}, function() {
						if (plugin.paused)
							plugin.resume();
					});
				}
			}

			plugin.isInitializing = false;
		},

		getPanels: function() {
			return $(this.settings.panelElement + "." + this.settings.panelClass, this.panels);
		},

		getPanelCount: function() {
			return this.getPanels().size();
		},

		/**
		 * Gets the duration (in seconds) that a panel should be displayed for.
		 *
		 * Each panel's html tag may contain a data-duration attribute.
		 * If one is not found, the default duration is returned instead.
		 *
		 * @param   panel  jQuery object or panel index.
		 * @return  int
		 */
		getDurationOfPanel: function(panel) {
			var plugin = this;
			if (typeof panel == 'undefined') {
				return plugin.settings.duration;
			}
			else if (typeof panel == 'number') {
				panel = plugin.getPanels().eq(panel);
			}

			var duration = panel.attr('data-duration') || plugin.settings.duration;
			plugin.settings.debug && window.console && console.info('Panel duration (in seconds): '+ duration);
			duration *= 1000;

			return duration;
		},

		getMarkers: function() {
			var plugin = this;
			return plugin.settings.markers ? $(plugin.settings.markerElement + "." + plugin.settings.markerClass, plugin.currentScroller) : $([]);
		},

		pause: function() {
			var plugin = this;
			plugin.settings.debug && window.console && console.info('Scroller: Pause');
			if (plugin.settings.markers && plugin.settings.animateMarker) plugin.getMarkers().pause();
			plugin.panelTimer && plugin.panelTimer.pause();
		},

		resume: function() {
			var plugin = this;
			if (!plugin.stopped) {
				plugin.settings.debug && window.console && console.info('Scroller: Resume');
				if (plugin.settings.markers && plugin.settings.animateMarker) plugin.getMarkers().resume();
				plugin.panelTimer && plugin.panelTimer.resume();
			}
		},

		stop: function() {
			var plugin = this;
			plugin.settings.debug && window.console && console.info('Scroller: Stop');
			plugin.pause();
			plugin._resetMarkerAnimations(true);
			plugin.stopped = true;
		},

		play: function() {
			this.settings.debug && window.console && console.info('Scroller: Play');
			this.next();
		},

		next: function() {
			var plugin = this;
			plugin.settings.debug && window.console && console.log('-------------------------------');
			plugin.settings.debug && window.console && console.info('Scroller: Moving to next panel');
			plugin.panelTimer && clearTimeout(plugin.panelTimer);
			plugin.seek(plugin.index + 1);
		},

		seek: function(i, noAnimation) {
			var plugin = this;
			plugin.settings.debug && window.console && console.info('Scroller: Seek to '+ i +' '+ (noAnimation ? 'without' : 'with') + ' animation');
			if (i == plugin.getPanelCount()) i = 0;
			plugin.index = i;

			if (noAnimation !== true && plugin.settings.markers && plugin.settings.animateMarker && plugin.getPanelCount() > 1) {
				plugin._startMarkerAnimation(i);
			}
			else if (plugin.settings.markers) {
				plugin.getMarkers()
					.removeClass(plugin.settings.activeMarkerClass)
					.eq(i)
					.addClass(plugin.settings.activeMarkerClass);
			}

			var $panel = plugin.getPanels().eq(i);

			if (plugin.caption)
				plugin.caption.html($panel.attr('data-caption'));

			if (!plugin.isInitializing && noAnimation !== true) {
				plugin.settings.transition.call(plugin, $panel);

				$.when(plugin.panels).done(function() {
					plugin._set_timer(i);
				});
			}
			else
				plugin.panels.css('left', -$panel.position().left +'px');
		},

		_set_timer: function(cur_panel_index) {
			var plugin = this;
			plugin.panelTimer && plugin.panelTimer.pause();
			plugin.panelTimer = new Timer($.proxy(function(){ plugin.next() }, plugin), plugin.getDurationOfPanel(cur_panel_index));
		},

		_resetMarkerAnimations: function(i) {
			var plugin = this;
			if (plugin.settings.markers && plugin.settings.animateMarker) {
				if (typeof i == 'boolean' && i) {
					plugin.getMarkers()
						.css('backgroundPosition', '-'+plugin.markerWidth+'px 0')
						.removeClass(plugin.settings.halfwayClass)
						.removeClass(plugin.settings.activeMarkerClass);
				}
				else if (typeof i == 'undefined') {
					// plugin.getMarkers().css('backgroundPosition', '-'+markerWidth+'px 0');
					plugin.getMarkers()
						.animate({
							backgroundPosition: '('+ ('-'+plugin.markerWidth) +'px 0)'
						}, {
							queue: false,
							duration: 'fast'
						})
						.removeClass(plugin.settings.halfwayClass)
						.removeClass(plugin.settings.activeMarkerClass);
				}
				else {
					// plugin.getMarkers().eq(i).css('backgroundPosition', '-'+markerWidth+'px 0');
					plugin.getMarkers().eq(i)
						.animate({
							backgroundPosition: '('+ ('-'+plugin.markerWidth) +'px 0)'
						}, {
							queue: false,
							duration: 'fast'
						})
						.removeClass(plugin.settings.halfwayClass)
						.removeClass(plugin.settings.activeMarkerClass);
				}
			}
		},

		_startMarkerAnimation: function(i) {
			var plugin = this;
			if (plugin.settings.markers && plugin.settings.animateMarker && plugin.getPanelCount() > 1) {
				var duration, panelDuration;
				duration = panelDuration = plugin.getDurationOfPanel(i);

				var $marker = plugin.getMarkers().eq(i),
					classAdded = false;

				plugin._resetMarkerAnimations();

				$marker
					.stop()
					.animate({
						backgroundPosition: '(0 0)'
					}, {
						queue: false,
						duration: duration,
						easing: 'linear',
						step: function(now, fx) {
							if (!classAdded && fx.state > 0.5) {
								classAdded = true;
								$marker.addClass(plugin.settings.halfwayClass);
							}
						}
					});
			}
		}
	}
});

})(jQuery);
