/**
 * (c) Benjamin Falk at Euroweb Internet GmbH
 *
 * @package jquery.headingslider.js
 * @author Benjamin Falk
 * @email falk[at]citrosaft[dot]com
 * @requires jQuery 1.4.2+
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

(function( $ ){

	var methods = {
		/**
		 * init plugin
		 */
		init : function( options ) {
			// default settings
			var settings = {
				oninit:				function(active) {},
				onstart:			function(newActive, oldActive) {},
				oncomplete:			function(newActive, oldActive) {},
				
				interrupt:			true,
				slides:				null,
				timeout:			4000,
				fxDuration:			"fast",
				easing:				false,
				autostart:			true,
				reorder:			true,
				blockOnAnimation:	false
			}
			
			// check for new options to overwrite default settings
			if (typeof options == 'object') {
				settings = $.extend(settings, options);
			}
			
			return this.each(function(){

				// get object and object-data
				var $this = $(this),
					data = $this.data('headingslider');
				
				// read first element, to get to know what elements has to be animated
				if (settings.slides == null) {
					settings.slides = $this.children().first().get(0).tagName;
				}
				
				// check slides-option again, if it is a jQuery-object
				if (typeof settings.slides != 'object' || typeof settings.slides.length != 'number') {
					settings.slides	= $(settings.slides);
				}
				
				// If the plugin hasn't been initialized yet
				if ( ! data ) {
					// add wrapper
					$this.wrapInner('<div class="headingslider-wrap" />');
					$wrapper = $this.children('.headingslider-wrap');
					
					// get children
					$children = $wrapper.children(settings.slides);
					
					// get middle element as active one
					var active = Math.ceil($children.length / 2) - 1;
					var activeLeft = $($children[active]).position().left;
					
					// clone elements
					$children.each(function() {
						$(this).clone().appendTo($wrapper);
					});
					$children = $wrapper.children(settings.slides);
					
					// calculate width of wrapper
					var wrapperWidth = 0;
					var clones = [];
					var i = 0;
					$children.each(function() {
						$(this).data('headingsliderID', i);
						wrapperWidth += $(this).width();
						i++;
					});
					$children = $.merge($children, clones);
					
					// calculate middle element
					var left = $(document).width() / 2;
					left -= activeLeft + ($($children[active]).width() / 2);
					
					// set width and position for wrapper
					$wrapper.css({
						width:	wrapperWidth+'px',
						left:	left+'px'
					});
					
					// start interval, if autostart is enabled
					var interval = false;
					if (settings.autostart) {
						interval	= setInterval(function() { methods.loop($this) }, settings.timeout);
					}
					
					settings.oninit($($children[active]));
					
					// write data into object
					$(this).data('headingslider', {
						target:		$this,
						children:	$children,
						wrapper:	$wrapper,
						settings:	settings,
						active:		active,
						interval:	interval,
						started:	settings.autostart
					});
					
					$(window).resize(function() {
						var data = $this.data('headingslider');
						
						// get list of elements
						$children = data.children;
						
						data.wrapper.stop();
						
						// calculate new position
						var active = data.active;
						var activeLeft = $($children[active]).position().left;
						var left = $(document).width() / 2;
						left -= activeLeft + ($($children[active]).width() / 2);
						
						// restart interval
						if (data.interval) {
							clearInterval(data.interval);
						}
						if (data.started) {
							data.interval	= setInterval(function() { methods.loop($this) }, data.settings.timeout);
							$this.data('headingslider', data);
						}
						
						data.wrapper.css('left', left+'px');
						/*if (data.settings.reorder) {
							methods.reorder($this);
						}*/
					});
				}
			});
		},
		/**
		 * loop function for interval
		 * @param slider
		 */
		loop: function( slider ) {
			if (typeof slider == 'undefined') {
				return;
			}
			
			methods.next(slider, true);
		},
		
		/**
		 * starts slider
		 * @param slider
		 */
		start: function( slider ) {
			if (typeof slider == 'undefined') {
				return;
			}
			var data		= slider.data('headingslider');
			data.interval	= setInterval(function() { methods.loop(slider) }, data.settings.timeout);
			data.started	= true;
			slider.data('headingslider', data);
		},
		
		/**
		 * stops slider
		 * @param slider
		 */
		stop: function( slider ) {
			if (typeof slider == 'undefined') {
				return;
			}
			var data = slider.data('headingslider');
			
			if (data.interval) {
				clearInterval(data.interval);
				data.interval	= false;
				data.started	= false;
				slider.data('headingslider', data);
			}
		},
		
		/**
		 * updates/animates slider
		 * @param slider
		 * @param int oldActive		previouse active index
		 * @param int newActive		new active index
		 * @param bol loop			animation caused by interval?
		 */
		update: function( slider, oldActive, newActive, loop ) {
			if (typeof slider == 'undefined') {
				return;
			}
			if (typeof loop == 'undefined') {
				loop = true;
			}
			var data = slider.data('headingslider');
			
			// get list of elements
			$children = data.children;
			
			// calculate new position
			var active = newActive;
			var activeLeft = $($children[active]).position().left;
			var left = $(document).width() / 2;
			left -= activeLeft + ($($children[active]).width() / 2);
			
			data.active	= active;
			slider.data('headingslider', data);
			
			var newActiveObject	= $($children[active]);
			var oldActiveObject	= $($children[oldActive]);
			
			// stop interval to avoid multiple animations
			if (data.interval) {
				clearInterval(data.interval);
				data.interval = false;
			}
			
			// call user-function for animation-start
			slider.children('.headingslider-active').removeClass('headingslider-active');
			newActiveObject.addClass('headingslider-active');
			data.settings.onstart(newActiveObject, oldActiveObject);
			// stop animations
			if (data.settings.interrupt) {
				data.wrapper.stop();
			}
			// start new animation
			data.wrapper.animate({
				left: left
			}, data.settings.fxDuration, data.settings.easing, function() {
				// restart interval
				if (data.started) {
					data.interval	= setInterval(function() { methods.loop(slider) }, data.settings.timeout);
					slider.data('headingslider', data);
				}
				
				// call user-function for completion
				data.settings.oncomplete(newActiveObject, oldActiveObject);
			});
		},
		
		/**
		 * set next slide
		 * @param slider
		 * @param bol loop		either is this function called by interval or not
		 */
		next: function( slider, loop ) {
			if (typeof slider == 'undefined') {
				return;
			}
			var data = slider.data('headingslider');
			if (typeof loop == 'undefined' || typeof loop == 'object') {
				loop = false;
				if (data.wrapper.is(':animated') && data.settings.blockOnAnimation) {
					return;
				}
			}
			
			// get current active slide
			var current = data.active;
			var next	= current;
			
			// set next slide
			if (current == data.children.length-1) {
				next = 0;
			}
			else {
				next++;
			}
			if (loop && data.interval) {
				clearInterval(data.interval);
				data.interval = false;
				slider.data('headingslider', data);
			}
			
			if (data.settings.reorder) {
				methods.reorder(slider, current, next, 'next');
			}
			
			// animate
			methods.update(slider, current, next, loop);
		},
		
		/**
		 * set previous slide
		 * @param slider
		 * @param bol loop		either is this function called by interval or not
		 */
		prev: function( slider, loop ) {
			if (typeof slider == 'undefined') {
				return;
			}
			var data = slider.data('headingslider');
			if (typeof loop == 'undefined' || typeof loop == 'object') {
				loop = false;
				if (data.wrapper.is(':animated') && data.settings.blockOnAnimation) {
					return;
				}
			}
			
			// get current active slide
			var current = data.active;
			var prev	= current;
			
			// set next slide
			if (current == 0) {
				prev = data.children.length-1;
			}
			else {
				prev--;
			}
			if (loop && data.interval) {
				clearInterval(data.interval);
				data.interval = false;
				slider.data('headingslider', data);
			}
			
			if (data.settings.reorder) {
				methods.reorder(slider, current, prev, 'prev');
			}
			
			// animate
			methods.update(slider, current, prev, loop);
		},
		
		/**
		 * Allows endless loop
		 * @param slider
		 * @param int current		current active element
		 * @param int newActive		new active slider
		 * @param str step			what button was pressed
		 */
		reorder: function( slider, current, newActive, step ) {
			if (typeof slider == 'undefined') {
				return;
			}
			
			var data = slider.data('headingslider');
			data.wrapper.stop();
			
			// get list of elements
			$children = data.children;
			
			// calculate new position
			var active = newActive;
			var activeLeft = $($children[active]).position().left;
			var left = $(document).width() / 2;
			left -= activeLeft + ($($children[active]).width() / 2);
			
			var currentLeft = data.wrapper.position().left;
			
			if (left < 0) {
				var visible = data.wrapper.width() + left;
				var missing = 'right';
			}
			else {
				var visible = slider.width() - left;
				var missing = 'left';
			}
			
			if (visible < slider.width()) {
				if (missing == 'right') {
					var newRight = data.wrapper.children(data.settings.slides).first();
					newRight.appendTo(data.wrapper);
					$children[newRight.data('headingsliderID')] = newRight;
					visible += newRight.width();
					left	+= newRight.width() * 2;
					currentLeft += newRight.width();
				}
				else {
					var newLeft = data.wrapper.children(data.settings.slides).last();
					newLeft.prependTo(data.wrapper);
					visible += newLeft.width();
					left	-= newLeft.width() * 2;
					currentLeft -= newLeft.width();
				}
				
				data.children = $children;
				data.wrapper.css('left', currentLeft+'px');
				slider.data('headingslider', data);
			}
		}
	};

	$.fn.headingslider = function( method ) {

		if ( methods[method] ) {
			return methods[method]( this, Array.prototype.slice.call( arguments, 1 ));
		} else if ( typeof method === 'object' || ! method ) {
			return methods.init.apply( this, arguments );
		} else {
			$.error( 'Method ' +  method + ' does not exist on jQuery.headingslider' );
		}

	};

})( jQuery );
