/**
 * Lightboxer Class
 *
 * @static
 */
Core.createNamespace('nl.code.lightbox');
nl.code.lightbox.Lightboxer = {
    /**
     * @var Pager, a refrence to this site's pager
     */
    pager: null,

    /**
     * @var boolean, if the lightbox becomes bigger then the visible screen, then make it fit the screen
     */
    fit_screen: false,

    /**
     * @var integer, the depth (z-index) at which the lightboxes are displayed
     */
    zIndex: 800,

    /**
     * @var array
     */
    lightbox_arr: [],

    /**
     * @var array
     */
    visible_lightbox_arr: [],

    /**
     * The Lightboxer has it's own parseLinks method
     * The PageData class does not contain this method because there would be a from of "tight coupling" between Lightboxer and PageData
     * This is a piece of functionality that stands on it's own
     *
     * @param Pager, refrence to this site's pager
     * @param Element, the html element to scan for anchors that refer to a lightbox, defaults to body
     * @return void
     */
    parseLightboxLinks: function(pager, root) {
        // store the reference to the pager in the static attribute
        nl.code.lightbox.Lightboxer.pager = pager;

        // if no Element is provided default to the body element
        if (! $type(root)) {
            root = $(document.body);
        }

        // get all anchors and loop through them
        var anchor_arr = root.getElements('a');
        for (var i = 0; i < anchor_arr.length; i++) {
            // we only need enchors with an rel attribute with a value "lightbox"
            if (anchor_arr[i].get('rel') != 'lightbox') {
                continue;
            }

            // remove all already attached onclick events
            anchor_arr[i].removeEvents('click');

            anchor_arr[i].addEvent('click', function(event) {
                event.stop();

                nl.code.pager.PageData.request(this.get('href'), nl.code.lightbox.Lightboxer);
            });
        }

        // if the user presses "ESC" hide the active lightbox
        document.removeEvents();
        document.addEvent('keydown', function(e) {
            var event = new Event(e);

            if (event.key == 'esc') {
                nl.code.lightbox.Lightboxer.hideLightbox();
            }
        });
    },

    /**
     * JSON request completed
     *
     * @param Object, the response JSON object {'content': content, 'width': width, 'height': height, 'scripts': scripts}
     * @paran string, the response JSON as text
     * @return void
     */
    setContent: function(JSON, text) {
        nl.code.lightbox.Lightboxer.openLightbox(JSON, text);
    },

    /**
     * @param JSON
     * @2retrun void
     */
    openLightbox: function(JSON) {
        // check if the lightbox is already generated and in the cache
        var lightbox = nl.code.lightbox.Lightboxer.findLightbox(JSON.id);

        // create the lightbox if it is not found in the cache
        if (! lightbox) {
            lightbox = nl.code.lightbox.Lightboxer.createLightbox(JSON.id);
        } else {
            // if the lightbox was already in the visible array then remove it
            if (nl.code.lightbox.Lightboxer.visible_lightbox_arr.contains(lightbox) && nl.code.lightbox.Lightboxer.visible_lightbox_arr[nl.code.lightbox.Lightboxer.visible_lightbox_arr.length - 1] != lightbox) {
                nl.code.lightbox.Lightboxer.visible_lightbox_arr.erase(lightbox);
            }
        }

        // set the z-index for the lightbox
        lightbox.setZIndex(nl.code.lightbox.Lightboxer.zIndex++);

        // add the lightbox to the stack array
        nl.code.lightbox.Lightboxer.visible_lightbox_arr.push(lightbox);

        $(document.body).addClass('lightbox');
        lightbox.show(JSON);
    },

    /**
     * Try to find a lightbox in the cache
     *
     * @param string, the id of the lightbox
     * @return Lightbox or false
     */
    findLightbox: function(id) {
        for (var i = 0; i < nl.code.lightbox.Lightboxer.lightbox_arr.length; i++) {
            if (nl.code.lightbox.Lightboxer.lightbox_arr[i].id == id) {
                return nl.code.lightbox.Lightboxer.lightbox_arr[i];
            }
        }

        return false;
    },

    /**
     * Create a lightbox
     *
     * @param integer
     * @return void
     */
    createLightbox: function(id) {
        var lightbox = new nl.code.lightbox.Lightbox(id);

        nl.code.lightbox.Lightboxer.lightbox_arr.push(lightbox);

        return lightbox;
    },

    /**
     * @param int
     * @return int
     */
    calculateXPosition: function(width) {
        var x = ((Window.getWidth().toInt() - width) / 2) + Window.getScrollLeft();

        if (x < 10) {
            x = 10;
        }

        return x;
    },

    /**
     * @param int
     * @return int
     */
    calculateYPosition: function(height) {
        var y = ((Window.getHeight().toInt() - height) / 2) + Window.getScrollTop();

        if (y < 10)  {
            y = 10;
        }

        return y;
    },

    /**
     * Removes the focus of the currently active lightbox
     *
     * @return void
     */
    hideLightbox: function() {
        if (! nl.code.lightbox.Lightboxer.visible_lightbox_arr.length) {
            return;
        }

        var index = nl.code.lightbox.Lightboxer.visible_lightbox_arr.length - 1;

        nl.code.lightbox.Lightboxer.visible_lightbox_arr[index].hide();
        nl.code.lightbox.Lightboxer.visible_lightbox_arr.erase(nl.code.lightbox.Lightboxer.visible_lightbox_arr[index]);

        if (! nl.code.lightbox.Lightboxer.visible_lightbox_arr.length) {
            $(document.body).removeClass('lightbox');
        }
    },

    /**
     * Remove the focus of all lightboxes
     *
     * @return void
     */
    hideAllLightboxes: function() {
        for (var i = 0; i < nl.code.lightbox.Lightboxer.visible_lightbox_arr.length; i++) {
            nl.code.lightbox.Lightboxer.visible_lightbox_arr[i].hide();
        }

        nl.code.lightbox.Lightboxer.visible_lightbox_arr = [];
        $(document.body).removeClass('lightbox');
    }
};

/**
 * Lightbox Class
 */
Core.createNamespace('nl.code.lightbox');
nl.code.lightbox.Lightbox = new Class({
    /**
     * Constructor
     *
     * @param integer, the identifier
     */
    initialize: function(id) {
        this.id = id;

        this.data = null;
        this.visible = false;

        this.container = this.getContainer();

        var thisObject = this;

        // set the style for the canvas
        this.overlay = this.container.getElement('div.overlay');
        this.overlay.setStyle('opacity', 0);
        this.overlay.addEvent('click', function(event) {
            event.stop();

            thisObject.hide();
        });

        // get the canvas
        this.canvas = this.container.getElement('div.canvas');

        // get the content
        this.content = this.container.getElement('div.content');

        // get, if available, the animation object
        try {
            this.animation = new LightboxAnimation(this);
        } catch (e) {
            this.animation = null;
        }

        window.addEvent('resize', function() {
            thisObject.setSize();
        });
    },

    /**
     * @param integer
     * @return void
     */
    setZIndex: function(zIndex) {
        this.container.setStyle('z-index', zIndex);
    },

    /**
     * Show the lightbox
     *
     * @param JSON, the data
     * @return void
     */
    show: function(data) {
        // check if the content must be preloaded to determine the width or height
        if (data.height === 0 || data.width === 0) {
            var preloader = new nl.code.lightbox.Preloader(data, this);
        } else if (this.animation !== null) {
            this.animation.show(data);
        } else {
            this.setSize(data);
            this.setContent(data.content);
            this.container.setStyle('display', 'block');
        }
    },

    /**
     * Set the content of the lightbox
     *
     * @param string
     * @return void
     */
    setContent: function(content) {
        this.content.set('html', content);

        var thisObject = this;
        var anchor_arr = this.content.getElements('a[rel=close]');
        for (var i = 0; i < anchor_arr.length; i++) {
            anchor_arr[i].addEvent('click', function(event) {
                event.stop();

                nl.code.lightbox.Lightboxer.hideLightbox(thisObject.id);
            });
        }

        anchor_arr = this.content.getElements('a');
        for (var i = 0; i < anchor_arr.length; i++) {
            if (anchor_arr[i].get('rel') == 'lightbox') {
                continue;
            }

            if (anchor_arr[i].get('rel') == 'close') {
                continue;
            }

            if (! nl.code.pager.Uri.isInternalUrl(anchor_arr[i])) {
                continue;
            }

            anchor_arr[i].addEvent('click', function(e) {
                nl.code.lightbox.Lightboxer.hideAllLightboxes();
            });
        }

        nl.code.lightbox.Lightboxer.pager.scanContent(this.content);

        this.content.setStyle('display', 'block');
    },

    /**
     * Remove the content of the lightbox
     *
     * @return void
     */
    removeContent: function() {
        this.content.empty();
        this.content.setStyle('display', 'none');
    },

    /**
     * @param JSON
     * @return void
     */
    setSize: function(data) {
        if (!$type(data)) {
            data = {width: this.canvas.getSize().x, height: this.canvas.getSize().y};
        }

        var window_width = Window.getScrollWidth().toInt();
        var window_height = Window.getScrollHeight().toInt();
        var canvas_width = (data.width > (window_width - 100)) ? window_width - 100 : data.width;
        var canvas_height = (data.height > (window_height - 100)) ? window_height - 100 : data.height;

        this.overlay.setStyles({
            width: window_width,
            height: window_height,
            opacity: 0.8
        });

        this.canvas.setStyles({
            width: canvas_width,
            height: canvas_height,
            left: nl.code.lightbox.Lightboxer.calculateXPosition(canvas_width),
            top: nl.code.lightbox.Lightboxer.calculateYPosition(canvas_height)
        });
    },

    /**
     * Hide the lightbox
     *
     * @return void
     */
    hide: function() {
        if (this.animation !== null) {
            this.animation.hide();
        } else {
            this.removeContent();
            this.container.setStyle('display', 'none');
        }
    },

    /**
     * Get the container for the lightbox
     *
     * @return Element
     */
    getContainer: function() {
        var container = $('lightbox');

        // if no container in the document create one
        if (! container) {
            container = this.renderContainer();
        }

        // clone the html, in this way another lightbox can use the same html
        return container.clone().inject($(document.body));
    },

    /**
     * Render the lightbox container
     *
     * @return Element
     */
    renderContainer: function() {
        var container = new Element('div', {'class': 'lightbox', 'id': 'lightbox'});
        var overlay = new Element('div', {'class': 'overlay'});

        // use a canvas element, in this way you can animate the lightbox without showing the content
        var canvas = new Element('div', {'class': 'canvas'});
        var content = new Element('div', {'class': 'content'});

        container.inject($(document.body));
        overlay.inject(container);
        canvas.inject(container);
        content.inject(canvas);

        return container;
    }
});

/**
 * Preloader Class
 */
Core.createNamespace('nl.code.lightbox');
nl.code.lightbox.Preloader = new Class({
    Extends: nl.code.util.Observable,

    /**
     * Constructor
     *
     * @param JSON
     * @param Lightbox
     */
    initialize: function(data, lightbox) {
        this.data = data;
        this.lightbox = lightbox;

        this.images_preloaded = 0;

        this.preload_content = this.createContentContainer();
        this.addImageEvents();
    },

    /**
     * @return void
     */
    addImageEvents: function() {
        var thisObject = this;
        this.preload_image_arr = this.preload_content.getElements('img');
        if (this.preload_image_arr.length) {
            for (var i = 0; i < this.preload_image_arr.length; i++) {
                var preload_image = new Asset.image(this.preload_image_arr[i].src, {
                    onload: function() {
                        thisObject.preloadImage();
                    },
                    onerror: function() {
                        thisObject.preloadImage();
                    },
                    onabort: function() {
                        thisObject.preloadImage();
                    }
                });
            }
        } else {
            this.preloadComplete();
        }
    },

    /**
     * @return Element
     */
    createContentContainer: function() {
        var preload_container = new Element('div', {
            'class': 'lightbox-preload-container'
        });
        preload_container.injectInside(document.body);

        var preload_content = new Element('div', {
            'class': 'lightbox-preload-content'
        });

        preload_content.injectInside(preload_container);
        preload_content.set('html', this.data.content);

        return preload_content;
    },

    /**
     * @return void
     */
    preloadComplete: function() {
        var coordinates = this.preload_content.getCoordinates();

        if (nl.code.lightbox.Lightboxer.fit_screen) {
            this.data.width = (coordinates.width > (Window.getSize().x - 40) ? (Window.getSize().x - 40) : coordinates.width);
            this.data.height = (coordinates.height > (Window.getSize().y - 40) ? (Window.getSize().y - 40) : coordinates.height);
        } else {
            this.data.width = coordinates.width;
            this.data.height = coordinates.height;
        }

        this.lightbox.show(this.data);
    },

    /**
     * @return void
     */
    preloadImage: function() {
        this.images_preloaded++;

        if (this.images_preloaded == this.preload_image_arr.length) {
            this.preloadComplete();
        }
    }
});