/** @class */
jscape.settings.ManagementRolesController = jscape.ui.DatagridController.extend(
    /** @lends {jscape.settings.ManagementRolesController.prototype} */
{
    view: null,

    /** @constructor */
    initialize: function() {
        this._super(true);

        this._initValidators();
    },

    onCreate: function(managementService) {
        this.managementService = managementService;

        this.addController = new jscape.settings.AddManagementRoleController();
        this.editController = new jscape.settings.EditManagementRoleController();
        this.deleteController = new jscape.settings.DeleteManagementRoleController();
    },

    onStart: function(listener) {
        this.listener = listener || jscape.settings.ManagementRolesController.LISTENER;

        if (this.view == null) {
            this.view = new jscape.settings.ManagementRolesView();
        }
        this.view.show(this);
    },

    onResume: function() {
        this.view.refresh();
    },

    onRefresh: function(params) {
        return this.managementService.getManagementRoles()
                .done($.proxy(function(roles) { this.listener.onChangeRoles(roles); }, this));
    },

    hasChanged: function() {
        return false;
    },

    onAddRole: function() {
        this.addController.start(this.managementService)
                .done($.proxy(this._roleAdded, this));
    },

    onEditRole: function(role) {
        this.editController.start(this.managementService, role)
                .done($.proxy(this._roleEdited, this));
    },

    onDeleteRole: function (roles) {
        this.deleteController.start(this.managementService, roles)
                .done($.proxy(this._roleDeleted, this, roles));
    },

    _roleAdded: function(role) {
        this._recordAdded(role);
    },

    _roleEdited: function(role) {
        this._recordEdited(role);
    },

    _roleDeleted: function(roles) {
        this._recordDeleted(roles);
    },

    _initValidators: function() {
        var v = new jscape.Validator();
        v.nonEmptyRule("acl_role_nonempty", window.R.string.ACL_ERROR_BAD_ROLE);
        v.rule("acl_role_unique", $.proxy(this._roleNotExists, this), window.R.string.ACL_ERROR_ROLE_ALREDY_EXISTS);
    },

    _roleNotExists: function(value) {
        return this.view.indexOf(value) == -1;
    }
});
jscape.settings.ManagementRolesController.LISTENER = {
    onChangeRoles: $.noop
};

/** @class */
jscape.settings.ManagementRolesView = jscape.ui.DatagridView.extend(
    /** @lends {jscape.settings.ManagementRolesView.prototype} */
{
    selection: null,

    /** @constructor */
    initialize: function() {
        this.fieldset = $("fieldset[name=aclrolesfields]");

        var initializing = true;

        this.data = $("table[role=grid][aria-label=roles]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            singleSelect: false,
            ctrlSelect: true,
            sortOrder: "asc",
            columns: [[{field: "_name", title: $("thead th:nth-child(1)", this.data).text(), width: 200, formatter: function(_, row) { return String(row || "").escapeXml(); }}]],
            onSelect: $.proxy(this._adjustState, this),
            onUnselect: $.proxy(this._adjustState, this),
            onSortColumn: $.proxy(this._adjustState, this),
            onDblClickRow: $.proxy(this._onClick$EditButton, this),
            loader: $.proxy(this._onLoad$Data, this),
            onBeforeLoad: function() { return !initializing; },
            onLoadSuccess: $.proxy(this._adjustState, this)
        });

        this.addButton = $("a[role=button][name=add]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$AddButton, this)});
        this.editButton = $("a[role=button][name=edit]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$EditButton, this)});
        this.deleteButton = $("a[role=button][name=delete]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$DeleteButton, this)});

        initializing = false;
    },

    show: function(listener) {
        this.listener = listener || jscape.settings.ManagementRolesView.LISTENER;
        this.data.datagrid("resize");
        this._adjustState();
    },

    getRoles: function() {
        return this.getRecords();
    },

    indexOf: function(value) {
        return $.inArray(value, this.data.datagrid("getRows"));
    },

    _onLoad$Data: function(params, success, error) {
        return this.listener.onRefresh(params).done(success).fail(error);
    },

    _onClick$AddButton: function() {
        this.listener.onAddRole(this);
    },

    _onClick$EditButton: function() {
        this.listener.onEditRole(this.selection[0]);
    },

    _onClick$DeleteButton: function() {
        this.listener.onDeleteRole(this.selection);
    },

    _adjustState: function() {
        this.selection = this.data.datagrid("getSelections");
        this.editButton.linkbutton(this.selection.length == 1 ? "enable" : "disable");
        this.deleteButton.linkbutton(this.selection.length != 0 ? "enable" : "disable");
    }
});
jscape.settings.ManagementRolesView.LISTENER = {
    onRefresh: $.noop,
    onAddRole: $.noop,
    onEditRole: $.noop,
    onDeleteRole: $.noop
};

/** @class */
jscape.settings.AddManagementRoleController = Class.extend(
    /** @lends {jscape.settings.AddManagementRoleController.prototype} */
{
    deferred: null,

    /** @constructor */
    initialize: function() {
        this.addDomainAccessController = new jscape.settings.AddDomainAccessController();
        this.deleteDomainAccessController = new jscape.settings.DeleteDomainAccessController();
    },

    start: function(managementService) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onDomainPermissions: function(dialog, domainAccess) {
        var d = new jscape.settings.DomainPermissionsDialog();
        d.setDomainName(domainAccess.domainName);
        d.setPermissions(domainAccess.permissions);
        d.show({
            onSubmit: function(d) { domainAccess.permissions = d.getPermissions(); d.destroy(); },
            onCancel: function(d) { d.destroy(); }
        });
    },

    onAddDomainAccess: function(dialog) {
        var values = dialog.getDomainAccesses();
        this.addDomainAccessController.start(this.managementService, values)
                .done(function (access) {
                          values.push(access);
                          dialog.setDomainAccesses(values);
                          dialog.selectDomainAccess(access);
                      });
    },

    onDeleteDomainAccess: function(dialog, domainAccess) {
        var values = dialog.getDomainAccesses();
        var index = $.inArray(domainAccess, values);
        if (index != -1) {
            this.deleteDomainAccessController.start(domainAccess)
                    .done(function () {
                              values.splice(index, 1);
                              dialog.setDomainAccesses(values);
                              if (values.length) {
                                  index = Math.min(Math.max(0, index), values.length - 1);
                                  dialog.selectDomainAccess(values[index]);
                              }
                          });
        }
    },

    onSubmit: function (dialog) {
        var role = this._createRole(dialog);
        dialog.destroy();
        this.managementService.addManagementRole(role)
                .done($.proxy(function (role) { this.deferred.resolve(role.name); this.deferred = null; }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
    },

    onCancel: function(dialog) {
        dialog.destroy();
        this.deferred.reject();
        this.deferred = null;
    },

    _createRole: function(dialog) {
        return new jscape.ManagementRole(dialog.getName(),
                                         dialog.getGlobalPermissions(),
                                         dialog.getDomainAccesses());
    },

    _setupDialog: function() {
        var dialog = new jscape.settings.AddManagementRoleDialog();
        this.managementService.getAclTags().done(function(tags) { dialog.setAvailableTags(tags); });
        return dialog;
    }
});

/** @class */
jscape.settings.EditManagementRoleController = Class.extend(
    /** @lends {jscape.settings.EditManagementRoleController.prototype} */
{
    deferred: null,

    /** @constructor */
    initialize: function() {
        this.addDomainAccessController = new jscape.settings.AddDomainAccessController();
        this.deleteDomainAccessController = new jscape.settings.DeleteDomainAccessController();
    },

    start: function(managementService, roleName) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        this.roleName = roleName;
        this.managementService.getManagementRole(roleName)
                .done($.proxy(function(role) { this._setupDialog(role).show(this); }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

    onDomainPermissions: function(dialog, domainAccess) {
        var d = new jscape.settings.DomainPermissionsDialog();
        d.setDomainName(domainAccess.domainName);
        d.setPermissions(domainAccess.permissions);
        d.show({
            onSubmit: function(d) { domainAccess.permissions = d.getPermissions(); d.destroy(); },
            onCancel: function(d) { d.destroy(); }
        });
    },

    onAddDomainAccess: function(dialog) {
        var values = dialog.getDomainAccesses();
        this.addDomainAccessController.start(this.managementService, values)
                .done(function (access) {
                          values.push(access);
                          dialog.setDomainAccesses(values);
                          dialog.selectDomainAccess(access);
                      });
    },

    onDeleteDomainAccess: function(dialog, domainAccess) {
        var values = dialog.getDomainAccesses();
        var index = $.inArray(domainAccess, values);
        if (index != -1) {
            this.deleteDomainAccessController.start(domainAccess)
                    .done(function () {
                              values.splice(index, 1);
                              dialog.setDomainAccesses(values);
                              if (values.length) {
                                  index = Math.min(Math.max(0, index), values.length - 1);
                                  dialog.selectDomainAccess(values[index]);
                              }
                          });
        }
    },

    onSubmit: function (dialog) {
        var role = this._createRole(dialog);
        dialog.destroy();
        this.managementService.setManagementRole(this.roleName, role)
                .done($.proxy(function () { this.deferred.resolve(this.roleName); this.deferred = null; }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
    },

    onCancel: function(dialog) {
        dialog.destroy();
        this.deferred.reject();
        this.deferred = null;
    },

    _createRole: function(dialog) {
        return new jscape.ManagementRole(this.roleName,
                                         dialog.getGlobalPermissions(),
                                         dialog.getDomainAccesses());
    },

    _setupDialog: function(role) {
        var dialog = new jscape.settings.EditManagementRoleDialog();
        dialog.setName(role.name);
        dialog.setGlobalPermissions(role.permissions);
        dialog.setDomainAccesses(role.domainAccesses);
        this.managementService.getAclTags().done(function(tags) { dialog.setAvailableTags(tags); });
        return dialog;
    }
});

/** @class */
jscape.settings.DeleteManagementRoleController = Class.extend(
    /** @lends {jscape.settings.DeleteManagementRoleController.prototype} */
{
    deferred: null,

    start: function(managementService, roleNames) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        this._confirmDelete(roleNames)
                .done($.proxy(this._deleteConfirmed, this, roleNames))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

    _confirmDelete: function(roleNames) {
        return jscape.ConfirmDialog.confirm(window.R.string.ACL_CONFIRM_DELETE_ROLE_TITLE,
                                            window.R.string.ACL_CONFIRM_DELETE_ROLE_MESSAGE.supplant({0: roleNames.join(", ")}));
    },

    _deleteConfirmed: function (roleNames) {
        $.when.apply(jQuery, $.map(roleNames, $.proxy(function (name) {
            return this.managementService.deleteManagementRole(name);
        }, this)))
                .done($.proxy(function () { this.deferred.resolve(roleNames); this.deferred = null; }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
    }
});

/** @class */
jscape.settings.AddDomainAccessController = Class.extend(
    /** @lends {jscape.settings.AddDomainAccessController.prototype} */
{
    deferred: null,

    start: function (managementService, domainAccesses) {
        this.deferred = $.Deferred();
        managementService.getDomainNames()
                .then(function (domainNames) {
                          return $.grep(domainNames, function (domainName) {
                              for (var i = 0; i < domainAccesses.length; i++) {
                                  if (domainAccesses[i].domainName == domainName) {
                                      return false;
                                  }
                              }
                              return true;
                          });
                      })
                .done($.proxy(function (domainNames) {
                          if (domainNames.length != 0) {
                              this._setupDialog(domainNames).show(this);
                          } else {
                              this._showNoDomainsError();
                              this.deferred.reject();
                              this.deferred = null;
                          }
                      }, this))
                .fail($.proxy($.proxy(function () {
                          this.deferred.resolve();
                          this.deferred = null;
                      }, this)));
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var access = this._createDomainAccess(dialog);
        dialog.destroy();
        this.deferred.resolve(access);
        this.deferred = null;
    },

    onCancel: function(dialog) {
        dialog.destroy();
        this.deferred.reject();
        this.deferred = null;
    },

    _createDomainAccess: function(dialog) {
        return new jscape.DomainAccess(dialog.getDomain(), [], [], true);
    },

    _setupDialog: function(domains) {
        var dialog = new jscape.settings.AddDomainAccessDialog();
        dialog.setDomains(domains);
        return dialog;
    },

    _showNoDomainsError: function() {
        $.messager.alert(window.R.string.APPLICATION_ERROR_TITLE,
                         window.R.string.ACL_ERROR_DOMAINS_NOT_AVAILABLE,
                         "warning");
    }
});

/** @class */
jscape.settings.AddDomainAccessDialog = Class.extend(
    /** @lends {jscape.settings.AddDomainAccessDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-settings-acl-domainaccess").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, height: "auto",
            modal: true, resizable: false, closable: true, closed: true,
            buttons: [
                {text: window.R.string.DIALOG_BUTTON_OK || "Ok", handler: $.proxy(this._onSubmit, this)},
                {text: window.R.string.DIALOG_BUTTON_CANCEL || "Cancel", handler: $.proxy(this._onCancel, this)}
            ],
            onClose: $.proxy(function () {if (!this.closing) { this._onCancel(); }}, this)
        }).show();

        this.fieldset = $("fieldset[name=domainaccessfields]", this.dialog);
        this.domain = $("select[name=domain]", this.fieldset).combobox({required: true, editable: false, width: 250, panelHeight: "auto"});
    },

    getDomain: function() {
        return this.domain.combobox("getValue");
    },

    setDomains: function(values) {
        this.domain.combobox("loadData", values);
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.domain.focus();
    },

    hide: function () {
        if (!this._closed()) {
            this.closing = true;
            this.dialog.dialog("close");
            this.fieldset.form("disableValidation");
            this.closing = false;
        }
    },

    destroy: function() {
        this.dialog.dialog("destroy");
    },

    _onSubmit: function () {
        if (this.fieldset.form("validate")) {
            this.hide();
            this.listener.onSubmit(this);
        }
    },

    _onCancel: function () {
        this.hide();
        this.listener.onCancel(this);
    },

    _closed: function () {
        return !!this.dialog.dialog("options").closed;
    }
});

/** @class */
jscape.settings.DeleteDomainAccessController = Class.extend(
    /** @lends {jscape.settings.DeleteDomainAccessController.prototype} */
{
    start: function(domainAccess) {
        return jscape.ConfirmDialog.confirm(window.R.string.ACL_CONFIRM_DELETE_DOMAIN_ACCESS_TITLE,
                                            window.R.string.ACL_CONFIRM_DELETE_DOMAIN_ACCESS_MESSAGE.supplant({0: domainAccess.domainName}));
    }
});

/** @class */
jscape.settings.ManagementRoleDialog = Class.extend(
    /** @lends {jscape.settings.ManagementRoleDialog.prototype} */
{
    selection: null,
    editIndex: null,
    tags: null,

    /** @constructor */
    initialize: function(dialog) {
        this.selection = [];
        this.editIndex = -1;
        this.tags = [];

        this.dialog = dialog.clone().attr("id", "");
        this.dialog.dialog({
            width: 800, height: "auto",
            modal: true, resizable: false, closable: true, closed: true,
            buttons: [
                {text: window.R.string.DIALOG_BUTTON_OK || "Ok", handler: $.proxy(this._onSubmit, this)},
                {text: window.R.string.DIALOG_BUTTON_CANCEL || "Cancel", handler: $.proxy(this._onCancel, this)}
            ],
            onClose: $.proxy(function () {if (!this.closing) { this._onCancel(); }}, this)
        }).keydown($.proxy(function(e) {
            if (e.keyCode == 13) {
                e.stopPropagation();
                this._onSubmit();
            } else if (e.keyCode == 27) {
                e.stopPropagation();
                this._onCancel();
            }
        }, this)).show();

        this.fieldset = $("fieldset[name=aclrolefields]", this.dialog);

        this.serverPermissions = $("fieldset[name=serverpermissions]", this.fieldset).find("select")
                .tagbox({limitToList: true, hasDownArrow: true, width: 450, panelHeight: "auto"})
                .tagbox("clear");

        this.domains = $("table[role=grid][aria-label=domainspermissions]", this.fieldset);
        this.domains.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            singleSelect: true,
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onLoadSuccess: $.proxy(this._adjustRowSelection, this),
            onClickRow: $.proxy(this._onClick$Row, this),
            onDblClickRow: $.proxy(this._onClick$PermissionsButton, this),
            onBeginEdit: $.proxy(function(index) { this.editIndex = index; }, this),
            onEndEdit: $.proxy(function() { this.editIndex = -1; }, this),
            sortName: "domainName",
            columns: [[
                    {field: "domainName", title: $("thead th:nth-child(1)", this.domains).text(), sortable: true, width: 100},
                    {field: "enabled", title: $("thead th:nth-child(2)", this.domains).text(), sortable: true, width: 70, align: "center", editor: "accessible", formatter: function(value) { return "<input type=\"checkbox\"" + (value ? " checked=\"checked\"" : "") + "/>"; }},
                    {field: "tags", title: $("thead th:nth-child(3)", this.domains).text(), width: 200, editor: "tags", formatter: function(value) { return (value || []).join(", ").escapeXml(); }}
            ]],
            editors: {
                accessible: $.extend({}, $.fn.datagrid.defaults.editors.checkbox, {
                    init: function(parent) {
                        return $("<input type=\"checkbox\">").appendTo(parent);
                    },
                    getValue: function (target) {
                        return $(target).is(":checked");
                    },
                    setValue: function (target, value) {
                        $(target).prop("checked", !!value);
                    }
                }),
                tags: $.extend({}, $.fn.datagrid.defaults.editors.combobox, {
                    init: function (parent, options) {
                        return $("<input type=\"text\">").appendTo(parent).tagbox({limitToList: true, hasDownArrow: true, width: "100%", panelHeight: "auto", onLoadSuccess: $.noop});
                    },
                    getValue: function (target) {
                        return $(target).tagbox("getValues");
                    },
                    setValue: function (target, tags) {
                        if (tags) {
                            $(target).tagbox("setValues", tags);
                        } else {
                            $(target).tagbox("clear");
                        }
                    }
                })
            }
        });

        this.permissionsButton = $("a[role=button][name=permissions]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$PermissionsButton, this)});
        this.addAccessButton = $("a[role=button][name=add]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$AddAccessButton, this)});
        this.deleteAccessButton = $("a[role=button][name=delete]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$DeleteAccessButton, this)});
    },

    getGlobalPermissions: function() {
        var permissions = [];
        this.serverPermissions.each(function(_, input) {
            var values = $(input).tagbox("getValues");
            if (values.length) {
                permissions = permissions.concat(values);
            }
        });
        return permissions;
    },

    setGlobalPermissions: function(values) {
        for (var i = 0; i < values.length; ++i) {
            var value = values[i];
            for (var j = 0, size = this.serverPermissions.length; j < size; ++j) {
                var found = false;
                var input = $(this.serverPermissions[j]);
                var valueField = input.tagbox("options").valueField;
                var data = input.tagbox("getData");
                for (var k = 0; k < data.length; ++k) {
                    if (value == data[k][valueField]) {
                        input.tagbox("select", data[k][valueField]);
                        found = true;
                        break;
                    }
                }
                if (found) {
                    break;
                }
            }
        }
    },

    getDomainAccesses: function() {
        return this.domains.datagrid("acceptChanges").datagrid("getRows");
    },

    setDomainAccesses: function(values) {
        this.domains.datagrid("rejectChanges")
                .datagrid("loading").datagrid("loadData", values).datagrid("loaded");
    },

    selectDomainAccess: function(value) {
        this.domains.datagrid("selectRow", this.domains.datagrid("getRowIndex", value));
    },

    setAvailableTags: function(values) {
        this.tags = values;
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.find(":input:not(:disabled):first").focus();
        this._adjustRowSelection();
    },

    hide: function () {
        if (!this._closed()) {
            this.closing = true;
            this.dialog.dialog("close");
            this.fieldset.form("disableValidation");
            this.closing = false;
        }
    },

    destroy: function() {
        this.dialog.dialog("destroy");
    },

    _onSubmit: function () {
        this.domains.datagrid("acceptChanges");
        if (this.fieldset.form("validate")) {
            this.hide();
            this.listener.onSubmit(this);
        }
    },

    _onCancel: function () {
        this.hide();
        this.listener.onCancel(this);
    },

    _closed: function () {
        return !!this.dialog.dialog("options").closed;
    },

    _onClick$Row: function(index) {
        if (this.editIndex != index) {
            this.domains.datagrid("endEdit", this.editIndex).datagrid("beginEdit", index);
            var editor = this.domains.datagrid("getEditor", {index: index, field: "tags"});
            editor && $(editor.target).combobox("loadData", this.tags);
        }
    },

    _onClick$PermissionsButton: function() {
        this.listener.onDomainPermissions(this, this.selection[0]);
    },

    _onClick$AddAccessButton: function() {
        this.listener.onAddDomainAccess(this);
    },

    _onClick$DeleteAccessButton: function() {
        this.listener.onDeleteDomainAccess(this, this.selection[0]);
    },

    _adjustRowSelection: function() {
        this.selection = this.domains.datagrid("getSelections");
        this._adjustButtons();
    },

    _adjustButtons: function() {
        this.permissionsButton.linkbutton(this.selection.length == 1 ? "enable" : "disable");
        this.deleteAccessButton.linkbutton(this.selection.length != 0 ? "enable" : "disable");
    }
});
jscape.settings.ManagementRoleDialog.LISTENER = {
    onAccDomainAccess: $.noop,
    onDeleteDomainAccess: $.noop,
    onDomainPermissions: $.noop,
    onSubmit: $.noop,
    onCancel: $.noop
};

/** @class */
jscape.settings.AddManagementRoleDialog = jscape.settings.ManagementRoleDialog.extend(
    /** @lends {jscape.settings.AddManagementRoleDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-settings-acl-addrole"));

        this.name = $("input[name=name]", this.fieldset).textbox({required: true, validType: ["acl_role_nonempty", "acl_role_unique"], missingMessage: window.R.string.ACL_ERROR_BAD_ROLE, width: "60%"});
    },

    getName: function() {
        return this.name.textbox("getValue");
    }
});

/** @class */
jscape.settings.EditManagementRoleDialog = jscape.settings.ManagementRoleDialog.extend(
    /** @lends {jscape.settings.EditManagementRoleDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-settings-acl-editrole"));

        this.title = this.dialog.dialog("header").text() || "";
    },

    setName: function(value) {
        this.dialog.dialog("setTitle", this.title.supplant({0: value}));
    }
});

/** @class */
jscape.settings.DomainPermissionsDialog = Class.extend(
    /** @lends {jscape.settings.DomainPermissionsDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-settings-acl-domainpermissions").clone().attr("id", "");
        this.dialog.dialog({
            width: 670, height: "auto",
            modal: true, resizable: false, closable: true, closed: true,
            buttons: [
                {text: window.R.string.DIALOG_BUTTON_OK || "Ok", handler: $.proxy(this._onSubmit, this)},
                {text: window.R.string.DIALOG_BUTTON_CANCEL || "Cancel", handler: $.proxy(this._onCancel, this)}
            ],
            onClose: $.proxy(function () {if (!this.closing) { this._onCancel(); }}, this)
        }).keydown($.proxy(function(e) {
            if (e.keyCode == 13) {
                e.stopPropagation();
                this._onSubmit();
            } else if (e.keyCode == 27) {
                e.stopPropagation();
                this._onCancel();
            }
        }, this)).show();

        this.title = this.dialog.dialog("header").text() || "";

        this.fieldset = $("fieldset[name=domainpermissionsfields]", this.dialog);

        this.permissions = this.fieldset.find("select")
                .tagbox({limitToList: true, hasDownArrow: true, width: 350, panelHeight: "auto"}).tagbox("clear");
    },

    setDomainName: function(value) {
        this.dialog.dialog("setTitle", this.title.supplant({0: value}));
    },

    getPermissions: function() {
        var permissions = [];
        this.permissions.each(function(_, input) {
            var values = $(input).tagbox("getValues");
            if (values.length) {
                permissions = permissions.concat(values);
            }
        });
        return permissions;
    },

    setPermissions: function(values) {
        for (var i = 0; i < values.length; ++i) {
            var value = values[i];
            for (var j = 0, size = this.permissions.length; j < size; ++j) {
                var found = false;
                var input = $(this.permissions[j]);
                var valueField = input.tagbox("options").valueField;
                var data = input.tagbox("getData");
                for (var k = 0; k < data.length; ++k) {
                    if (value == data[k][valueField]) {
                        input.tagbox("select", data[k][valueField]);
                        found = true;
                        break;
                    }
                }
                if (found) {
                    break;
                }
            }
        }
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
    },

    hide: function () {
        if (!this._closed()) {
            this.closing = true;
            this.dialog.dialog("close");
            this.fieldset.form("disableValidation");
            this.closing = false;
        }
    },

    destroy: function() {
        this.dialog.dialog("destroy");
    },

    _onSubmit: function () {
        if (this.fieldset.form("validate")) {
            this.hide();
            this.listener.onSubmit(this);
        }
    },

    _onCancel: function () {
        this.hide();
        this.listener.onCancel(this);
    },

    _closed: function () {
        return !!this.dialog.dialog("options").closed;
    }
});

/** @class */
jscape.settings.AclTagsController = jscape.ui.DatagridController.extend(
    /** @lends {jscape.settings.AclTagsController.prototype} */
{
    view: null,

    /** @constructor */
    initialize: function() {
        this._super(true);

        this._initValidators();
    },

    onCreate: function(managementService) {
        this.managementService = managementService;

        this.addController = new jscape.settings.AddAclTagController();
        this.deleteController = new jscape.settings.DeleteAclTagController();
    },

    onStart: function() {
        if (this.view == null) {
            this.view = new jscape.settings.AclTagsView();
        }
        this.view.show(this);
    },

    onResume: function() {
        this.view.refresh();
    },

    onRefresh: function(params) {
        return this.managementService.getAclTags();
    },

    hasChanged: function() {
        return false;
    },

    onAddTag: function() {
        this.addController.start(this.managementService)
                .done($.proxy(this._tagAdded, this));
    },

    onDeleteTag: function(tags) {
        this.deleteController.start(this.managementService, tags)
                .done($.proxy(this._tagDeleted, this));
    },

    _tagAdded: function(tag) {
        this._recordAdded(tag);
    },

    _tagDeleted: function(tags) {
        this._recordDeleted(tags);
    },

    _initValidators: function () {
        var v = new jscape.Validator();
        v.rule("acl_tag_unique", $.proxy(this._tagNotExists, this), window.R.string.ACL_ERROR_TAG_ALREDY_EXISTS);
    },

    _tagNotExists: function (value, excludes) {
        return (excludes && $.inArray(value, excludes) != -1) || $.inArray(value, this.tags) == -1;
    }
});

/** @class */
jscape.settings.AclTagsView = jscape.ui.DatagridView.extend(
    /** @lends {jscape.settings.AclTagsView.prototype} */
{
    selection: null,

    /** @constructor */
    initialize: function() {
        var initializing = true;

        this.fieldset = $("fieldset[name=acltagsfields]");

        this.data = $("table[role=grid][aria-label=tags]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            singleSelect: false,
            ctrlSelect: true,
            columns: [[{field: "_name", title: $("thead th:nth-child(1)", this.data).text(), width: 200, formatter: function (_, row) { return String(row).escapeXml(); }}]],
            onSelect: $.proxy(this._adjustState, this),
            onUnselect: $.proxy(this._adjustState, this),
            onSortColumn: $.proxy(this._adjustState, this),
            loader: $.proxy(this._onLoad$Data, this),
            onBeforeLoad: function() { return !initializing; },
            onLoadSuccess: $.proxy(this._adjustState, this)
        });

        this.addButton = $("a[role=button][name=add]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$AddButton, this)});
        this.deleteButton = $("a[role=button][name=delete]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$DeleteButton, this)});

        initializing = false;
    },

    show: function (listener) {
        this.listener = listener || jscape.settings.AclTagsView.LISTENER;
        this.data.datagrid("resize");
        this._adjustState();
    },

    indexOf: function (value) {
        return $.inArray(value, this.data.datagrid("getRows"));
    },

    _onLoad$Data: function(params, success, error) {
        this.listener.onRefresh(params).done(success).fail(error);
    },

    _onClick$AddButton: function () {
        this.listener.onAddTag(this);
    },

    _onClick$DeleteButton: function () {
        this.listener.onDeleteTag(this.selection);
    },

    _adjustState: function () {
        this.selection = this.data.datagrid("getSelections");
        this.deleteButton.linkbutton(this.selection.length != 0 ? "enable" : "disable");
    }
});
jscape.settings.AclTagsView.LISTENER = {
    onAddTag: $.noop,
    onDeleteTag: $.noop
};

/** @class */
jscape.settings.AddAclTagController = Class.extend(
    /** @lends {jscape.settings.AddAclTagController.prototype} */
{
    deferred: null,

    start: function(managementService) {
        this.deferred = $.Deferred();
        this.managementService = managementService;
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var tagName = dialog.getName();
        dialog.destroy();
        this.managementService.storeAclTag(tagName)
                .done($.proxy(function () { this.deferred.resolve(tagName); this.deferred = null; }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
    },

    onCancel: function(dialog) {
        dialog.destroy();
        this.deferred.reject();
        this.deferred = null;
    },

    _setupDialog: function() {
        return new jscape.settings.AddAclTagDialog();
    }
});

/** @class */
jscape.settings.DeleteAclTagController = Class.extend(
    /** @lends {jscape.settings.DeleteAclTagController.prototype} */
{
    deferred: null,

    start: function(managementService, tags) {
        this.deferred = $.Deferred();
        this._confirmDelete(tags)
                .done($.proxy(function() {
                                  $.when.apply(jQuery, $.map(tags, function (tag) { return managementService.deleteAclTag(tag); }))
                                          .done($.proxy(function () { this.deferred.resolve(tags); this.deferred = null; }, this))
                                          .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
                              }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

    _confirmDelete: function(tags) {
        return jscape.ConfirmDialog.confirm(window.R.string.ACL_CONFIRM_DELETE_TAG_TITLE,
                                            window.R.string.ACL_CONFIRM_DELETE_TAG_MESSAGE.supplant({0: tags.join(", ")}));
    }
});

/** @class */
jscape.settings.AddAclTagDialog = Class.extend(
    /** @lends {jscape.settings.AddAclTagDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-setting-acl-addtag").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, height: "auto",
            modal: true, resizable: false, closable: true, closed: true,
            buttons: [
                {text: window.R.string.DIALOG_BUTTON_OK || "Ok", handler: $.proxy(this._onSubmit, this)},
                {text: window.R.string.DIALOG_BUTTON_CANCEL || "Cancel", handler: $.proxy(this._onCancel, this)}
            ],
            onClose: $.proxy(function () {if (!this.closing) { this._onCancel(); }}, this)
        }).show();

        this.title = this.dialog.dialog("header").text() || "";
        this.fieldset = $("fieldset[name=acltagfields]", this.dialog);

        this.name = $("input[name=name]", this.fieldset).textbox({required: true, validType: ["non_empty", "acl_tag_unique"], missingMessage: window.R.string.ACL_ERROR_BAD_TAG, invalidMessage: window.R.string.ACL_ERROR_BAD_TAG, width: "60%"});
    },

    getName: function() {
        return this.name.textbox("getValue");
    },

    setName: function(value) {
        this.name.textbox("setValue", value);
        this.name.textbox("textbox").validatebox("options").validType = ["non_empty", "acl_tag_unique['" + value + "']"];
        this.dialog.dialog("setTitle", this.title.supplant({0: value}));
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.name.textbox("textbox").focus();
    },

    hide: function () {
        if (!this._closed()) {
            this.closing = true;
            this.dialog.dialog("close");
            this.fieldset.form("disableValidation");
            this.closing = false;
        }
    },

    destroy: function() {
        this.dialog.dialog("destroy");
    },

    _onSubmit: function () {
        if (this.fieldset.form("validate")) {
            this.hide();
            this.listener.onSubmit(this);
        }
    },

    _onCancel: function () {
        this.hide();
        this.listener.onCancel(this);
    },

    _closed: function () {
        return !!this.dialog.dialog("options").closed;
    }
});