;
(function($){
   var WmiGallery = function(element, options)
   {
       var elem = $(element);
       var obj = this;
       var images = {};
       var currentImage = null;
       var slide = null;
       var boundLists = [];
       var firstImage = null;
       var lastImage = null;
       var captionSelector = 'p';
       var timer = null;
       var deadTimer = false;
       var settings = $.extend({
           slide: null,
           boundLists: [],
           captions: true,
           captionSelector: 'p',
           prevElem: null,
           nextElem: null,
           disabledControlClass: 'disabled',
           loopImages: true,
           fadeTime: 500,
           asyncTrans: true,
           activeClass: 'wmi-gallery-selected',
           galleryPrefix: 'wmigal',
           timerInterval: 8000,
           slideshow: false,
           killTimerOnClick: true
           //transitionOut: null, // Not yet implemented
           //transitionIn: null,
           //transitionPre: null,
           //transitionPost: null

       }, options || {});
       
       // Public methods
       
       // Sets up the gallery

       this.init = function()
       {
         slide = settings['slide'];
         boundLists = settings['boundLists'];
         collectImages();
         for (list in boundLists) {        
            initBoundList(boundLists[list]);
         }

         initControls();
         obj.refreshControls();
         obj.refreshBoundLists();
         showCaption();

         if (settings['slideshow']) {
            // if a slideshow, we have to loop images
            settings['loopImages'] = true;
            resetTimer();
         }
         
       };
       
       // Change currentImage to the provided picId
       // and execute the transition effect

       this.showImage = function(picId)
       {
         
         if (currentImage != picId) {
            hideCaption();
            var prevImage = currentImage;
            currentImage = picId; 
            doTransition(prevImage, currentImage);
            this.refreshBoundLists();
            this.refreshControls();
            if (timer && !deadTimer) {
              resetTimer();
            }
         }
       };


       // Move to the next image in the sequence
       //
       this.nextImage = function()
       {

          if (images[currentImage]['next'] != null) {
            var nextImage = images[currentImage]['next'];
          } else if (settings['loopImages'] == true) {
            var nextImage = firstImage;
          }

          if (nextImage != undefined) {
            this.showImage(nextImage);
          }
       };

       this.prevImage = function()
       {
          if (images[currentImage]['prev'] != null) {
            var prevImage = images[currentImage]['prev'];
          } else if (settings['loopImages'] == true) {
            var prevImage = lastImage;
          }

          if (prevImage != undefined) {
            this.showImage(prevImage);
          }
       }

       // Refresh the state of all bound lists
       // Will switch the 'active' class to the element
       // that is passed in. Because this refresh may
       // happen prior to image transition, activeElem
       // may not be equal to currentImage
       this.refreshBoundLists = function(imageId)
       {
          if (!imageId || imageId == undefined) {
            var imageId = currentImage;
          }

         imageId = imageId.replace('#', '');

          for (var i=0; i<boundLists.length; i++) {
            if (boundLists[i]) {
              boundLists[i].find('li').removeClass(settings['activeClass']);
              var activeElem = boundLists[i].find("li a[href='#" + imageId + "']");
              activeElem.parent().addClass(settings['activeClass']);
            }
          }
       }

       this.refreshControls = function(imageId)
       {
         
         if (!imageId || imageId == undefined) {
          var imageId = currentImage;
         }

         imageId = imageId.replace('#', '');

         var disabledClass = settings['disabledControlClass'];
         var prev = settings['prevElem'];
         var next = settings['nextElem'];

         if (prev) {
           prev.removeClass(disabledClass);
           if (!settings['loopImages']) {
             if (imageId == firstImage) {
               prev.addClass(disabledClass);               
             }
           }
         }
         
         if (next) {
           next.removeClass(disabledClass);
           if (!settings['loopImages']) {
             if (imageId == lastImage) {
               next.addClass(disabledClass);               
             }
           }
         } 
       };

       // Private methods
       
       // Loop through the 'slide' elements and 
       // collect all the relevant data

       var collectImages = function()
       {
          var items = slide.find('li');
          for(var i = 0; i<items.length; i++) {
            var item = $(items[i]);
            if (item != undefined) {
              var caption = item.find(settings['captionSelector'] + ':first');
              caption.css({display: 'none'});

              var prefix = settings['galleryPrefix'];
              var picId = getPicId(i);

              if (i == 0) {
                firstImage = picId;
              } else if (i == items.length - 1) {
                lastImage = picId;
              }

              var prev = null;
              var next = null;

              if (imageExists(getPicId(i-1))) {
                prev = getPicId(i-1);
              }

              var nextElem = slide.find('li:nth-child(' + (parseInt(i)+2) + ')')
              if (nextElem.length) {
                next = getPicId(i+1);
              }

              var entry = {
                            slide: item,
                            caption: caption,
                            next: next,
                            prev: prev
                          };

              images[picId] = entry;

              if (item.hasClass(settings['activeClass'])) {
                currentImage = picId;
              }
              
              var imageAnchor = item.children('a:first');
              if (imageAnchor.length) {
                clickEvent('next', imageAnchor, function() {});
              }
            }
          }
       }

       // Generate a unique id to reference images by
       // User can provide custom pre-fix, for using
       // more than one gallery on a page
       
       var getPicId = function(num)
       {
          var prefix = settings['galleryPrefix'];
          return prefix + '-img' + num;
       }

       // Check if image at a given picId
       // is already registered

       var imageExists = function(picId)
       {
          if (images[picId] != undefined) {
              return true;
          }
       }

       // Add picIds and click handlers
       // to list items. Assumes list order
       // is parallel to slide order.
       // Used to bind thumbnails and tick marks 

       var initBoundList = function(elem)
       {
         var items = elem.find('li');
         for(var i=0; i<items.length; i++) {
          var item = $(items[i]);
          var prefix = settings['galleryPrefix'];
          var picId = prefix + '-img' + i;
          if (imageExists(picId)) {
            var anchor = item.find('a:first'); 
            anchor.attr('href', '#' + picId);
            clickEvent('show', anchor, function() {});
          }
         }
       }



       // Intialize controls for prev/next selectors
       // and/or image tick marks

       var initControls = function()
       {
         var prev = settings['prevElem'];
         var next = settings['nextElem'];
         var ticks = settings['controlTicks'];

         if (prev && prev.length) {
            clickEvent('prev', prev, function() {});

         }

         if (prev && next.length) {
            clickEvent('next', next, function() {});
         }

       }

       // Execute the transition from one image
       // to another. Default is fade, unless 
       // custom callbacks are provided

       var doTransition = function(from, to, callback)
       {
          callback = typeof(callback) == 'undefined' ? postTransition : callback;
          var fromElem = $(images[from]['slide']);
          var toElem = $(images[to]['slide']);

          if (!fromElem.length || !toElem.length) {
            return;
         }
        
         fromElem.removeClass(settings['activeClass']);
         toElem.addClass(settings['activeClass']);

         var fadeTime = settings['fadeTime'];

         // TODO: add support for custom transition callbacks;
          
         if (settings['asyncTrans']) {
            fromElem.fadeOut(fadeTime);
            toElem.fadeIn(fadeTime, function() {
                callback();
            });
         } else {
            fromElem.fadeOut(fadeTime, function() {
              toElem.fadeIn(fadeTime, function(){
                  callback();
                });
            });
         }

       }

       // Actions to take after a transition has been completed
       var postTransition = function()
       {
         if (settings['captions']) {
          showCaption();
         }
       }

       // If captions are showing, hide them

       var hideCaption = function()
       {
          if (settings['captions']) {
            var caption = images[currentImage]['caption'];
            caption.css({display: 'none'});
          }
       }

       var showCaption = function() {
          if (settings['captions']) {
            var caption = images[currentImage]['caption'];
            var coords = getSlideOrigin();
            caption.css({
                  position: 'absolute',
                  left: coords[0],
                  bottom: coords[1]
            });
            caption.slideDown(300);
          }
       }


       // Bind a click event to anchor element
       // Either 'show', 'next', or 'prev'
       // Includes a callback executed immediately after the click
       var clickEvent = function(action, anchor, callback)
       {
         anchor.click(function() {

           if (settings['killTimerOnClick']) {
              clearTimeout(timer);
              deadTimer = true;
           }

           callback(($(this)));
           switch(action) {
            case 'show':
              var picId = $(this).attr('href').replace('#', '');
              obj.showImage(picId);
              break;
            case 'next':
              obj.nextImage();
              break;
            case 'prev':
              obj.prevImage();
              break;
           }
           return false;
         });
       }

       var resetTimer = function()
       {
          timer = setTimeout(function() {
            obj.nextImage();
          }, settings['timerInterval']);
       }

       // Get the offset of the bottom left of the slide
       // image from the bottom left of the slide element

       var getSlideOrigin = function()
       {
          var slideElem = images[currentImage]['slide'];
          var imgElem = slideElem.find('img:first');

          var offsetX = slideElem.outerWidth() - imgElem.outerWidth();
          var offsetY = slideElem.outerHeight() - imgElem.outerHeight();

          return [offsetX, offsetY];
       }
   };

   // Plugin constructor
   $.fn.wmiGallery = function(options)
   {
       return this.each(function()
       {
           var element = $(this);
          
           // Return early if this element already has a plugin instance
           if (element.data('wmigallery')) return;

           // pass options to plugin constructor
           var gallery = new WmiGallery(this, options);

           // Store plugin object in this element's data
           element.data('wmigallery', gallery);

           gallery.init();
           
           return this;
       });
   };
})(jQuery);

