(function($) {
  $.fn.okGallery = function(options) {

    // Default Options
    var defaults = {  
      targetImage:   null, // The element that the slide image will replace
      targetCaption: null, // The caption for the given slide
      pager:         null, // The container for the gallery paging
      prevLink:      null, // The next slide link
      nextLink:      null, // The previous slide link
      startSlide:    null, // If you're using a next/prev button you'll need
      endSlide:      null, // to set the startSlide and EndSlide
      spinner:       null, // Element to show while picture is loading
      before:        null, // Function to run before ajax call is made
      after:         null  // Function to run after ajax call is made
    };  

    var settings = $.extend(defaults, options);  
   
    // Plugin Definition
    return this.each(function() {
      $(this).click(function(event){
        event.preventDefault();
        var $link =  event.target.tagName == 'A' ? $(event.target) : $(event.target).parent('a');

        // don't do anything if the clicked link is the active slide or if it has no href
        if (!$link.attr('href') || $link.hasClass('active')) return false;

        $.ajax({
          url: $link.attr('href'),
          dataType: 'json',
          beforeSend: function(){
            hideSlide($link);
          },
          success: function (data) {
            showSlide(data, $link);
          },
          complete: function (data, status) {
            if (settings.spinner && $(settings.spinner).is(':visible'))
              $(settings.spinner).hide();
          }
        });

      })
    });

    function hideSlide($link) {
      $(settings.targetImage).fadeOut('fast', function() {
        if (settings.spinner)
          $(settings.spinner).show();
      });

      if (settings.targetCaption)
        $(settings.targetCaption).html('')

    }

    function showSlide(json, $link) {
      var $image   = $(settings.targetImage);
      var $caption = $(settings.targetCaption);
      var new_img  = new Image(),
          src      = $(json.image).attr('src');

      new_img.onload = function(){
        if (settings.spinner)
          $(settings.spinner).hide();

        $image.attr('src', src).fadeIn();

        if ( settings.targetCaption) {
          $caption.append(json.caption);
          $caption.hide().fadeIn();
        }

        if (settings.after)
          settings.after(json, $link);

        setActiveLink($link);

        if (settings.prevLink && settings.nextLink)
          updatePrevNext($link);
      }

      new_img.src = src;

    } 


    function setActiveLink($link){
      $('.active', settings.pager).removeClass('active');
      $('[href='+ $link.attr('href') +']', settings.pager).addClass('active');
    }

    function updatePrevNext($link){
      var $next      = $(settings.nextLink),
          $prev      = $(settings.prevLink),
          currentPos = extractPosition($link.attr('href'));
        $next.attr('href', setPath($next.attr('href'), currentPos != settings.endSlide ? (currentPos + 1) : currentPos))
        $prev.attr('href', setPath($prev.attr('href'), currentPos != settings.startSlide ? (currentPos - 1) : currentPos))
    }

    // 
    // Utilities
    //

    function setPath(href, position){
      if (/(\d+)(\/)?$/.test(href))
        return href.replace(/(\d+)(\/)?$/, position + "$2") 
      if (/(\/)?$/.test(href))
        return href.replace(/(\/)?$/, "/" + position)
    }

    function extractPosition( href ) {
      if (/^.*(\d+)\/?$/.test(href))
        return parseInt(RegExp.$1);
    }

  };

})(jQuery);
