/*!
 * Karmic Flow 0.1
 * http://www.karmagination.com
 * Released under the MIT, BSD, and GPL Licenses - Choose one that fit your needs
 * Copyright (c) 2009 Kean L. Tan 
 * Start date: 2009-07-20
 * Build date: 2009-09-02
*/
 
(function karmicFlow(JQ){JQ.fn.karmicFlow = function(opts){
	// default options
	opts = JQ.extend({
		container: 'karmic_flow_container',
		slider: 'karmic_flow_slider',
		slides: 'karmic_flow_slides',
		sliding: 'karmic_flow_sliding',
		slide_selected: 'karmic_flow_slide_selected',
		slide_overflow: 'karmic_flow_slide_overflow',
		controller: 'karmic_flow_controller',
		controller_selected: 'karmic_flow_controller_selected',
		next: 'karmic_flow_next_controller',
		prev: 'karmic_flow_prev_controller',
		play: 'karmic_flow_play_controller',
		pause: 'karmic_flow_pause_controller',
		duration: 300,
		timer: 2500,
		auto: false
	}, opts || {});
	
	this.data('opts', opts);
	this.data('cur_index', this.data('cur_index') || 0);

	var playSlide = function(el, auto){
		
		var JQel = JQ(el),
			target_container = JQel.attr('target'),
			JQcontainer = JQ('#'+target_container),
			JQslider = JQcontainer.children(),
			JQcurrent_selected = JQcontainer.find('.'+opts.slide_selected),
			JQallSlides = JQcurrent_selected.parent().children(),
			index = JQallSlides.index(JQcurrent_selected),
			total = JQallSlides.length,
			old_idx = JQcontainer.data('cur_index'),
			duration = JQcontainer.data('opts').duration,
			multiplier = 1;

		
		// -1 means not found.. just quit
		if (index == -1) return false;
		
		if (auto || JQel.hasClass(opts.next))
			index = (index + 1 == total) ? 0 : index + 1;
		else if (JQel.hasClass(opts.prev))
			index = (index == 0) ? total - 1 : index - 1;
			
		JQcontainer.data('cur_index', index);
		
		JQallSlides.removeClass(opts.slide_selected);
		JQallSlides.eq(index).addClass(opts.slide_selected);
		
		var JQcontroller_target = JQ('[href=#'+ JQallSlides.eq(index)[0].id + ']'),
			JQcontroller_siblings = JQ('[target=' + JQcontroller_target.attr('target') + ']');
		
		JQcontroller_siblings.removeClass(opts.controller_selected);
		JQcontroller_target.addClass(opts.controller_selected);
		
		JQslider.addClass(opts.sliding);
		
		if((index == 0 && old_idx == total-1) || (index == total-1 && old_idx == 0)) {
			multiplier = total;
		}
		
		JQslider.stop().animate({
			marginLeft: -1 * JQcurrent_selected.width() * index
		}, duration * multiplier, function(){
			JQslider.removeClass(opts.sliding);	
		});
		
		return JQcontroller_target[0];
	}

	for(var i=0; i < this.length; i++) {
		JQ(this[i]).find('.'+ opts.slides).each(function(){
			var JQdiv = JQ('<div></div>');
			JQdiv
			 .append(this.childNodes)
			 .appendTo(this)
			 .addClass(opts.slide_overflow)
			 .css('height', JQ(this).parent().parent().height());
		});
	}
	
	var isChildOf = function(el, className) {
		var cache = [];
		while(el) {
			cache.push(el);
			el = el.parentNode;
		}
		
		for (var i = 0; i < cache.length; i++)
			if (JQ(cache[i]).hasClass(className))
				return cache[i];
	
		return false;
	}
	
	// first time init, we want delegate all those controllers with different class name so they know their purpose
	// we do not want to delegate twice
	// 3rd, this is faster than live since we know what we are doing here
	var JQdoc = JQ();
	if (!JQdoc.data('karmic_flow_init')) {
		JQdoc.data('karmic_flow_init', 1);

		JQdoc.bind('click', function(e){
			var el = e.target,
				generic_controller = isChildOf(el, opts.controller),
				next_controller = isChildOf(el, opts.next),
				prev_controller = isChildOf(el, opts.prev),
				play_controller = isChildOf(el, opts.play);

			// slide controller
			if (generic_controller) {
				(function generic_controller_function(generic_controller, e) {
					var target_container = JQ(generic_controller).attr('target'),
						JQcontainer = JQ('#' + target_container), 
						JQel = JQ(generic_controller);
						JQfound_slide = JQcontainer.find('#' + generic_controller.hash.substring(1, generic_controller.hash.length)),
						index = JQfound_slide.parent().children().index(JQfound_slide[0]),
						JQslider = JQfound_slide.parent(),
						old_index = JQcontainer.data('cur_index'),
						duration = JQcontainer.data('opts').duration,
						timer = JQcontainer.data('opts').timer;
	
					JQ('[target=' + target_container + ']').removeClass(opts.controller_selected);
					JQel.addClass(opts.controller_selected);
					JQslider.children().removeClass(opts.slide_selected);
					JQfound_slide.addClass(opts.slide_selected);
					JQslider.addClass(opts.sliding);
					JQcontainer.data('cur_index', index);
					
					var marginLeft = -1 * JQfound_slide.width() * index;
					JQslider.stop().animate({ marginLeft: -1 * JQfound_slide.width() * index }, duration * Math.abs(index-old_index), function(){
						JQslider.removeClass(opts.sliding);
						
						if(JQcontainer.data('interval') !== null) {
							JQcontainer.data('player', JQ('.'+opts.play).filter('[target=' + target_container + ']'));
							JQcontainer.data('interval', setInterval(function(){
								JQcontainer.data('player', playSlide(JQcontainer.data('player'), true));
							}, JQcontainer.data('opts').timer));
						}
					});
					
					if(JQcontainer.data('interval') !== null) {
						clearInterval(JQcontainer.data('interval'));
					}
					
					// prevent default and stop propagation
					e.preventDefault();
					e.stopPropagation();
				})(generic_controller, e);
			}
			
			// next button, flawed, should use current selected or will be off
			else if (next_controller || prev_controller) {
				playSlide(next_controller || prev_controller);
				// prevent default and stop propagation
				e.preventDefault();
				e.stopPropagation();
			}
			
			// play button
			else if (play_controller) {
				(function play_controller_function(play_controller, e) {
					var curEl = play_controller,
						JQel = JQ(play_controller),
						target_container = JQ(play_controller).attr('target');
						JQcontainer = JQ('#' + target_container),
						timer = JQcontainer.data('opts').timer;
					
					if(JQcontainer.data('interval') !== null) {
						clearInterval(JQcontainer.data('interval'));
						JQcontainer.data('interval', null);
						JQel.removeClass(opts.pause);
					}
					else {
						JQel.addClass(opts.pause);
						JQcontainer.data('player', curEl);
						JQcontainer.data('interval', setInterval(function(){
							JQcontainer.data('player', playSlide(JQcontainer.data('player'), true));
						}, timer));
					}
					// prevent default and stop propagation
					e.preventDefault();
					e.stopPropagation();
				})(play_controller, e);
			}

			// other normal anchors should not be affected and behave normally so no return false, prevent default or stop propagation here
		});

	}
	
	for (var i=0; i < this.length; i++) {
		var JQcontainer = JQ(this[i]),
			JQslider = JQcontainer.children(),
			JQslides = JQslider.height(JQcontainer.height()).children(),
			timer = JQcontainer.data('opts').timer,
			JQselected_slide = JQslider.find('.' + opts.slide_selected),
			JQplayer = JQ('.'+opts.controller).filter('[href=#'+ JQselected_slide[0].id +']');
			JQselected_slide_controller = JQ('.'+opts.controller).filter('[href=#'+ JQselected_slide[0].id +']'),
			selected_index = JQslides.index(JQselected_slide);
		
		JQselected_slide_controller.addClass(opts.controller_selected);
		JQslider.css({ marginLeft: -1 * JQslides.eq(0).innerWidth() * selected_index });
		JQslider.width(JQslides.length * JQcontainer.width());
		JQslides.width(JQcontainer.width());
		
		if (opts.auto) {
			// find the play button for current container
			var JQplay_button = JQ('.'+opts.play).filter('[target=' + this[i].id + ']');
			
			JQplay_button.addClass(opts.pause);
			// if not found, create one and hide it, we use fake play button to simulate a true button action
			if(!JQplay_button.length)
				JQplay_button = JQ('<a href="#" target="' + this[i].id + '" class="' + opts.next + '">&nbsp;</a>');
			
			JQcontainer.data('player', JQplay_button[0]);
			
			// loop the slides
			JQcontainer.data('interval', setInterval(function(){
				JQcontainer.data('player', playSlide(JQcontainer.data('player'), true));
			}, timer));
		}
	}
	
	// make this plugin chainable
	return this; 

}})(this.jQuery||this.Karma);
