/**
 * Create a dialog.
 *
 * The `.uiDialog()` method creates a dialog for each element in the set of matched elements.
 *
 * ### Examples
 * Apply .uiDialog() with an Ok and Cancel Button to all divs with class dialog.
 *
 * JavaScript:
 *
 *     ep('div.dialog')
 *         .uiDialog({
 *             buttons: [
 *              {text: 'Ok', click: function(){ alert('OK'); }},
 *              {text: 'Cancel', click: function(){ alert('Cancel'); }}
 *             ]
 *         });
 *
 *     // use identifier as button text
 *
 *     ep('div.dialog')
 *         .uiDialog({
 *             buttons: {
 *              Ok:     {click: function(){ alert('OK'); }},
 *              Cancel: {click: function(){ alert('Cancel'); }}
 *             }
 *         });
 *
 * Apply .uiDialog() with an Ok and Cancel Button including a form handling.
 *
 * JavaScript:
 *
 *     ep('div.dialog')
 *         .uiDialog({
 *             form: {
 *                 action: '?ViewAction=MyExample',
 *                 method: 'post'
 *             },
 *             buttons:{
 *              Ok:     {type: 'submit'},
 *              Cancel: {click: function(){ ep(this).uiDialog('close'); }}
 *             }
 *         });
 *
 *
 * @class jQuery.ui.uiDialog
 * @extends jQuery.dialog
 *
 * @uses ep
 * @uses ep.ui.form
 * @uses ep.ui.input
 * @uses jQuery.ui.dialog
 * @since 6.11.0
 */

/**
 * See `jQuery.ui.uiDialog` for details.
 *
 * @method uiDialog
 * @member jQuery
 *
 * @since 6.11.0
 */

/*
 * @copyright       © Copyright 2006-2010, epages GmbH, All Rights Reserved.
 *
 * @module          ep.ui.dialog
 */

/*jslint nomen:true*/
/*globals define*/

define("ep/ui/dialog", [
    "jquery",
    "ep",
    "$dict!ep/dict",

    "jquery/ui/dialog",
    "ep/ui/form",
    "ep/ui/input"
], function ($, ep, dict) {
    'use strict';

    $.widget("ui.uiDialog", $.ui.dialog, {

        /**
         * @cfg {Object} [options] A map of additional options pass to the method.
         * @cfg {Object} [options.form] A map of attributes passed to a form around the dialog.
         * @cfg {Boolean} [options.closeText] A boolean indication whether to display the close button, with the default close text.
         */
        options: {
            //  closeOnClickOut: false,
            draggable: false,
            resizable: "se",
            preventScroll: false
        },

        _create: function () {
            var self = this,
                o = self.options;

            self._superApply(arguments);

            // #BUG http://bugs.jqueryui.com/ticket/9097
            self.element.data("ui-dialog", self);

            if (o.form) {
                self._createForm(o.form);
            }

            self.uiDialog
                .removeClass("ui-corner-all")
                .addClass("epDialog");
            self.uiDialogTitlebar
                .removeClass("ui-corner-all");

            self._setOption("closeText", dict.translate('{Close}'));
            self.uiDialogTitlebar
                .find(".ui-dialog-titlebar-close")
                .insertBefore(self.uiDialogTitlebar.find(".ui-dialog-title"));
        },

        _createForm: function (form) {
            var opt = form.options;

            if (opt) {
                delete form.options;
            }

            this.uiForm = this.options.form = ep("<form>")
                .attr(this.options.form)
                .attr({
                    // Setting tabIndex makes the form focusable
                    tabIndex: -1,
                    role: "dialog"
                })
                .addClass(this.uiDialog.attr("class"))
                .toggle(this._isOpen)
                .insertBefore(this.uiDialog)
                .append(this.uiDialog.children())
                .uiForm(opt || {});

            // Remove previous used dialog element
            this.uiDialog.remove();

            // Set form as dialog container
            this.uiDialog = this.uiForm;

            // Initialize wrapper again
            this._initWrapper();
        },

        // Copy of original _createWrapper, but splitted into _createWrapper and _initWrapper
        _createWrapper: function () {
            this.uiDialog = $("<div>")
                .addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
                    this.options.dialogClass)
                .hide()
                .attr({
                    // Setting tabIndex makes the div focusable
                    tabIndex: -1,
                    role: "dialog"
                })
                .appendTo(this._appendTo());

            this._initWrapper();
        },

        _initWrapper: function () {

            this._on(this.uiDialog, {
                keydown: function (event) {
                    if (this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && event.keyCode === $.ui.keyCode.ESCAPE) {
                        event.preventDefault();
                        this.close(event);
                        return;
                    }

                    // prevent tabbing out of dialogs
                    if (event.keyCode !== $.ui.keyCode.TAB) {
                        return;
                    }
                    var tabbables = this.uiDialog.find(":tabbable"),
                        first = tabbables.filter(":first"),
                        last = tabbables.filter(":last");

                    if ((event.target === last[0] || event.target === this.uiDialog[0]) && !event.shiftKey) {
                        first.focus(1);
                        event.preventDefault();
                    } else if ((event.target === first[0] || event.target === this.uiDialog[0]) && event.shiftKey) {
                        last.focus(1);
                        event.preventDefault();
                    }
                },
                mousedown: function (event) {
                    if (this._moveToTop(event) && !this.uiDialog.find('select').length) {
                        this._focusTabbable();
                    }
                }
            });

            // We assume that any existing aria-describedby attribute means
            // that the dialog content is marked up properly
            // otherwise we brute force the content as the description
            if (!this.element.find("[aria-describedby]").length) {
                this.uiDialog.attr({
                    "aria-describedby": this.element.uniqueId().attr("id")
                });
            }
        },

        _moveToTop: function (event, silent) {
            var moved = Boolean(this.uiDialog.nextAll(":visible").not('.ep-cke-full').insertBefore(this.uiDialog).length);
            if (moved && !silent) {
                this._trigger("focus", event);
            }

            return (moved && !silent);
        },

        _setOption: function (key, value) {
            var self = this;

            switch (key) {
            case "form":
                if (!self.uiForm) {
                    self._createForm(value);
                } else {
                    self.uiForm.uiForm(value);
                }
                break;
            case "title":
                $(".ui-dialog-title", self.uiDialogTitlebar)
                    .empty()
                    .append(self.options.title = (value || "&#160;"));
                break;
            case "closeText":
                value = value === true ? dict.translate('{Close}') : (value || "");
                self.uiDialogTitlebar.find(".ui-dialog-titlebar-close").attr('title', value);
                break;
            default:
                self._superApply(arguments);
            }
        },

        _keepFocus: function () {
            if (this.options.closeOnClickOut) {
                this.close();
            } else {
                this._superApply(arguments);
            }
        },

        // Add classes to button area elements
        _createButtonPane: function () {
            var sup = this._superApply(arguments);

            this.uiDialogButtonPane.addClass("epDialogButtonBar");
            this.uiButtonSet.addClass("uiDialogButtonPane");

            return sup;
        },

        // Custom createButtons method (add button object to options, no click required, no jQuery UI Button)
        _createButtons: function () {
            var that = this,
                buttons = this.options.buttons;

            // if we already have a button pane, remove it
            this.uiDialogButtonPane.remove();
            this.uiButtonSet.empty();

            if ($.isEmptyObject(buttons)) {
                this.uiDialog.removeClass("ui-dialog-buttons");
                return;
            }

            $.each(buttons, function (name, props) {
                var click,
                    buttonOptions;

                if ($.isFunction(props)) {
                    props = {
                        click: props,
                        text: name
                    };
                }

                // Default to a non-submitting button
                props = $.extend({
                    type: "button",
                    text: name
                }, props);
                // Change the context for the click callback to be the main element
                click = props.click || function () {};
                props.click = function () {
                    click.apply(that.element[0], arguments);
                };

                delete props.icons;
                delete props.showText;
                buttons[name] = $("<button></button>", props).appendTo(that.uiButtonSet);
            });
            this.uiDialog.addClass("ui-dialog-buttons");
            this.uiDialogButtonPane.appendTo(this.uiDialog);
        },

        open: function () {
            if (this.options.preventScroll) {
                $("body").css("overflow", "hidden");
            }
            return this._superApply(arguments);
        },

        close: function () {
            if (this.options.preventScroll) {
                $("body").css("overflow", "auto");
            }
            return this._superApply(arguments);
        }

    });

    return ep;

});