(function($){
  $.fn.extend({
    slideShow: function(options) {
      var defaults = {
        delay: 4000,
        animateTime: 500,
        easing: 'swing',
        slideContainer: '.slideContainer',
        slideFrame: '.slideFrame',
        navContainer: '.navContainer',
        mouseOverPauses: true,
        navStopsAutoAdvance: false
      };
        
      var o = $.extend(defaults, options);
      
      // if child selectors was not passed as an array, make it one
      if (o.children && o.children.length > 0) {
        for (var i = 0; i < o.children.length; i++) {
          if (!o.children[i].selector.length || o.children[i].selector.length < 1) {
            o.children[i].selector = [o.children[i].selector];
          }
        }
      }
    
      var ret_val = this.each(function() {
        var context = this;
        var slide_width = $(o.slideContainer + ' > a > div', context).width();
        var count = $(o.slideContainer + ' > a > div', context).length;
        var timer = false;
        var pos = 0;
        var dest = false;
        var animating = false;
        var hover_count = 0;
        var user_click = false;
        var any_clicks = false;
        
        $(o.navContainer + " a:first", context).addClass("active");
        $(o.navContainer, context).show();
        
        var slideChildren = function(out_slide, in_slide) {
          if (o.children && o.children.length && o.children.length > 0) {

            for (var i = 0; i < o.children.length; i++) {
              var child = o.children[i];
              
              // CSS for child animations
              // c = curent slide, n = next slide
              // s = start values, e = end values
              var c_s_css = {};
              var c_e_css = {};
              var n_s_css = {};
              var n_e_css = {};
              
              c_s_css[child.animate.p] = child.animate.b;
              c_e_css[child.animate.p] = child.animate.a;
              
              n_s_css[child.animate.p] = child.animate.c;
              n_e_css[child.animate.p] = child.animate.b;
              
              for (var l = 0; l < child.selector.length; l++) {
                var slide_child_out = $(o.slideFrame + ' ' + child.selector[l] + '.slideNumber' + out_slide, context);
                var slide_child_in = $(o.slideFrame + ' ' + child.selector[l] + '.slideNumber' + in_slide, context);
                
                slide_child_out.each(function() {
                  var that = this;
                  var a_len = child.animateTime && child.animateTime <= o.animateTime ? child.animateTime : o.animateTime;
                  var wait = child.wait && child.wait.out ? child.wait.out : child.wait;
                  var easing = child.easing ? child.easing : o.easing; 
                  var oa = function() { $(that).css(c_s_css).stop().animate(c_e_css, a_len, easing); };
                  if (child.wait > 0) { setTimeout(oa, wait); } else { oa(); }
                });
                
                slide_child_in.each(function() {
                  var that = this;
                  var a_len = child.animateTime && child.animateTime <= o.animateTime ? child.animateTime : o.animateTime;
                  var wait = child.wait && child.wait['in'] ? child.wait['in'] : child.wait;
                  var easing = child.easing ? child.easing : o.easing;
                  var oa = function() { $(that).css(n_s_css).stop().animate(n_e_css, a_len, easing); };
                  if (child.wait > 0) { setTimeout(oa, wait); } else { oa(); }
                });
              }
            }
          }
        }; // slideChildren()
        
        var rotate = function() {
          if (animating || (hover_count > 0 && !user_click)) { return; }
          
          animating = true;
          clearTimeout(timer);
          
          var cur = pos;
  
          if (dest !== false) {
            pos = dest;
            dest = false;
          } else {
            pos++;
            if (pos >= count) {
              pos = 0;
            }
          }
          
          var old_slide = $('#slide' + (cur + 1), context);
          var new_slide = $('#slide' + (pos + 1), context);
          
          //$(o.slideContainer).prepend(new_slide).prepend(old_slide);
        
          $(o.navContainer + " a", context).removeClass('active');
          $(o.navContainer + ' a:eq(' + pos + ')', context).addClass('active').css({'left':0});
          
          var new_left = -slide_width;

          if (o.opacity && ('enter' in o.opacity) && ('on' in o.opacity) && ('exit' in o.opacity)) {
            //jQuery(old_slide).children('div:eq(0)').fadeTo(0, o.opacity.on, function() {
            $(old_slide).fadeTo(0, o.opacity.on, function() {
              $(this).fadeTo(o.animateTime, o.opacity.exit);
            });
            //jQuery(new_slide).children('div:eq(0)').fadeTo(0, o.opacity.enter, function() {
            $(new_slide).fadeTo(0, o.opacity.enter, function() {
              $(this).fadeTo(o.animateTime, o.opacity.on);
            });
          }
          
          if (typeof o.beforeSlideCallback === 'function') { o.beforeSlideCallback(cur, pos); }
          
          $('.slideFrame').each(function() {
            var slide = $(this);
            var idx = slide.parent().index();
            var new_left = slide_width * 2;
            
            if (idx === cur) {
              new_left = 0;
            } else if (idx === pos) {
              new_left = slide_width;
            }
            
            
            slide.css({'left': new_left,'position':'absolute','display':'block'}).removeClass('oldSlide').removeClass('newSlide');
          });
          
          old_slide.addClass('oldSlide');
          new_slide.addClass('newSlide');
          
          // move the slides
          $(o.slideContainer, context)
            .stop()
            .css({'left':0})
            .animate({'left':new_left}, o.animateTime, o.easing, function() {
              if (typeof o.afterSlideCallback === 'function') { o.afterSlideCallback(cur, pos); }
              if (!any_clicks  || o.navStopsAutoAdvance === false) {
                timer = setTimeout(rotate, o.delay);
              } else {
                clearTimeout(timer);
                timer = false;
              }
              animating = false;
              user_click = false;
            });
          
          slideChildren(cur, pos);
        }; // rotate()
        
        var mouseHoverIn = function() {
          hover_count++;
          if (timer) {
            clearTimeout(timer);
          }
        }; // mouseHoverIn()
        
        var mouseHoverOut = function() {
          hover_count--;
          // only reactivate the timer if we're not hovering over any of the slideshow's children
          if (hover_count === 0) {
            // check if the user has clicked to navigate and whether that should halt the auto-advancing
            if (!any_clicks  || o.navStopsAutoAdvance === false) {
              timer = setTimeout(rotate, o.delay);
            }
          }
        }; // mouseHoverOut()
        
        
        if (o.mouseOverPauses) {
          $('*', context).hover(mouseHoverIn, mouseHoverOut);
        }
        
        // Handle navigation
        $(o.navContainer + ' a', context).click(function() {
          var new_pos = $(this).index();
          if (!animating) {
            if (new_pos !== pos) {
              clearTimeout(timer);
              dest = new_pos;
              user_click = true;
              any_clicks = true;
              rotate();
            }
          }
          return false;
        });
        
        // Relocate all of the animated child overlays to the frame
        if (o.children && o.children.length && o.children.length > 0) {
          for (var i = 0; i < o.children.length; i++) {
            var c = o.children[i];
            $(o.slideContainer + ' > a').each(function() {
              var os_css = {};
              if ($(this).index() === 0) {
                os_css[c.animate.p] = c.animate.b;
              } else {
                os_css[c.animate.p] = c.animate.a;
              }
              for (var l = 0; l < c.selector.length; l++) {
                var ci = $(c.selector[l], this);
                ci
                  .addClass('slideOverlay slideNumber' + $(this).index())
                  .css(os_css)
                  .hover(mouseHoverIn, mouseHoverOut);
              }
            });
          }
        }
        
        timer = setTimeout(rotate, o.delay);  
      });
      
      if (o.postSetupCallback && typeof o.postSetupCallback === 'function') {
        o.postSetupCallback();
      }
      
      
      return ret_val;
    }
  });
})(jQuery);
