(function($) {

	// Light7Box jQuery object constructor
	$.fn.light7box = function(opts) {

		// get "real" options merging defaults and 
		// opts given by user
		var options = $.extend($.light7box.defaults, opts);

		// Spawn a light7box for every element we are calling this on
		return this.each(function(){
			new $.light7box(this, options)
		});

	}; // $.fn.light7box()


 	// Light7Box constructor
	$.light7box = function (opts) {
		
		// Global options for this boxView, right into the object
		this.options = $.extend({}, $.light7box.defaults, opts);
		this.init();

	}; // $.light7box()

	$.light7box.defaults = {

        // After this time, if the mouse didnt move, we hide the ui
        idleTime : 3000,

        // Duration (in ms) of all of the animations
        animationsLenght: 750,

        // Show this message if we cant load the image
        errorMessage: "Can't load this image :(",
        
        // Margins where to ignore mouse movements
        ignoreMouseMarginTop: 40,
        ignoreMouseMarginBottom: 50,
        ignoreMouseMarginLeft: 25,
        ignoreMouseMarginRight: 25,
		
		debug : 'false'

	};

	$.light7box.prototype = {
		init : function() {
		    var self = this;
		    
		    self.log("# Called init");

            // Get the DOM ready to show the light7box
            $('body').append(
				self.loadingImage = $('<div id="light7box-loading"></div>'),
				self.container = $('<div id="light7box-container"></div>'),
				self.contentBar = $('<div id="light7box-contentbar"></div>'),
                self.showContentButton = $('<div id="light7box-showcontentbutton">Show info</div>'),
                self.closeButton = $('<div class="light7box-controls" id="light7box-closebutton">Close</div>'),
                self.nextButton = $('<div class="light7box-controls" id="light7box-nextbutton">Next</div>'),
                self.prevButton = $('<div class="light7box-controls" id="light7box-prevbutton">prev</div>'),
                self.errorBox = $('<div id="light7box-errorBox"></div>')
			);
			
			// Append the current image to the container
			self.container.append($("<img src='' />"));
	        self.currentImg = self.container.find('img')[0];
	        
	        // Append the error message to the error box
	        self.errorBox.append(self.options.errorMessage);
            
			// Events: next, prev and close buttons clicks
			self.nextButton.click(function() { if (self.busy) self.loadNext(); });
			self.prevButton.click(function() { if (self.busy) self.loadPrev(); });
			self.closeButton.click(function() { if (self.busy) self._hide(); });

            // Mouseover on the "show content bar"  to ... show the content bar
            self.showContentButton.hover(function() { if (self.busy) self.showContent(); })

            // Mousemove on container and next/prev buttons, to make em move with the pointer
            self.container.mousemove(function(e) { if (self.busy) self.mouseMoved(e); })

            //self.nextButton.mousemove(function(e) { if (self.busy) self.mouseMoved(e); })
            //self.prevButton.mousemove(function(e) { if (self.busy) self.mouseMoved(e); })

      /// BEGIN BY RIK
      /// If we are using an iPad or iPhone, we cannot reclick on the button once we have clicked
      /// once to move it. So in this particular case, instead of a mouseMove, we click instead.
      if((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))) {
        self.nextButton.mousemove(function(e) { if (self.busy) self.loadNext();})
        self.prevButton.mousemove(function(e) { if (self.busy) self.loadPrev();})
      }
      else {
        self.nextButton.mousemove(function(e) { if (self.busy) self.mouseMoved(e); })
        self.prevButton.mousemove(function(e) { if (self.busy) self.mouseMoved(e); })
      }
      /// END RIK
            // On mouseout, start again the idle timer as if it was a mouse move event
            self.contentBar.hover(function() { }, function(e) { if (self.busy) self.startIdleTimer(); })

            // Catch window resize event
    	    $(window).resize(function(){ self.resize(); });

			// Some internal used variables
			self.busy = false;
			self.ul = null;
			self.li = null;
			self.imgs = [];
			self.currentHrefs = [];
			self.n = 0;
			self.current = 0;
			self.hideTimer = null;
			self.showPrevNext = true;
			self.showingContent = false;
			
			// Call a resize straight away, so light7box stuff get positioned
			self.resize();

		    self.log("# Init done");
	    },

	    // Handles the mouse movements: fade out the content bar and reset the
	    // idle timer
	    mouseMoved : function (e) {
	        var self = this;
	        // Content button visible? Fade it out and fade in the showContentButton
	        if (!self.showContentButton.is(":visible")) {
	            self.contentBar.fadeOut(self.options.animationsLength);
			    self.showContentButton.fadeIn(self.options.animationsLength);
		    }
	        
            self.startIdleTimer();
            self.showUI(e);
	    },

        // Will hide the UI after a hideTimer amount of ms, if nothing else
        // happens (mainly mouse movements)
        startIdleTimer : function() {
            var self = this;
            if (self.hideTimer) clearTimeout(self.hideTimer);
            self.hideTimer = setTimeout(function() { self.hideUI(); }, self.options.idleTime);
        },
	    
	    // Show and hide the Loading status indicator
	    showLoading : function() {
	        var self = this;
	        self.loadingImage.fadeIn(self.options.animationsLength);
	    },
	    
	    hideLoading : function() {
	        var self = this;
	        self.loadingImage.fadeOut(self.options.animationsLength);
	    },
	    
	    resize : function () {
	        var self = this;
	            body_h = $(window).height(),
	            body_w = $(window).width(),
	            load_t = parseInt((body_h - $(self.loadingImage).height()) / 2),
	            load_l = parseInt((body_w - $(self.loadingImage).width()) / 2),
	            error_t = parseInt((body_h - $(self.errorBox).height()) / 3 * 2),
	            error_l = parseInt((body_w - $(self.errorBox).width()) / 2);

            // Resize the container, the content bar and the content button
            self.container.width(body_w).height(body_h - self.showContentButton.height());
            self.contentBar.width(body_w);
            self.showContentButton.width(body_w);
            
            // Reposition the loading and error thingies
	        self.loadingImage.css({top: load_t+"px", left: load_l+"px"});
            self.errorBox.css({top: error_t+"px", left: error_l+"px"});

            // Resize the current loaded image, if there's one
	        if (self.busy) {
	            
	            
	            var href = self.currentHrefs[self.current],
                    img = self.imgs[href].img,
                    img_h = img.height,
                    img_w = img.width,
                    cont_h = self.container.height(),
                    cont_w = self.container.width(),
                    div_h = cont_h/img_h,
                    div_w = cont_w/img_w,
                    content = $(self.li[self.current]).find('div').html(),
                    // Get the smaller of the two, and multiply for both
                    // dimensions
                    smaller = Math.min(div_h, div_w);

	            self.log("# Resizing the current image "+href+": "+img_w+"x"+img_h+" // mul by: "+smaller);
                    
                self.currentImg.width = parseInt(img_w*smaller);
                self.currentImg.height = parseInt(img_h*smaller);

                if (self.currentImg.height < cont_h) {
                    var margin = parseInt((cont_h - parseInt(img_h*smaller)) / 2);
                    self.log("# Putting some margin-top to the image: "+margin+" h: "+self.currentImg.height);
                    $(self.currentImg).css({'margin-top': margin+"px"});
                } else {
                    $(self.currentImg).css({'margin-top': 0+"px"});
                }
	            
	        } // if self.busy
	            
	    }, // resize()
	    
	    loadImage : function(index) {
	        var self = this,
	            href = self.currentHrefs[index],
	            obj = self.imgs[href];

            // Dont ever try to overwrite an onload even while the image
            // is already loading .... EVER!
            if (obj.downloading == true) {
                self.log("# Already downloading "+href+", let it alone! ");
                return;
            }

            // Already loaded?? Great! Skip everything and just show it
            if (obj.loaded == true) {
                self.log("# LoadImage "+index+" already loaded! Using that one.. ");
                self.imageLoaded();
                return;
            }
	        
	        obj.downloading = true;
	        
	        self.log("# LoadImage img "+index+" :: "+href);
	        
	        obj.img.onerror = function() {
				obj.img.onerror = null;
				obj.img.onload = null;
				obj.loaded = true;
				obj.error = true;
				obj.downloading = false;
				self.log("# Error loading the image "+href);
                self.imageLoaded();
			};

            if (typeof obj.img.onload != "function") 
    			obj.img.onload = function() {
    				obj.img.onerror = null;
    				obj.img.onload = null;
    				obj.loaded = true;
    				obj.downloading = false;
    				self.log("# Here we are! Image "+href+" loaded!");
                    self.imageLoaded();
    			};

            // Finally set up the .src attr, so the download can begin
            obj.img.src = href;
	        
	        self.log("# LoadImage completed, waiting for "+href+" to get here.");
	    },

	    load : function (id) {
	        var self = this;
	        self.log("# Called load for gallery with id "+id);
	        
	        if (self.busy) return;
	        self.busy = true;
	        
	        self.showLoading();
	        
	        // Read the data from the DOM
	        self.ul = $('#'+id);
	        self.li = self.ul.find('li.light7box');
	        self.n = self.li.length-1;

            self.showPrevNext = (self.n<=0) ? false : true;
	        
	        // Start with the first image, and hide it straight away
	        self.current = 0;

            // Hide the current image (if there's any), and show the rest 
            // of the UI
	        $(self.currentImg).hide();
	        self._show();

            // Take note of all the hrefs we will load, create the imgs objects
            // with the brand new images ready to be loaded
            for (var i = self.li.length - 1; i >= 0; i--) {
                var href = $(self.li[i]).find('a').attr('href');
                self.currentHrefs[i] = href;
                if (typeof self.imgs[href] == "undefined") {
                    self.imgs[href] = {img: new Image(), loaded: false, downloading: false, href: href, error: false};
                    self.imgs[href].img.onload = null;
                } else
                    self.log("# Load: skipping already loaded "+href);
            };

            self.log("# "+(self.n+1)+" images loaded, ready for the show");

            // Start download the first image
            self.loadImage(self.current);

	    }, // load()
	    
	    imageLoaded : function () {
            var self = this,
                href = self.currentHrefs[self.current],
                img = self.imgs[href].img;
                
            if (self.currentImg.src == img.src) {
                self.log("# ImageLoaded: Skipping _LoadIndex of "+img.src+" since we are already displaying it .. ");
                
                // Just to be sure the image is correcly displayed, show it and hide the loading
                $(self.currentImg).show();
                self.hideLoading();

                return;
            }
            
            self.log("# ImageLoaded: loading "+self.current);
            self._loadIndex(self.current);

	    },
	    
	    // Loads a certain image index on the overlay
	    _loadIndex : function (index) {
	        var self = this;
	        //self.log("# Called _loadIndex with index "+index);
            
            var href = self.currentHrefs[index],
                img = self.imgs[href].img,
                img_h = img.height,
                img_w = img.width,
                cont_h = self.container.height(),
                cont_w = self.container.width(),
                div_h = cont_h/img_h,
                div_w = cont_w/img_w,
                content = $(self.li[index]).find('div').html();

            if (self.currentImg.src == img.src) {
                self.log("# Called _LoadIndex of "+img.src+", but we are already displaying it .. skip!");
                self.hideLoading();
                return;
            }

            // Animations: first hide the current image, when this animation is done
            // we can change the .src of the currentImg object, the content of the
            // content bar, resize and finally fadeIn again
            $(self.currentImg).stop().fadeOut(self.options.animationsLength, function() {
    	        self.currentImg.src = img.src;
                self.contentBar.html(content);
                self.current = index;
                self.resize();
                self.hideLoading();
                
                // Errors? Show the error box
                if (self.imgs[href].error == true)
                    $(self.errorBox).fadeIn();
                else
                    $(self.errorBox).fadeOut();
                
            }).fadeIn(self.options.animationsLength, function () {
                // Completed the fadeIn animation, be sure the opacity is 1!
                $(self.currentImg).css({opacity: 1});
            })

            self.startIdleTimer();

            // Try to preload next and previous image, if they are not loaded yet
            var preload = (index < self.n) ? index+1 : 0,
                pre_href = self.currentHrefs[preload],
                pre_loaded = self.imgs[pre_href].loaded;

            if (pre_loaded == false) {
                self.log("# Preloading " + preload);
                self.loadImage(preload);
            }
            
	    }, // _loadIndex()

        // Will set the new .current, show the loading object and call 
        // the load procedure on the new current image
	    loadNext : function() {
	        var self = this;
            self.current = (self.current == self.n) ? 0 : self.current+1;
            self.showLoading();
	        self.loadImage(self.current);
	    },

	    loadPrev : function () {
	        var self = this;
	        self.current = (self.current == 0) ? self.n : self.current - 1;
            self.showLoading();
	        self.loadImage(self.current);
	    },

	    // Shows the content bar
	    showContent : function () {
	        var self = this,
	            show = {display: 'block'},
	            hide = {display: 'none'};

	        if (!self.busy) return;
            
            self.showingContent = true;

	        self.showContentButton.css(hide);
	        self.contentBar.css(show);
	    },

        hideUI : function () {
            var self = this,
	            show = {display: 'block'},
	            hide = {display: 'none'};
	            
	        if (!self.busy) return;
	            
	        $(".light7box-controls").fadeOut(self.options.animationsLength);

            if (!self.showingContent)
		        self.showContentButton.fadeIn(self.options.animationsLength);
			    
        },
        
        showUI : function (e) {
            var self = this,
	            show = {display: 'block'},
	            hide = {display: 'none'},
                // Use clientX/Y so the page under can be scrolled up and down
	            x = e.clientX,
	            y = e.clientY,
	            limX = $(window).width() - self.options.ignoreMouseMarginRight,
	            limY = $(window).height() - self.options.ignoreMouseMarginBottom,
	            middleX = parseInt($(window).width()/2);
            
            // Show the close button
            self.closeButton.css(show);

            if (self.showPrevNext)
                // Are we outside of the ignore area?
                if (x > self.options.ignoreMouseMarginLeft && 
                    x < limX &&
                    y > self.options.ignoreMouseMarginTop &&
                    y < limY) {

                    var newX = x - parseInt(self.prevButton.width()/2),
                        newY = y - parseInt(self.prevButton.height()/2);
                
                    if (x < middleX) {
                        self.nextButton.css(hide);
                        self.prevButton.css(show).css({left: newX+'px', top: newY+'px'});
                    } else {
                        self.prevButton.css(hide);
                        self.nextButton.css(show).css({left: newX+'px', top: newY+'px'});
                    }
                
                // Inside the ignore area, hide next/prev
                } else {
                    self.nextButton.css(hide);
                    self.prevButton.css(hide);
                }
            
        }, // showUI()
	    
	    // Shows the entire overlay along with controls
	    _show : function () {
	        var self = this;
	        var show = {display: 'block'},
	            hide = {display: 'none'};
	        
	        // Show container, content button and the UI
	        self.container.css(show);
	        self.showContentButton.css(show);
	        $(".light7box-controls").css(show);
	        self.contentBar.css(hide);

            // But hide the prev next buttons if needed
            if (!self.showPrevNext) {
                self.nextButton.css(hide);
                self.prevButton.css(hide);
            }

	    },

	    // Hides everything!
	    _hide : function () {
	        var self = this,
	            hide = {display: 'none'};

            // The container, and all the rest
	        self.container.css(hide);
	        self.showContentButton.css(hide);
	        self.loadingImage.css(hide);
	        self.errorBox.css(hide);
            self.contentBar.css(hide);
	        $(".light7box-controls").css(hide);

            // Delete the timer, if it was active
	        if (self.hideTimer) clearTimeout(self.hideTimer);
	        
	        // Ok, we are not busy anymore... 
	        self.busy = false;
	    },
	    
	    log : function(w) {
	        if (this.options.debug == "false")
                return;
            
            if (typeof console == "undefined") {
                if ($("#debug_foo").attr('id') == null) 
                    $("body").append("<div id='debug_foo' style='position: absolute; right:2px; bottom: 2px; border: 1px solid red; font-size: 1.1em;'></div>");
                $("#debug_foo").append("<div>"+w+"</div>");
            } else {
                console.log(w);
            }
	    } // log()
    };

})(jQuery)