(function($)
{
  var settings = {
    type: 'clickthru',
    style: 'single',
    effect: 'fade',
    template: $('#slide_template'),

    //default for auto slideshows
    delay: 3000
  };
  
  var methods = {
    init: function(options)
    {
      return this.each(function()
      {
        var $this = $(this),
          data = $this.data('slideshow'),
          prefix = $(this).attr('id') + '_slide';

        if (options)
        {
          $.extend(settings, options);
        }

        $this.data('slideshow', {
          slides: settings.slides,
          position: {
            top: $this.position().top,
            left: $this.position().left
          }
        });

        if (settings.style == 'filmstrip')
        {
          var $wrap = $this.children(),
            first_slide = $wrap.children();

          settings.previousAnchor.hide();
          first_slide.addClass(prefix).addClass(prefix + '_0');
          $wrap.width(first_slide.outerWidth(true));

          for (var i = 1; i < $this.data('slideshow').slides.length; i++)
          {
            _loadSlide.call(this, i);
          }
        }
        else
        {
          if ($this.children().length)
          {
            //wrap child elements
            $this.children().wrapAll('<div class="' + prefix + ' ' + prefix + '_0"/>');

            //position the first slide
            var first_slide = $('.' + prefix + '_0');
            first_slide.css({
              'position': 'absolute',
              'top': '0px',
              'left': '0px',
              'width': $this.width() + 'px'
            });
          }
        }

        $this.data('slideshow').currentSlide = 0;
        _setEvents.call(this);
      });
    },

    next: function()
    {
      return this.each(function()
      {
        //go to next slide
        var $this = $(this),
          data = $this.data('slideshow');

        data.forward = true;

        if (settings.style == 'filmstrip')
        {
          _doTransition.call(this, data.currentSlide + 1);
        }
        else
        {
          if (data.currentSlide == data.slides.length - 1)
          {
            _gotoSlide.call(this, 0, true);
          }
          else
          {
            _gotoSlide.call(this, data.currentSlide + 1, true);
          }
        }
      });
    },

    previous: function()
    {
      return this.each(function()
      {
        //go to previous slide
        var $this = $(this),
          data = $this.data('slideshow');

        data.forward = false;

        if (settings.style == 'filmstrip')
        {
          _doTransition.call(this, data.currentSlide - 1);
        }
        else
        {
          if (data.currentSlide == 0)
          {
            _gotoSlide.call(this, data.slides.length - 1, false);
          }
          else
          {
            _gotoSlide.call(this, data.currentSlide - 1, false);
          }
        }
      });

    },

    gotoSlide: function(index)
    {
      var $this = $(this),
        data = $this.data('slideshow');

      if (data.currentSlide !== index)
      {
        data.forward = null;
        _gotoSlide.call(this, index);
      }
    }
  };

  function _setEvents()
  {
    var $this = $this;
    
    if (settings.type == 'clickthru')
    {
      //set event handlers
      settings.previousAnchor.click({ slideshow: this }, function(event)
      {
        var $slideshow = $(event.data.slideshow);

        if (!$slideshow.find(':animated').length)
        {
          $(event.data.slideshow).slideshow('previous');
        }
        
        return false;
      });

      settings.nextAnchor.click({ slideshow: this }, function(event)
      {
        var $slideshow = $(event.data.slideshow);

        if (!$slideshow.find(':animated').length)
        {
          $(event.data.slideshow).slideshow('next');
        }

        return false;
      });

      $(window).keyup({
        slideshow: this
      }, function(event)
      {
        var $slideshow = $(event.data.slideshow);

        if ($slideshow.is(':visible') && !$slideshow.find(':animated').length && (event.which == 37 || event.which == 39))
        {
          if (event.which == 37)
          {
            $slideshow.slideshow('previous');
          }
          else
          {
            $slideshow.slideshow('next');
          }
        }
      });
    }
    else
    {
      //set interval
      window.setInterval(function(slideshow)
      {
        $(slideshow).slideshow('next');
      }, settings.delay, this);
    }
  }

  function _gotoSlide(index)
  {
    var $this = $(this),
      prefix = $this.attr('id') + '_slide';

    var replacement = $('.' + prefix + '_' + index);

    if (!replacement.length)
    {
      _loadSlide.call(this, index);
    }
    else
    {
      _doTransition.call(this, index, replacement);
    }
  }

  function _loadSlide(index)
  {
      var $this = $(this),
        data = $this.data('slideshow'),
        prefix = $this.attr('id') + '_slide';

      if (settings.style == 'filmstrip')
      {
        var $wrap = $this.children();
        $('#slide_template').tmpl(data.slides[index]).addClass(prefix).addClass(prefix + '_' + index).addClass('inactive').appendTo($wrap);

        var $slide = $('.' + prefix + '_' + index);
        $wrap.width($wrap.width() + $slide.outerWidth(true));
      }
      else
      {
        var slide = $('<div class="' + prefix + ' ' + prefix + '_' + index + '"/>').css('visibility', 'hidden');
        $('<img src="' + data.slides[index].file + '"/>').load({
          slideshow: this,
          slideData: data.slides[index],
          slideIndex: index,
          slide: slide
        }, function(event)
        {
          _handleSlideLoaded.call(event.data.slideshow, event.data.slideIndex, event.data.slide);
        });
      }
  }

  function _handleSlideLoaded(index, slide)
  {
    var $this = $(this),
      data = $this.data('slideshow');

    $('#slide_template').tmpl(data.slides[index]).appendTo(slide);
    slide.appendTo($this);
    slide.css({
      'position': 'absolute',
      'top': '0px',
      'left': '0px',
      'width': $this.width() + 'px',
      'visibility': 'inherit'
    });
    slide.hide();

    _doTransition.call($this, index, slide);
  }

  function _doTransition(index, replacement)
  {
    var $this = $(this),
      data = $(this).data('slideshow'),
      prefix = $this.attr('id') + '_slide';

    if (settings.style == 'filmstrip')
    {
      var $wrap = $this.children(),
        current = $('.' + prefix + ':not(.inactive)');

      current.addClass('inactive');
      $('.' + prefix + '_' + index).removeClass('inactive');

      if (data.forward)
      {
        $wrap.animate(
          {
            'left': ($wrap.position().left - current.outerWidth(true)) + 'px'
          },
          'slow'
        );
      }
      else
      {
        $wrap.animate(
          {
            'left': ($wrap.position().left + $('.' + prefix + '_' + index).outerWidth(true)) + 'px'
          },
          'slow'
        );
      }

      if (index == 0)
      {
        settings.previousAnchor.fadeOut();
      }
      else if (index == data.slides.length - 1)
      {
        settings.nextAnchor.fadeOut();
      }
      else
      {
        if (settings.previousAnchor.is(':hidden'))
        {
          settings.previousAnchor.fadeIn();
        }

        if (settings.nextAnchor.is(':hidden'))
        {
          settings.nextAnchor.fadeIn();
        }
      }
    }
    else
    {
      var current = $('.' + prefix + ':visible');

      switch(settings.effect)
      {
        case 'fade':
          current.fadeOut('slow');
          replacement.fadeIn('slow');
          break;

        case 'slide':
          if (data.forward === false)
          {
            current.hide('slide', { direction: 'right' }, 'slow');
            replacement.show('slide', { direction: 'left' }, 'slow');
          }
          else
          {
            current.hide('slide', { direction: 'left' }, 'slow');
            replacement.show('slide', { direction: 'right' }, 'slow');
          }
          break;

        default:
          current.hide();
          replacement.show();
      }
    }

    data.currentSlide = index;
  }

  $.fn.slideshow = function(method)
  {
    if (methods[method])
    {
      return methods[method].apply(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.slideshow');
    }
  };
})(jQuery);
