/*
 * @copyright        © Copyright 2006-2012, epages GmbH, All Rights Reserved.
 *
 * @module            ep.ui.core
 *
 * @revision        $Revision: 1.16 $
 */

/*jslint browser: true, plusplus: true, nomen: true, ass: true, todo: true, regexp: true*/
/*global define*/

define("ep/ui/core", ["jquery", "ep", "util/scope"], function ($, ep, scope) {

    'use strict';

    /**
     * @class ep.ui
     *
     */

    var ui = scope("ep.ui"),

        getId = function (selector) {
            var id = "";

            if (typeof selector === "string") {
                id = selector;
            } else if (selector instanceof $) {
                id = selector.selector;
            }

            if (!id) {
                if (!selector[$.expando]) {
                    selector[$.expando] = ++$.guid;
                }

                id = "Expando:" + selector[$.expando];
            }

            return id;
        },

        getElemData = function (item, stack) {
            var type = $.type(item),
                itemData = {},
                data,
                elem;

            if (type === "string") {
                data = {
                    src: item,
                    desc: ""
                };
            } else if (item.nodeType) {
                elem = $(item);

                if (elem.is("img")) {
                    itemData = elem.data();

                    // get href from parent node if available
                    itemData.href = elem.closest("a")
                        .attr("href");

                    data = {
                        node: item,
                        src: elem.attr("src"),
                        desc: (elem.attr("title") || elem.attr("alt"))
                            .replace(/\\\\n/g, "\n")
                    };
                }
            } else if (type === "object" && item.src) {
                itemData = item;
                data = {
                    src: item.src,
                    desc: item.desc || ""
                };
            }

            if (data) {
                data.srcL = itemData.srcL || itemData.srcM || data.src;
                data.srcMl = itemData.srcMl || data.srcL;
                data.srcM = itemData.srcM || data.srcMl;
                data.srcMs = itemData.srcMs || data.srcM;
                data.srcS = itemData.srcS || data.srcM;
                data.srcXs = itemData.srcXs || data.srcS;

                data.href = itemData.href || data.srcL;

                // Method to get the best src for the given container
                data.getSrcFor = function (selector) {
                    var box = selector instanceof $ ? selector : $(selector),
                        boxWidth = box.innerWidth(),
                        boxHeight = box.innerHeight(),
                        imgSrc;

                    if (boxWidth >= 750 || boxHeight >= 750) {
                        imgSrc = data.srcL;
                    } else if (boxWidth >= 350 || boxHeight >= 350) {
                        imgSrc = data.srcMl;
                    } else if (boxWidth >= 200 || boxHeight >= 200) {
                        imgSrc = data.srcM;
                    } else if (boxWidth >= 100 || boxHeight >= 100) {
                        imgSrc = data.srcMs;
                    } else if (boxWidth >= 75 || boxHeight >= 75) {
                        imgSrc = data.srcS;
                    } else {
                        imgSrc = data.srcXs;
                    }

                    return imgSrc;
                };

                // Returns alternative src if base name matches the default src, else return default src.
                // TODO: Should be removed after disable the posibility to upload different images
                data._checkSrcName = function (defaultSrc, alternativeSrc) {
                    var alternativeName = alternativeSrc.replace(/(_(xs|s|ms|m|ml|l))?\.[^\.]+$/i, ""),
                        defaultName = defaultSrc.replace(/(_(xs|s|ms|m|ml|l))?\.[^\.]+$/i, "");

                    return alternativeName === defaultName ? alternativeSrc : defaultSrc;
                };

                stack.push(data);
            }
        };

    /**
     * @class ep.ui.imgData
     *
     */

    /**
     * The `ep.ui.imgData()` method read the extended image data of elements an handle them in an array like object.
     *
     * The item objects of an image data object:
     * {
     * "desc" : <description>,
     * "href" : <path to an url / image>,
     * "src"  : <path to default image>,
     * "srcXs": <path to thumbnail image>,
     * "srcS" : <path to small image>,
     * "srcM" : <path to medium image>,
     * "srcL" : <path to large image>,
     * "node" : <the source DOM element (optional)>
     * }
     *
     * ### Examples
     * Find all images.
     *
     * JavaScript:
     *
     *     ep.ui.imgData('img');
     *
     * HTML:
     *
     *     <ul>
     *     <li>
     *     <a href="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010.jpg">
     *       <img
     *           alt="Eureka El Capitan IV"
     *           src="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010.jpg"
     *           data-src-xs="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_xs.jpg"
     *           data-src-s="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_s.jpg"
     *           data-src-m="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_m.jpg"
     *           data-src-l="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010.jpg"
     *       />
     *     </a>
     *     </li>
     *     <li>
     *     <a href="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_2.jpg">
     *       <img
     *           alt="Eureka El Capitan IV"
     *           src="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_2.jpg"
     *           data-src-xs="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_2_xs.jpg"
     *           data-src-s="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_2_s.jpg"
     *           data-src-m="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_2_m.jpg"
     *           data-src-l="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_2.jpg"
     *       />
     *     </a>
     *     </li>
     *     <li>
     *     <a href="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_3.jpg">
     *       <img
     *           alt="Eureka El Capitan IV"
     *           src="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_3.jpg"
     *           data-src-xs="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_3_xs.jpg"
     *           data-src-s="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_3_s.jpg"
     *           data-src-m="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_3_m.jpg"
     *           data-src-l="/WebRoot/Store/Shops/DemoShop/Products/eg_1000111010/eg_1000111010_3.jpg"
     *       />
     *     </a>
     *     </li>
     *     </ul>
     *
     *
     * ### Dependencies
     *
     *  + `ep`
     *  + `jQuery.scope`
     *
     * @param {String OR Element OR Object OR Array OR Array} p A string containing a selector expression to find images OR An image DOM element OR An existing jQuery object containing images OR An array containing a set of image DOM elements OR An array containing a set of imageData items.
     *
     * @fires change
     * @fires resize
     * @fires resizestop
     *
     * @method constructor
     * @member ep.ui.imgData
     *
     * @since 6.14.0
     */

    ui.imgData = function (selector) {
        var id = getId(selector),
            result = ui.imgData.cache[id],
            type,
            data;

        if (!result) {
            type = $.type(selector);
            data = [];

            if (type === "string" && !(/\//).test(selector)) {
                $(selector).each(function () {
                    getElemData(this, data);
                });
            } else if (type === "array") {
                $.each(selector, function (i, item) {
                    /*jslint unparam: true */
                    getElemData(item, data);
                });
            } else if (selector instanceof $) {
                selector.each(function () {
                    getElemData(this, data);
                });
            } else if (type === "string" || (type === "object" && (selector.src || selector.nodeType))) {
                getElemData(selector, data);
            }

            result = new ui.imgData.init(data);

            result.on("location", function () {
                var href = (result.current() || {})
                        .href;
                if (href) {
                    location.href = href;
                }
            });

            if (id) {
                ui.imgData.cache[id] = result;
            }
        }

        return result;
    };

    ui.imgData.cache = {};

    /**
     * Initialize a new instance of ep.ui.imgData.
     *
     * @param {Array} stack of image information objects.
     *
     * @method init
     * @static
     * @member ep.ui.imgData
     *
     * @since 6.14.0
     */

    /**
     * @event resize This event is triggered while the window resize.
     * @member ep.ui.imgData
     */

    /**
     * @event resizestop This event is triggered when the window resize is stopped.
     * @member ep.ui.imgData
     */
    ui.imgData.init = function (data) {
        var self = this;

        self.eventObject = $('<div/>');

        $(window)
            .on("resize", function () {
                // trigger resize
                self.trigger("resize");

                // clear trigger delay
                clearTimeout(self._resizestopdelay);

                // trigger a special event when resize stopped
                self._resizestopdelay = setTimeout(function () {
                    self.trigger("resizestop");
                }, 100);
            });

        return $.merge(self, data);
    };

    ui.imgData.init.prototype = {

        length: 0,

        currentItem: 0,

        /**
         * Iterate over a imaga data object, executing a function for each item.
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @param {Function} callback The function that will be executed on every image data item object.
         *
         * @method each
         * @member ep.ui.imgData
         *
         * @since 6.14.0
         */

        each: $.fn.each,

        /**
         * Retrieve the items matched by the image data object.
         *
         * The `.get()` method grants us access to the items underlying each image data object. Without a parameter,
         * `.get()` returns an array of all items.
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @param {Boolean} [index] A zero-based integer indicating which item to retrieve.
         *
         * @method get
         * @member ep.ui.imgData
         *
         * @since 6.14.0
         */
        get: function (i) {
            return arguments.length ? this[i] : $.merge([], this);
        },

        /**
         * Retrieve the item which is marked as current of the image data object (if no argument is given) or
         * mark an item of the image data object as current (if an argument is given).
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @param {Boolean} [index] A zero-based integer indicating which item to mark as current.
         *
         * @method current
         * @member ep.ui.imgData
         *
         * @since 6.14.0
         */

        /**
         * @event change This event is triggered when the current item of image dat object is changed.
         * @member ep.ui.imgData
         */
        current: function (i) {
            var result,
                set;

            if (arguments.length) {
                set = this.currentItem;

                if (i === 'next') {
                    set++;
                } else if (i === 'prev') {
                    set--;
                } else {
                    set = i;
                }

                if (set === this.currentItem || set >= this.length || set < 0) {
                    result = this;
                } else {
                    this.currentItem = set;

                    result = this.trigger('change');
                }
            } else {
                result = this.get(this.currentItem);
            }

            return result;
        },

        /**
         * Retrieve the item before the current item of the image data object.
         *
         * May is `undefined`, if current item the first of the image data object.
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @method prev
         * @member ep.ui.imgData
         *
         * @since 6.14.0
         */
        prev: function () {
            return this.get(this.currentItem - 1);
        },

        /**
         * Retrieve the item after the current item of the image data object.
         *
         * May is `undefined`, if current item the last of the image data object.
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @method next
         * @member ep.ui.imgData
         *
         * @since 6.14.0
         */
        next: function () {
            return this.get(this.currentItem + 1);
        },

        first: function () {
            return this.get(0);
        },

        last: function () {
            return this.get(this.length - 1);
        },

        /**
         * Attach an event handler function for one or more events to the image data object.
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @param {String} events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
         * @param {Function} handler(eventObject) A function to execute when the event is triggered.
         *
         * @method on
         * @member ep.ui.imgData
         *
         * @since 6.14.0
         */
        on: function (events, handler) {
            this.eventObject.on(events, $.proxy(handler, this));
            return this;
        },

        /**
         * Remove an event handler from the image data object.
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @param {String} events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
         * @param {Function} [handler] A handler function previously attached for the event(s). It takes the event object as its first argument: `handler(eventObject)`
         *
         * @method off
         * @member ep.ui.imgData
         *
         * @since 6.14.0
         */
        off: function (events, handler) {
            if (handler) {
                this.eventObject.off(events, $.proxy(handler, this));
            } else {
                this.eventObject.off(events);
            }
            return this;
        },

        /**
         * Execute all handlers and behaviors attached to the image data object for the given event type.
         *
         * ### Dependencies
         *
         *  + `ep`
         *  + `jQuery.scope`
         *
         * @param {String} eventType A string containing a JavaScript event type, such as click or submit.
         * @param {Event} event A jQuery.Event object.
         *
         * @method trigger
         * @member ep.ui.imgData
         *
         * @since 6.14.0,6.14.0
         */
        trigger: function (events) {
            this.eventObject.trigger(events);
            return this;
        }
    };

    // Create a
    ui.createMoveToTop = function (elementName) {
        return function (event, silent) {
            var elem = this[elementName],
                moved = !!elem.nextAll(":visible").insertBefore(elem).length;

            if (moved && !silent && this._trigger) {
                this._trigger("focus", event);
            }

            return this;
        };
    };

    return ep;

});