var lux = (function ($) {

    var copyArguments = function (args) {
        var array = new Array();
        for (var i = 0; i < args.length; i++) {
            array[i] = args[i];
        }
        return array;
    };
    
    var wrapConsole = function (method) {
        if (console && console[method]) {
            method = console[method];
            return function () {
                var args = copyArguments(arguments);
                return method.apply(console, args);
            }
        } else {
            return function () { };
        }
    };

    var lux = { };
    lux.log = wrapConsole('log');
    lux.debug = wrapConsole('debug');

    lux.when = function (condition, callback) {
        if (condition()) {
            callback();
            return undefined;
        } else { 
            setTimeout(function () { lux.when(condition, callback); }, 250);
        }
    };

    lux.defClass = function (constructor, superclass) {
        var klass = function () {
            var me = this;
            var args = copyArguments(arguments);
            args.unshift($);
            args.unshift(me);
            if (superclass) {
                superclass.constructor.apply(me, args);
            }
            constructor.apply(me, args);
        };
        klass.constructor = constructor;
        klass.subClass = function (sub) {
            return lux.defClass(sub, klass);
        };
        return klass;
    };

    var _scale = function(x, in_min, out_min, in_max, out_max, easing) {
        return out_min + easing( (x - in_min) / (in_max - in_min) ) * (out_max - out_min);
    };

    lux.linearScale = function(x, in_min, out_min, in_max, out_max) {
        return _scale(x, in_min, out_min, in_max, out_max, 
                      function (x) { return x; });
    };

    lux.parabolicScale = function(x, in_min, out_min, in_max, out_max) {
        return _scale(x, in_min, out_min, in_max, out_max, 
                      function (x) { 
                          var sqrt_2 = 1.414; // Math.sqrt(2);
                          var sqrt_0_5 = 0.707; // Math.sqrt(0.5);
                          if (x < 0.5) {
                              return 0.5 - Math.pow(sqrt_2 * x - sqrt_0_5, 2);
                          } else {
                              return 0.5 + Math.pow(sqrt_2 * x - sqrt_0_5, 2);
                          }
                      });
    };

    lux.cubicScale = function(x, in_min, out_min, in_max, out_max) {
        return _scale(x, in_min, out_min, in_max, out_max, 
                      function (x) { 
                          return 0.5 + 4 * Math.pow(x - 0.5, 3);
                      });
    };

    lux.ceil = function (value, divider) {
        if (divider === undefined) { divider = 1.0; }
        return Math.ceil(value / divider) * divider;
    };

    return lux;

})(jQuery);

(function ($) {
    $.browser.language = (navigator.language || navigator.userLanguage) || "";

    $.fn.centerLeft = function (left) {
        if (arguments.length == 1) {
            left = parseInt(left);
            $(this).each(function () {
                $(this).css('left', Math.round(left - $(this).outerWidth() / 2) + 'px');
            });
            return this;
        } else {
            return Math.round(parseInt($(this).css('left')) + $(this).outerWidth() / 2);
        }
    };

    $.fn.centerTop = function (top) {
        if (arguments.length == 1) {
            top = parseInt(top);
            $(this).each(function () {
                $(this).css('top', Math.round(top - $(this).outerHeight() / 2) + 'px');
            });
            return this;
        } else {
            return Math.round(parseInt($(this).css('top')) + $(this).outerHeight() / 2);
        }
    };

    $.fn.center = function (center) {
        if (arguments.length == 1) {
            if (center.left !== undefined) { $(this).centerLeft(center.left); }
            if (center.top !== undefined) { $(this).centerTop(center.top); }
            return this;
        } else {
            return { left: $(this).centerLeft(),
                     top: $(this).centerTop() };
        }
    };

    var scrollBarSize = undefined;

    $.fn.scrollBarSize = function () {
        if (scrollBarSize === undefined) { 
            var body = $('body');
            var currentOverflow = body.css('overflow');
            body.css('overflow','hidden');
            var widthWithoutScroll = body.width();
            var heightWithoutScroll = body.height();
            body.css('overflow','scroll');
            var widthWithScroll = body.width();
            var heightWithScroll = body.height();
            body.css('overflow',currentOverflow);

            scrollBarSize = {
                width: widthWithoutScroll - widthWithScroll,
                height: heightWithoutScroll - heightWithScroll 
            };
        }
        return scrollBarSize;
    };

    $(window).resize(function () { scrollBarSize = undefined; });

    $.fn.clickPreventDefault = function (callback) {
        return $(this).click(function (e) {
            var ret = callback(e);
            e.preventDefault();
            return ret;
        });
    };

    $.fn.stop = function () {
        if (window.stop !== undefined) {
            window.stop();
        } else if (document.execCommand !== undefined) {
            document.execCommand('Stop',false);
        }
    };

    $.some = function (array, condition) {
        var some = false;
        $.each(array, function (i, object) {
            some = some || condition(object);
        });
        return some;
    };

})(jQuery);

lux.BufferedImageLoader = lux.defClass(function (me, $, url, href) {
    me.buffer = new Image();
    me.buffer.src = url;

    me.node = $("<div class='buffered-image'> </div>");

    me.node.addClass('loading');
    me.node.append($("<div class='loading-image'> </div>"));
    me.link = $("<a> </a>");
    me.link.attr('href', href || url);
    me.node.append(me.link);

    me.cropBox = $("<div class='crop-box'> </div>");
    me.cropBox.css({ position: 'relative' });
    me.cropPos = $("<div class='crop-pos'> </div>");
    me.cropPos.css({ position: 'absolute',
                     top: '0px',
                     left: '0px' });
    me.cropBox.append(me.cropPos);
    me.link.append(me.cropBox);
    
    /* The shifting and unshifting here is because we want to ensure
     * that the whenLoaded callbacks are called in the order they were
     * created (at not the order their timeouts happen to run in) */
    me.whenLoadedStack = [ ]; // a queue of the collected callbacks
    me.is_loaded = function () {
        return me.buffer.complete;
    };
    me.whenLoaded = function (callback) {
        me.whenLoadedStack.push(callback);
        lux.when(function () { return me.is_loaded(); }, 
                 function () { 
                     // call all the callbacks in the order
                     // me.whenLoaded was called. the other timeouts
                     // will run and simply do nothing. we're ok with
                     // this (tracknig them and canceling them at this
                     // point isn't worth the effort)
                     var c;
                     while ((c = me.whenLoadedStack.shift()) !== undefined) {
                         c(me);
                     }
                 });
        return me;
    };

    me.whenLoaded(function() {
        me.originalHeight = me.buffer.height;
        me.originalWidth  = me.buffer.width;
        me.node.removeClass('loading');
        me.cropPos.append(me.buffer);
        return me;
    });

    me.fitToBox = function (width, height) {
        me.cropPos.css({ left: '0px',
                         top: '0px' });
        me.cropBox.css({ width: 'auto',
                         height: 'auto',
                         overflow: 'visible' });
        var scaleFactor = 1;
        if (width) {
            scaleFactor = width / me.originalWidth;
        }
        if (height && height < me.originalHeight * scaleFactor) {
            scaleFactor = height / me.originalHeight;
        }
        var newWidth = me.originalWidth * scaleFactor;
        var newHeight = me.originalHeight * scaleFactor;
        me.width(newWidth);
        me.height(newHeight);
        me.node.css({ width: newWidth + 'px', height: newHeight + 'px' });
        return me;
    };

    me.cropToBox = function (width, height) {
        var scaleFactor = width / me.originalWidth;
        if (height > me.originalHeight * scaleFactor) {
            scaleFactor = height / me.originalHeight;
        }
        var newWidth = Math.ceil(me.originalWidth * scaleFactor);
        var newHeight = Math.ceil(me.originalHeight * scaleFactor);

        me.width(newWidth);
        me.height(newHeight);

        me.cropBox.css({ width: width,
                         height: height,
                         overflow: 'hidden' });

        me.cropPos.css({ left: (width - newWidth) / 2,
                         top:  (height - newHeight) / 2 });

        return me;
    };

    me.width = function (width) {
        if (arguments.length == 1) {
            $(me.buffer).css({ width: width + 'px' });
            return me;
        } else {
            return $(me.buffer).width();
        }
    };

    me.height = function (height) {
        if (arguments.length == 1) {
            $(me.buffer).css({ height: height + 'px' });
            return me;
        } else { 
            return $(me.buffer).height();
        }
    };

    me.setSize = function (width, height) {
        $(me.buffer).css({ width: width + 'px',
                           height: height + 'px' });
        return me;
    };
});

lux.GalleryImage = lux.defClass(function (me, $, div) {
    me.node = $(div);
    me.image = $("> div.image", me.node);
    me.caption = $("> div.caption", me.node);

    var src = $("img", me.image).attr('src');
    var last_slash = src.lastIndexOf("/");
    var last_dot = src.lastIndexOf(".");
    me.basename = src.substring(last_slash + 1, last_dot);
    me.extension = src.substring(last_dot + 1);
    
    me.loadImage = function (directory, extension, href_directory, href_extension) {
        return new lux.BufferedImageLoader(directory + me.basename + '.' + (extension || me.extension), 
                                           href_directory ? (href_directory + me.basename + '.' + (href_extension || me.extension))
                                                          : undefined);
    };

});

lux.Gallery = lux.defClass(function (me, $, root) {
    me.node = $(root);
    me.notes = $("> div.notes", me.node);
    me.notes.hide();

    me.images = $("div.photograph", me.node).map(function (i, element) {
        return new lux.GalleryImage(element);
    });

    me.rollingIndex = function (array, index) {
        while (index < 0 || index >= array.length) {
            if (index >= array.length) {
                index -= array.length;
            }
            if (index < 0) {
                index += array.length;
            }
        }
        return index;
    };

    me.rollingGet = function (array, index) {
        return array[me.rollingIndex(array, index)];
    };

    me.getImage = function (index) {
        return me.rollingGet(me.images, index);
    };

    me.render = function () { };

    me.resize = function () { me.render(); };

    $(window).resize(function () { me.resize(); });
});

