(function($) {
    /**
 * $ Blackbox
 *
 * opens images inside one big blackbox, centered.
 *
 *
 * TODO:
 *          - Event handling
 *          - Options
 *          - Navigation between sets of links (up, down)
 *          - Key-Mapping
 *          - Refactoring
 */
    $.fn.blackbox = function(options) {

        //object storing the image links and their targets
        /**
     * Helper object storing data and misc stuff for the blackbox
     */
        blackbox.options = $.extend($.fn.blackbox.defaultOptions, options);
        //Creating new blackbox layer
        var layer = new blackbox.layerManager.Layer();
        blackbox.layerHolder.addItem(layer);

        this.each(function(){
            var _this = $(this);
        
            //TODO: for debugging only!!!
        
            //Saving data for the link
            _this.data("bbLinkData", {
                linkIndex : layer.addLink(_this.attr("href"))
            });
            _this.attr("href","#");
            blackbox.utils.logger("info", "added one more link. This is link n°" + _this.data("bbLinkData").linkIndex);

            _this.click(function() {
                var linkID = $(this).data("bbLinkData").linkIndex;
                blackbox.utils.logger("info", "Link clicked! current item no°" + linkID);
                layer.show(linkID);
                return false;
            });
        });
    };



    /*
 *  Blackbox Helper used by the blackbox fn
 */
    var blackbox = blackbox || {};
    /*
 *some statics, class names etc.
 */
    blackbox.statics = {
        LAYER_ID : "Jq-blackbox",
        SLIDER_ID : "Jq-blackbox-slider",
        ELEMENT_CLASSES : {
            layer_class : "bb-layer",
            image_class : "bb-image",
            thumbnail_class: "bb-thumb",
            close_class : "bb-close-link",
            info_class : "bb-info",
            close_x_class : "bb-close-x",
            nav_prev_class : "bb-prev-image",
            nav_next_class : "bb-next-image",
            loader_class : "bb-loader",
            hidden_class : "bb-hidden",
            bb_visible : "bb-visible",
            control_fade: "bb-control-fade",
            slider_class: "bb-slider"
        }

    }
    /*
 * options object
 */
    blackbox.options = {}
    /*
 *default options
 *(public)
 */
    $.fn.blackbox.defaultOptions = {
        bgColor: '#000',
        bgTextColor: '#fff',
        preloadRange: 2,
        imagePadding: 140
    },
    /**
     * The Blackbox factory "class"
     */
    blackbox.layerManager = {
        Layer: function(){
            //get a unique ID for the blackbox
            var uid = blackbox.utils.uid();
            this.setID = uid;
            //the layer element
            var layerElement = blackbox.layerManager.layerHTML(uid);
            $("body").append(layerElement);
            //image manager
            var image = $('#' + blackbox.statics.ELEMENT_CLASSES.image_class + '_' + uid);
            var imageManager = new blackbox.ImageManager(layerElement,image);
            //Creating the link set
            var linkSet = new blackbox.ItemSet();
            //the controls
            var nextControls = layerElement.find("." + blackbox.statics.ELEMENT_CLASSES.nav_next_class);
            var prevControls = layerElement.find("." + blackbox.statics.ELEMENT_CLASSES.nav_prev_class);
            var closeControls = layerElement.find("." + blackbox.statics.ELEMENT_CLASSES.close_class);
            var info_1 = layerElement.find('.bb-info_1');
            var info_2 = layerElement.find('.bb-info_2');
            var info_3 = layerElement.find('.bb-info_3');
            var slider = $("#" + blackbox.statics.SLIDER_ID + '_' + uid);

            var preloadRange = blackbox.options.preloadRange;

            init();
        
            /*public functions*/
            this.show = function(linkID) {
                linkSet.setCurrent(linkID);
                hideNavigation();
                //$("body").addClass(blackbox.statics.ELEMENT_CLASSES.bb_visible);
                layerElement.show("drop","normal",function(){
                    loadContent();
                });
            };

            this.addLink = function(link) {
                $('#thumbs_' + uid).append('<img class="' + blackbox.statics.ELEMENT_CLASSES.thumbnail_class+ '" width="100" src="' + link + '"/>');
                return linkSet.addItem(link);
            };
            function loadContent() {
                var current = linkSet.getCurrent();
                blackbox.utils.logger("log", "loading link " + current);
                imageManager.loadImage(current,function(){
                    blackbox.utils.logger("log", "finished loading");
                    setupScreen();
                });
            };

            function loadNext() {
                linkSet.setANeighbor(1);
                loadContent()
            }
            function loadPrev() {
                linkSet.setANeighbor(-1);
                loadContent()
            }
            function setupScreen() {
                info_1.text("( ");
                info_2.text(linkSet.realPosition());
                info_3.text(' / ' + linkSet.realSize() + ')');
                linkSet.isFirst() ? prevControls.hide() : prevControls.show();
                linkSet.isLast() ? nextControls.hide() : nextControls.show();
                imageManager.preload(linkSet.getNeighbors(preloadRange,1));
                slider.slider('option','max', linkSet.setLength);
                slider.slider('option','value', linkSet.current);
            }
            function hideNavigation() {
                prevControls.hide();
                nextControls.hide();
            }
        
            function init() {
                info_1.css("color",blackbox.options.bgTextColor);
                info_2.css("color",blackbox.options.bgTextColor);
                info_3.css("color",blackbox.options.bgTextColor);
                slider.slider({
                    min: 0,
                    animate: true,
                    step: 1,
                    start: function(event, ui) {
                        startSliding(ui.value);
                    },
                    slide: function(event, ui) {
                        refreshThumb(ui.value);
                    //     $("#thumb").attr("src",linkSet.items[ui.value]);
                    },
                    stop: function(event,ui) {
                        stopSliding(ui.value);
                    }
                });
                closeControls.bind("click", function(){
                    hide();
                });
                nextControls.bind("click", function() {
                    loadNext();
                });
                prevControls.bind("click", function() {
                    loadPrev();
                });
            }
            function startSliding(value) {
                //image.fadeTo("normal",0.3);
            }
            function stopSliding(value) {
                if (value != linkSet.current) {
                    linkSet.setCurrent(value);
                    loadContent();
                }else {
                  //  image.fadeTo("fast",1);
                }
                
            }

            function refreshThumb(value) {
                blackbox.utils.logger("log", "loading Thumbnail" + value);
                $("#thumb_" + uid).attr("src",linkSet.items[value]);
            }
            function hide() {
                imageManager.unload();
                layerElement.slideUp("normal",function() {
                    //$("body").removeClass(blackbox.statics.ELEMENT_CLASSES.bb_visible);
                    });
            }

        },

        layerHTML: function(uid) {
            var bbLayer = $('<div id="' + blackbox.statics.LAYER_ID + '_' + uid + '" class="' + blackbox.statics.ELEMENT_CLASSES.hidden_class + ' ' + blackbox.statics.ELEMENT_CLASSES.layer_class + '" />')
            .css("background-color",blackbox.options.bgColor)
            .append('<div class="' + blackbox.statics.ELEMENT_CLASSES.info_class + '"><span class="' + blackbox.statics.ELEMENT_CLASSES.info_class + '_1"/><span class="' + blackbox.statics.ELEMENT_CLASSES.info_class + '_2"/><span class="' + blackbox.statics.ELEMENT_CLASSES.info_class + '_3"/></div>')
            .append('<div class="' + blackbox.statics.ELEMENT_CLASSES.close_class + ' ' + blackbox.statics.ELEMENT_CLASSES.close_x_class +'"></div>')
            .append('<a class="' + blackbox.statics.ELEMENT_CLASSES.close_class + '"><img id="' + blackbox.statics.ELEMENT_CLASSES.image_class + '_' + uid + '" class="' + blackbox.statics.ELEMENT_CLASSES.image_class + '" alt=""/></a>')
            .append('<div class="' + blackbox.statics.ELEMENT_CLASSES.nav_prev_class +'"></div>')
            .append('<div class="' + blackbox.statics.ELEMENT_CLASSES.nav_next_class +'"></div>')
            .append('<div class="' + blackbox.statics.ELEMENT_CLASSES.loader_class + '"></div>')
            .append('<div class="' + blackbox.statics.ELEMENT_CLASSES.slider_class + '" id="' + blackbox.statics.SLIDER_ID + '_' + uid + '"></div>')
            .append('<div class="bb-thumb-holder" id="thumbs_' + uid + '"></div>');
            //.append('<img id="thumb_' + uid + '" class="' + blackbox.statics.ELEMENT_CLASSES.thumbnail_class+ '" width="100"/>');
            return bbLayer;
        }
    }

    /**
     * Blackbox Utils
     */
    blackbox.utils = {

        /**
         * Calculates recursivly the maximum width/height of the imageSize relatively to the holderSize
         *
         * @param: imageSize (imageSize.x, imageSize.y)
         * @param: holderSize (imageSize.x, imageSize.y)
         */
        trimmImage: function(imageSize,holderSize) {
            //console.log("$ BB: timming image");
            var _multi = 1;
            if (imageSize.h > holderSize.h) {
                _multi = holderSize.h / imageSize.h;
                imageSize.h = holderSize.h;
                imageSize.w = _multi * imageSize.w;
                this.trimmImage(imageSize,holderSize);
            }
            if (imageSize.w > holderSize.w) {
                _multi = holderSize.w / imageSize.w;
                imageSize.w = holderSize.w;
                imageSize.h = _multi * imageSize.h;
                this.trimmImage(imageSize,holderSize);
            }

            return imageSize;

        },
        uid : (
            function(){
                var id=0;
                return function(){
                    return id++ ;
                };
            }
            )(),

        /**
         * Logging to the firebug console if active.
         *
         * !!!May cause errors in other browsers! For testing only.!!!
         * @param: type (log, debug, info, warn, error)
         * @param: msg (message to log)
         */
        logger: function(type, msg) {
            //Set DEBUT = true to activate logging
            var DEBUG = true;
            var PREFIX = "blackbox: ";
            var msg = PREFIX + msg;
            if (!DEBUG) return false;

            switch (type) {
                case "log":
                    console.log(msg);
                    break;
                case "debug":
                    console.debug(msg);
                    break;
                case "info":
                    console.info(msg);
                    break;
                case "warn":
                    console.warn(msg);
                    break;
                case "error":
                    console.error(msg);
                    break;
            }
            return true;
        }
    };

    /**
 *      The Blackbox imageManager
 */
    blackbox.ImageManager = function(holder,image) {
        var _this = this;
        this._imageHolder = holder;
        this._image = image;
        this._loader = $("." + blackbox.statics.ELEMENT_CLASSES.loader_class);
        this.w = 0;
        this.h = 0;
        $(window).resize(function(){
            _this.positionImage(function(){});
        });
        this._image.load(function() {
            _this.w = _this._image.width();
            _this.h = _this._image.height();
            _this._loader.hide();
            _this.positionImage(function() {
                _this._image.fadeTo("fast",1);
            });
        });
    }

    blackbox.ImageManager.prototype.loadImage = function(url,callback) {
        var _this = this;
        this._image.fadeOut("normal",function(){
            _this.unload();
            _this._loader.fadeIn("slow");
            _this._image.attr("src",url);
            callback.call();
        });
    };

    blackbox.ImageManager.prototype.positionImage = function(callback){
        this._image.css("height","").css("width","");
        var sizeTo = blackbox.utils.trimmImage(
        {
            w : this.w,
            h : this.h
        },

        {
            w : this._imageHolder.width()- blackbox.options.imagePadding,
            h : this._imageHolder.height() - blackbox.options.imagePadding
        }
        );
        var _offset = ( this._imageHolder.height() - sizeTo.h ) / 2;
        this._image.css("padding-top",_offset).width(sizeTo.w).height(sizeTo.h);
        callback.call();
    }

    blackbox.ImageManager.prototype.unload = function() {
        this._image.attr("src","");
        this._image.css("height","").css("width","");
    }

    blackbox.ImageManager.prototype.preload = function(urls) {
        for (url in urls) {
            var pre = new Image();
            pre.src = urls[url];
            $(pre).load(function() {
                delete pre;
            });
        }
    }


    /**
     * The blackbox ItemSet storing items and providing some useful methods
     *
     * The list starts with 0, no Array-1 maths needed.
     * Use realSize() and realPosition() for real life numbers (e.g. to display them on the screen)
     */
    blackbox.ItemSet = function() {
        this.items = [];
        this.setLength = -1;
        this.current = -1;
    }

    /**
     * Add an item to the list
     *
     * returns the current list length (starting with zero for the first one)
     */
    blackbox.ItemSet.prototype.addItem = function(item) {
        this.items.push(item);
        return(++this.setLength);
    }

    /**
     *    S E T T E R
     */
    //set the current element by ID
    blackbox.ItemSet.prototype.setCurrent = function(current) {
        this.current = current;
    }

    /**
     * Set one neighbor as the current one
     *
     * use distance to select the neighbor
     */
    blackbox.ItemSet.prototype.setANeighbor = function(distance) {
        var diff = this.current + distance;
        if (this.items[diff] != undefined) {
            this.current = diff;
        }
    }

    /**
     *    G E T T E R
     */
    //get the current element by ID
    blackbox.ItemSet.prototype.getCurrent = function() {
        return(this.items[this.current]);
    }
    /**
     *  get the amount of neighbors you need of the current item
     *
     *  number: amaount of neighbors
     *  start: 1 to start with the left side, -1 to start with the right side of the linkset
     */
    blackbox.ItemSet.prototype.getNeighbors = function(number,start) {
        var _result = [];
        var _number = number;
        var _distance = start;
        var _found = 0;

        var _multi = -1;
        var _items = this.items;
        var _current = this.current;
        findNeighbors();
    
        function findNeighbors() {
            if (_found < _number) {
                _distance *= _multi;
                var diff = _current + _distance;
                if (_items[diff] != undefined) {
                    _result.push( _items[diff] );
                    _found++;
                    if (_distance > 0) _distance++;
                }else {
                    _multi *= -1;
                }
                findNeighbors();
            }else {
                return;
            }
        }
        blackbox.utils.logger("log"," found " + _found + " list elements as neighbors");
        return _result;
    }
    blackbox.ItemSet.prototype.realSize = function() {
        return this.setLength + 1;
    }
    blackbox.ItemSet.prototype.realPosition = function() {
        return this.current + 1;
    }
    blackbox.ItemSet.prototype.isFirst = function() {
        return (this.current == 0);
    }
    blackbox.ItemSet.prototype.isLast = function() {
        return (this.current == this.setLength);
    }

    blackbox.ItemSet.prototype.toString = function() {
        return this.setLength + " Listeneinträge: " + this.items;
    }


    blackbox.layerHolder = new blackbox.ItemSet();


})(jQuery);