jscape.settings = jscape.settings || {};

/** @class */
jscape.ServerManagementServiceConfiguration = Class.extend(
    /** @lends {jscape.ServerManagementServiceConfiguration.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c) {
        this.host = a || "";
        this.port = parseInt(b) || 0;
        this.timeout = parseInt(c) || 0;
    },

    clone: function() {
        return new jscape.ServerManagementServiceConfiguration(this.host, this.port, this.timeout);
    }
});
jscape.ServerManagementServiceConfiguration.fromJSON = function(data) {
    return data ? new jscape.ServerManagementServiceConfiguration(data.host, data.port, data.timeout) : null;
};

/** @class */
jscape.ManagementAccessServiceConfiguration = Class.extend(
    /** @lends {jscape.ManagementAccessServiceConfiguration.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d, e) {
        this.rules = a || [];
        this.authTimeout = parseInt(b) || null;
        this.maxAuthAttempts = parseInt(c) || null;
        this.maxAuthTimeout = parseInt(d) || null;
        this.restoringTimeout = parseInt(e) || null;
    }
});
jscape.ManagementAccessServiceConfiguration.fromJSON = function (data) {
    return new jscape.ManagementAccessServiceConfiguration($.map(data.rules || [], jscape.IPAccessRule.fromJSON),
                                                           data.authTimeout,
                                                           data.maxAuthAttempts,
                                                           data.maxAuthTimeout,
                                                           data.restoringTimeout);
};

/** @class */
jscape.ManagementLogServiceConfiguration = Class.extend(
    /** @lends {jscape.ManagementLogServiceConfiguration.prototype} */
{
    /** @constructor */
    initialize: function(a) {
        this.recordExpirationPeriodMillis = parseInt(a) || null;
    }
});
jscape.ManagementLogServiceConfiguration.fromJSON = function(data) {
    return data ? new jscape.ManagementLogServiceConfiguration(data.recordExpirationPeriodMillis) : null;
};

/** @class */
jscape.ManagementLogRecords = Class.extend(
    /** @lends {jscape.ManagementLogRecords.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d, e, f, g) {
        this.date = parseInt(a) || 0;
        this.clientHost = b || "";
        this.clientPort = parseInt(c) || 0;
        this.user = d || "";
        this.domain = e || "";
        this.action = f || "";
        this.description = g || "";
    }
});
jscape.ManagementLogRecords.fromJSON = function(data) {
    return data ? new jscape.ManagementLogRecords(data.date, data.clientHost, data.clientPort, data.user, data.domain, data.action, data.description) : null;
};

/** @class */
jscape.IPAccessRule = Class.extend(
    /** @lends {jscape.IPAccessRule.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c) {
        this.mask = a;
        this.reason = b || "";
        this.allowed = c || false;
    }
});
jscape.IPAccessRule.fromJSON = function(data) {
    return data ? new jscape.IPAccessRule(data.mask, data.reason, data.allowed) : null;
};

/** @class */
jscape.Administrator = Class.extend(
    /** @lends {jscape.Administrator.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d, e) {
        this.name = a;
        this.username = b;
        this.enabled = c || false;
        this.roles = d || [];
        this.systemAdministrator = e || false;
    }
});
jscape.Administrator.fromJSON = function(data) {
    return data ? new jscape.Administrator(data.name, data.username, data.enabled, data.roles, data.systemAdministrator) : null;
};

/** @class */
jscape.ManagementRole = Class.extend(
    /** @lends {jscape.ManagementRole.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c) {
        this.name = a || "";
        this.permissions = b || [];
        this.domainAccesses = c || [];
    }
});
jscape.ManagementRole.fromJSON = function(data) {
    return data ? new jscape.ManagementRole(data.name, data.permissions, $.map(data.domainAccesses || [], jscape.DomainAccess.fromJSON)) : null;
};

/** @class */
jscape.DomainAccess = Class.extend(
    /** @lends {jscape.DomainAccess.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d) {
        this.domainName = a || "";
        this.permissions = b || [];
        this.tags = c || [];
        this.enabled = d != undefined ? !!d : false;
    }
});
jscape.DomainAccess.fromJSON = function(data) {
    return data ? new jscape.DomainAccess(data.domainName, data.permissions, data.tags, data.enabled) : null;
};


/** @class */
jscape.settings.ManagerServiceModule = jscape.settings.SettingsModule.extend(
    /** @lends {jscape.settings.ManagerServiceModule.prototype} */
{
    onCreate: function(managementService, serverAddresses) {
        this._super.apply(this, arguments);

        this.serviceController = new jscape.settings.ManagerServiceController();
        this.accessController = new jscape.settings.ManagerAccessController();
        this.logServiceController = new jscape.settings.ManagerLogServiceController();
        this.authenticationController = new jscape.settings.AuthenticationServiceController();
        this.administratorsController = new jscape.settings.AdministratorsController();
        this.rolesController = new jscape.settings.ManagementRolesController();
        this.tagsController = new jscape.settings.AclTagsController();

        this.serviceController.onCreate(managementService, serverAddresses);
        this.accessController.onCreate(managementService);
        this.logServiceController.onCreate(managementService);
        this.authenticationController.onCreate(managementService);
        this.administratorsController.onCreate(managementService);
        this.rolesController.onCreate(managementService);
        this.tagsController.onCreate(managementService);
    },

    onStart: function () {
        this.serviceController.onStart();
        this.accessController.onStart();
        this.logServiceController.onStart();
        this.authenticationController.onStart();
        this.administratorsController.onStart();
        this.rolesController.onStart(this);
        this.tagsController.onStart();
    },

    onPause: function () {
        this._confirmAndApply($.grep([
                                         this.serviceController,
                                         this.accessController,
                                         this.logServiceController,
                                         this.authenticationController
                                     ],
                                     function (controller) {
                                         return controller.hasChanged();
                                     }));
    },

    onResume: function () {
        this.serviceController.onResume();
        this.accessController.onResume();
        this.logServiceController.onResume();
        this.authenticationController.onResume();
        this.administratorsController.onResume();
        this.rolesController.onResume();
        this.tagsController.onResume();
    },

    onChangeRoles: function(roles) {
        this.authenticationController.onChangeRoles(roles);
    }
});

/** @class */
jscape.settings.ManagerServiceController = Class.extend(
    /** @lends {jscape.settings.ManagerServiceController.prototype} */
{
    DEFAULT_CONFIG: new jscape.ServerManagementServiceConfiguration("0.0.0.0", 10880, 180 * 1000),

    config: null,
    view: null,

    onCreate: function(managementService, addresses) {
        this.managementService = managementService;
        this.inetAddresses = addresses;
    },

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

    onResume: function() {
        this.managementService.getManagementServiceConfiguration()
                .done($.proxy(function (config) { this.config = config; }, this))
                .fail($.proxy(function () { this.config = this.DEFAULT_CONFIG; }, this))
                .always($.proxy(function () { this.view && this._setupView(this.config); }, this));
    },

    hasChanged: function () {
        return this.config != null && !Object._equals(this.config, this._createConfiguration());
    },

    onApply: function() {
        var config = this._createConfiguration();
        return this.managementService.setManagementServiceConfiguration(config)
                .done($.proxy(function () { this.config = config; }, this))
                .done($.proxy(this._fireApplySuccess, this));
    },

    onDiscard: function () {
        this._setupView(this.config);
        return $.Deferred().resolve().promise();
    },

    _createConfiguration: function() {
        return new jscape.ServerManagementServiceConfiguration(this.view.getHost(),
                                                               this.view.getPort(),
                                                               this.view.getTimeout());
    },

    _setupView: function(config) {
        this.view.setHosts(this.inetAddresses);

        if (config) {
            this.view.setHost(config.host);
            this.view.setPort(config.port);
            this.view.setTimeout(config.timeout);
        }
    },

    _fireApplySuccess: function () {
        $(document).triggerHandler("broadcast", [window.R.string.APPLICATION_SUCCESS_APPLY]);
    }
});

/** @class */
jscape.settings.ManagerServiceView = Class.extend(
    /** @lends {jscape.settings.ManagerServiceView.prototype} */
{
    TIMEOUT_FACTOR: 1000,

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

        this.host = $("select[name=host]", this.fieldset).combobox({required: true, validType: "non_empty", missingMessage: window.R.string.MANAGER_SERVICE_ERROR_BAD_HOST, width: 170, panelWidth: 255, panelHeight: "auto"});
        this.port = $("input[name=port]", this.fieldset).numberspinner({required: true, min: 1, max: 65535, width: 100});
        this.timeout = $("input[name=timeout]", this.fieldset).numberspinner({required: true, min: 1, max: 9999, width: 80});

        this.applyButton = $("a[role=button][name=apply]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$ApplyButton, this)});
        this.discardButton = $("a[role=button][name=discard]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$DiscardButton, this)});
    },

    show: function(listener) {
        this.listener = listener || jscape.settings.ManagerServiceView.LISTENER;
    },

    getHost: function() {
        return this.host.combobox("getValue");
    },

    setHost: function(value) {
        this.host.combobox("select", value).combobox("validate");
    },

    setHosts: function(values) {
        this.host.combobox("loadData", values);
    },

    getPort: function() {
        return parseInt(this.port.numberspinner("getValue"));
    },

    setPort: function(value) {
        this.port.numberspinner("setValue", value).numberspinner("validate");
    },

    getTimeout: function() {
        return parseInt(this.timeout.numberspinner("getValue")) * this.TIMEOUT_FACTOR;
    },

    setTimeout: function(value) {
        this.timeout.numberspinner("setValue", value / this.TIMEOUT_FACTOR).numberspinner("validate");
    },

    _onClick$ApplyButton: function() {
        if (this.fieldset.form("validate")) {
            this.applyButton.linkbutton("disable");
            this.listener.onApply()
                    .always($.proxy(function() { this.applyButton.linkbutton("enable"); }, this));
        }
    },

    _onClick$DiscardButton: function() {
        this.listener.onDiscard();
    }
});
jscape.settings.ManagerServiceView.LISTENER = {
    onApply: $.noop,
    onDiscard: $.noop
};

/** @class */
jscape.settings.ManagerAccessController = Class.extend(
    /** @lends {jscape.settings.ManagerAccessController.prototype} */
{
    DEFAULT_CONFIG: new jscape.ManagementAccessServiceConfiguration([
            new jscape.IPAccessRule("*.*.*.*", "default access", true),
            new jscape.IPAccessRule("*:*:*:*:*:*:*:*", "default access", true)
        ], 10 * 1000, 3, 60 * 1000, 60 * 60 * 1000),

    config: null,
    rules: null,
    view: null,

    /** @constructor */
    initialize: function() {
        this._initValidators();
    },

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

        this.addController = new jscape.settings.AddManagerAccessRuleController();
        this.editController = new jscape.settings.EditManagerAccessRuleController();
        this.deleteController = new jscape.settings.DeleteManagerAccessRuleController();
    },

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

    onResume: function() {
        this.managementService.getManagementAccessServiceConfiguration()
                .done($.proxy(function (config) { this.config = config; }, this))
                .fail($.proxy(function () { this.config = this.DEFAULT_CONFIG; }, this))
                .always($.proxy(function () {
                            this.rules = this._copyRules(this.config);
                            this.view && this._setupView(this.config, this.rules);
                        }, this));
    },

    hasChanged: function () {
        return this.config != null && !Object._equals(this.config, this._createConfiguration());
    },

    onApply: function () {
        var config = this._createConfiguration();
        return this.managementService.setManagementAccessServiceConfiguration(config)
                .done($.proxy(function () { this.config = config; }, this))
                .done($.proxy(this._fireApplySuccess, this));
    },

    onDiscard: function () {
        this.rules = this._copyRules(this.config);
        this._setupView(this.config, this.rules);
        return $.Deferred().resolve().promise();
    },

    onAddRule: function() {
        this.addController.start().done($.proxy(this._ruleAdded, this));
    },

    onEditRule: function(rule) {
        var index = $.inArray(rule, this.rules);
        if (index != -1) {
            this.editController.start(rule)
                    .done($.proxy(this._ruleEdited, this, index));
        }
    },

    onDeleteRule: function(rules) {
        this.deleteController.start(rules)
                .done($.proxy(this._ruleDeleted, this, rules));
    },

    onRuleUp: function(rule) {
        var index = $.inArray(rule, this.rules);
        this.rules.splice(index - 1, 2, this.rules[index], this.rules[index - 1]);
        this.view.setAccessRules(this.rules);
        this.view.selectItem(rule);
    },

    onRuleDown: function(rule) {
        var index = $.inArray(rule, this.rules);
        this.rules.splice(index, 2, this.rules[index + 1], this.rules[index]);
        this.view.setAccessRules(this.rules);
        this.view.selectItem(rule);
    },

    _ruleAdded: function(rule) {
        this.rules.push(rule);
        this.view.setAccessRules(this.rules);
        this.view.selectItem(rule);
    },

    _ruleEdited: function(index, rule) {
        this.rules[index] = rule;
        this.view.setAccessRules(this.rules);
        this.view.selectItem(rule);
    },

    _ruleDeleted: function(rules) {
        var index = -1;
        while (rules.length) {
            var rule = rules.pop();
            index = $.inArray(rule, this.rules);
            if (index != -1) {
                this.rules.splice(index, 1);
            }
        }
        this.view.setAccessRules(this.rules);

        if (this.rules.length) {
            index = Math.max(0, Math.min(index, this.rules.length - 1));
            this.view.selectItem(this.rules[index]);
        }
    },

    _copyRules: function(config) {
        return $.extend(true, [], config ? config.rules : []);
    },

    _createConfiguration: function() {
        return new jscape.ManagementAccessServiceConfiguration(this.view.getAccessRules(),
                                                               this.view.getAuthTimeout(),
                                                               this.view.getDisableIpAttempts(),
                                                               this.view.getDisableIpTimeout(),
                                                               this.view.getRestoringTimeout());
    },

    _setupView: function(config, rules) {
        this.view.setAccessRules(rules || []);
        this.view.setAuthTimeout(config.authTimeout);
        this.view.setDisableIpAttempts(config.maxAuthAttempts);
        this.view.setDisableIpTimeout(config.maxAuthTimeout);
        this.view.setRestoringTimeout(config.restoringTimeout);
    },

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

    _ruleNotExists: function(value, excludes) {
        for (var i = 0; i < this.rules.length; i++) {
            var mask = this.rules[i].mask;
            if (excludes && $.inArray(mask, excludes) != -1) {
                continue;
            }
            if (mask == value) {
                return false;
            }
        }
        return true;
    },

    _fireApplySuccess: function () {
        $(document).triggerHandler("broadcast", [window.R.string.APPLICATION_SUCCESS_APPLY]);
    }
});

/** @class */
jscape.settings.IpAccessDialog = Class.extend(
    /** @lends {jscape.settings.IpAccessDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#ssma-dialog-ipaccess").clone().attr("id", Math.random().toString(36).substr(2));
        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=manageripaccessfields]", this.dialog);

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

        this.access = $("input:radio[name=access]", this.fieldset);
        this.accessAllowed = this.access.filter("[value=allowed]");
        this.accessDenied = this.access.filter("[value=denied]");
    },

    getMask: function() {
        return this.mask.textbox("getValue");
    },

    setMask: function(value) {
        this.mask.textbox("setValue", value);
        if (value && value.trim().length) {
            this.mask.textbox("textbox").validatebox("options").validType = ["non_empty", "ipaccess_unique['" + value + "']"];
        }
    },

    getReason: function () {
        return this.reason.textbox("getValue");
    },

    setReason: function (value) {
        this.reason.textbox("setValue", value);
    },

    getAllowed: function () {
        return this.accessAllowed.is(":checked");
    },

    setAllowed: function (value) {
        if (value) {
            this.accessAllowed.prop("checked", true).change();
        } else {
            this.accessDenied.prop("checked", true);
        }
    },

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

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.mask.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;
    }
});

/** @class */
jscape.settings.ManagerAccessView = Class.extend(
    /** @lends {jscape.settings.ManagerAccessView.prototype} */
{
    /** @const */
    TIMEOUT_FACTOR: 1000,
    AUTH_TIMEOUT_FACTOR: 60 * 1000,

    selection: null,
    listener: null,

    /** @constructor */
    initialize: function() {
        this.selection = [];

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

        this.timeoutEnabled = $("input[name=timeoutenabled]:checkbox", this.fieldset).change($.proxy(function() {
            this.timeout.numberspinner(this.timeoutEnabled.is(":checked") ? "enable" : "disable");
        }, this));
        this.timeout = $("input[name=timeout]", this.fieldset).numberspinner({min: 1, max: 10, value: 10, disabled: true, width: 60});

        this.disableIp = $("input[name=disableip]:checkbox", this.fieldset).change($.proxy(function() {
            var checked = this.disableIp.is(":checked");
            $.each([this.disableIpAttempts, this.disableIpTimeout, this.restoringTimeout], function (_, spinner) {
                spinner.numberspinner(checked ? "enable" : "disable");
            });
        }, this));
        this.disableIpAttempts = $("input[name=disableipattempts]", this.fieldset).numberspinner({min: 1, max: 10, value: 3, required: true, disabled: true, width: 60});
        this.disableIpTimeout = $("input[name=disableiptimeout]", this.fieldset).numberspinner({min: 1, max: 10, value: 3, required: true, disabled: true, width: 60});
        this.restoringTimeout = $("input[name=restoringtimeout]", this.fieldset).numberspinner({min: 0, max: 1000, required: true, disabled: true, width: 60});

        this.data = $("table[role=grid][aria-label=accessrules]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            ctrlSelect: true,
            remoteSort: false,
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onLoadSuccess: $.proxy(this._adjustRowSelection, this),
            onDblClickRow: $.proxy(this._onClick$EditButton, this),
            columns: [[
                {field: "mask", title: $("thead th:nth-child(1)", this.data).text(), width: 60},
                {field: "allowed", title: $("thead th:nth-child(2)", this.data).text(), width: 15, formatter: function (value) {return value ? "allowed" : "denied"}, styler: function (value) {return value ? "color:green" : "color:red"}},
                {field: "reason", title: $("thead th:nth-child(3)", this.data).text(), width: 25}
            ]]
        });

        this.moveUpButton = $("a[role=button][name=moveup]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$MoveUpButton, this)});
        this.moveDownButton = $("a[role=button][name=movedown]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$MoveDownButton, 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)});
        this.applyButton = $("a[role=button][name=apply]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$ApplyButton, this)});
        this.discardButton = $("a[role=button][name=discard]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$DiscardButton, this)});
    },

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

    getAccessRules: function() {
        return this.data.datagrid("getRows");
    },

    setAccessRules: function(values) {
        this.data.datagrid("loading").datagrid("loadData", values).datagrid("loaded");
    },

    selectItem: function(value) {
        this.data.datagrid("selectRow", this.data.datagrid("getRowIndex", value));
    },

    getAuthTimeout: function() {
        return this.timeoutEnabled.is(":checked") ? parseInt(this.timeout.numberspinner("getValue")) * this.TIMEOUT_FACTOR : null;
    },

    setAuthTimeout: function(value) {
        if (value) {
            this.timeout.numberspinner("setValue", value / this.TIMEOUT_FACTOR);
        }
        this.timeoutEnabled.prop("checked", !!value).change();
    },

    getDisableIpAttempts: function() {
        return this.disableIp.is(":checked") ? parseInt(this.disableIpAttempts.numberspinner("getValue")) : null;
    },

    setDisableIpAttempts: function(value) {
        if (value) {
            this.disableIpAttempts.numberspinner("setValue", value);
        }
        this.disableIp.prop("checked", !!value).change();
    },

    getDisableIpTimeout: function() {
        return this.disableIp.is(":checked") ? parseInt(this.disableIpTimeout.numberspinner("getValue")) * this.AUTH_TIMEOUT_FACTOR : null;
    },

    setDisableIpTimeout: function(value) {
        if (value) {
            this.disableIpTimeout.numberspinner("setValue", value / this.AUTH_TIMEOUT_FACTOR);
        }
    },

    getRestoringTimeout: function() {
        return this.disableIp.is(":checked") ? parseInt(this.restoringTimeout.numberspinner("getValue")) * this.AUTH_TIMEOUT_FACTOR : null;
    },

    setRestoringTimeout: function(value) {
        if (value) {
            this.restoringTimeout.numberspinner("setValue", value / this.AUTH_TIMEOUT_FACTOR);
        }
    },

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

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

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

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

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

    _onClick$ApplyButton: function() {
        if (this.fieldset.form("validate")) {
            this.applyButton.linkbutton("disable");
            this.listener.onApply()
                    .always($.proxy(function() { this.applyButton.linkbutton("enable"); }, this));
        }
    },

    _onClick$DiscardButton: function() {
        this.listener.onDiscard();
    },

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

    _adjustButtonsState: function() {
        var index = this.selection.length == 1 ? this.data.datagrid("getRowIndex", this.selection[0]) : -1;
        var count = this.data.datagrid("getRows").length;

        this.moveUpButton.linkbutton(index > 0 && index <= count - 1 ? "enable" : "disable");
        this.moveDownButton.linkbutton(index >= 0 && index < count - 1 ? "enable" : "disable");
        this.editButton.linkbutton(this.selection.length == 1 ? "enable" : "disable");
        this.deleteButton.linkbutton(this.selection.length != 0 ? "enable" : "disable");
    }
});
jscape.settings.ManagerAccessView.LISTENER = {
    onAddRule: $.noop,
    onEditRule: $.noop,
    onDeleteRule: $.noop,
    onRuleUp: $.noop,
    onRuleDown: $.noop,
    onApply: $.noop,
    onDiscard: $.noop
};

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

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

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

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

    _createAccessRule: function (dialog) {
        return new jscape.IPAccessRule(dialog.getMask(), dialog.getReason(), dialog.getAllowed());
    },

    _setupDialog: function () {
        var dialog = new jscape.settings.IpAccessDialog();
        dialog.setTitle(window.R.string.MANAGER_ACCESS_ADD_TITLE);
        dialog.setAllowed(true);
        return dialog;
    }
});

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

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

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

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

    _createAccessRule: function (dialog) {
        return new jscape.IPAccessRule(dialog.getMask(), dialog.getReason(), dialog.getAllowed());
    },

    _setupDialog: function (rule) {
        var dialog = new jscape.settings.IpAccessDialog();
        dialog.setTitle(window.R.string.MANAGER_ACCESS_EDIT_TITLE);
        dialog.setMask(rule.mask);
        dialog.setReason(rule.reason);
        dialog.setAllowed(rule.allowed);
        return dialog;
    }
});

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

    start: function (rules) {
        var text = $.map(rules, function (rule) { return rule.mask; }).join(", ");
        return jscape.ConfirmDialog.confirm(window.R.string.MANAGER_ACCESS_CONFIRM_DELETE_TITLE,
                                            window.R.string.MANAGER_ACCESS_CONFIRM_DELETE_MESSAGE.supplant({0: text}));
    }
});

/** @class */
jscape.settings.AdministratorsController = Class.extend(
    /** @lends {jscape.AdministratorsController.prototype} */
{
    administrators: null,
    view: null,

    /** @constructor */
    initialize: function() {
        this._initValidators();
        this.administrators = [];
    },

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

        this.addController = new jscape.settings.AddAdministratorController();
        this.editController = new jscape.settings.EditAdministratorController();
        this.deleteController = new jscape.settings.DeleteAdministratorController();
        this.stateController = new jscape.settings.ChangeAdministratorStateController();
    },

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

    onResume: function() {
        this.managementService.getAdministrators()
                .done($.proxy(function (admins) { this.administrators = admins; }, this))
                .fail($.proxy(function () { this.administrators = []; }, this))
                .always($.proxy(function () { this.view && this._setupView(this.administrators); }, this));
    },

    hasChanged: function () {
        return false;
    },

    onApply: function () {
        return $.Deferred().resolve(this.administrators).promise();
    },

    onDiscard: function () {
        return $.Deferred().resolve().promise();
    },

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

    onEdit: function(admin) {
        var index = $.inArray(admin, this.administrators);
        if (index != -1) {
            this.editController.start(this.managementService, admin, this._hasEnabledAdministrators(admin))
                    .done($.proxy(this._adminEdited, this, index))
        }
    },

    onDelete: function(admin) {
        if (!this._hasEnabledAdministrators(admin)) {
            this._alertNoEnabledAdministrators();
            return;
        }
        this.deleteController.start(this.managementService, [admin])
                .done($.proxy(this._adminDeleted, this, admin));
    },

    onEnable: function(admin) {
        var index = $.inArray(admin, this.administrators);
        if (index != -1) {
            this.stateController.start(this.managementService, admin, true)
                    .done($.proxy(this._adminStateChanged, this, index));
        }
    },

    onDisable: function (admin) {
        if (!this._hasEnabledAdministrators(admin)) {
            this._alertNoEnabledAdministrators();
            return;
        }
        var index = $.inArray(admin, this.administrators);
        if (index != -1) {
            this.stateController.start(this.managementService, admin, false)
                    .done($.proxy(this._adminStateChanged, this, index));
        }
    },

    _adminAdded: function(admin) {
        this.administrators.push(admin);
        this.view.setAdministrators(this.administrators);
        this.view.selectItem(admin);
    },

    _adminEdited: function(index, admin) {
        this.administrators[index] = admin;
        this.view.setAdministrators(this.administrators);
        this.view.selectItem(admin);
    },

    _adminDeleted: function (admin) {
        var index = $.inArray(admin, this.administrators);
        if (index != -1) {
            this.administrators.splice(index, 1);
            this.view.setAdministrators(this.administrators);
        }
        if (this.administrators.length) {
            index = Math.max(0, Math.min(index, this.administrators.length - 1));
            this.view.selectItem(this.administrators[index])
        }
    },

    _adminStateChanged: function(index, admin) {
        this.administrators[index] = admin;
        this.view.setAdministrators(this.administrators);
        this.view.selectItem(admin);
    },


    _setupView: function(administrators) {
        this.view.setAdministrators([].concat(administrators || []));
    },

    _alertNoEnabledAdministrators: function() {
        $.messager.alert(window.R.string.APPLICATION_ERROR_TITLE,
                         window.R.string.ADMINISTRATORS_ERROR_NO_ENABLED_ADMINS,
                         "error");
    },

    _initValidators: function() {
        var v = new jscape.Validator();
        v.nonEmptyRule("admin_login_nonempty", window.R.string.ADMINISTRATORS_ERROR_BAD_LOGIN);
        v.rule("admin_unique", $.proxy(this._loginNotExists, this), window.R.string.ADMINISTRATORS_ERROR_LOGIN_EXISTS);
        v.rule("admin_confirmpwd", $.proxy(this._passwordConfirmed, this), window.R.string.ADMINISTRATORS_ERROR_PASSWORDS_NOT_MATCH);
    },

    _hasEnabledAdministrators: function(exceptAdmin) {
        return $.map(this.administrators, function(admin) { return admin != exceptAdmin && admin.enabled ? admin : null; }).length != 0;
    },

    _loginNotExists: function(value) {
        for (var i = 0; i < this.administrators.length; i++) {
            if (this.administrators[i].username == value) {
                return false;
            }
        }
        return true;
    },

    _passwordConfirmed: function(value, params) {
        return value == $(params[0]).val();
    }
});

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

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

    onSubmit: function(dialog) {
        var admin = this._createAdministrator(dialog);
        dialog.destroy();
        this.managementService.addAdministrator(admin)
                .done($.proxy(function(admin) { this.deferred.resolve(admin); 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;
    },

    _createAdministrator: function(dialog) {
        var admin = new jscape.Administrator(dialog.getName(), dialog.getLogin(), dialog.getEnabled(), dialog.getRoles(), dialog.isSystem());
        admin.password = dialog.getPassword();
        return admin;
    },

    _setupDialog: function() {
        var dialog = new jscape.settings.AddAdministratorDialog();
        dialog.setEnabled(true);
        dialog.setSystem(false);
        this.managementService.getManagementRoles()
                .done(function(roles) { dialog.setAvailableRoles(roles); });
        return dialog;
    }
});

/** @class */
jscape.settings.AddAdministratorDialog = Class.extend(
    /** @lends {jscape.settings.AddAdministratorDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#ssmsadmin-dialog-add").clone().attr("id", Math.random().toString(36));
        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=administratorfileds]", this.dialog);

        this.name = $("input[name=name]", this.fieldset).textbox({width: "60%"});
        this.enabled = $("input[name=enabled]:checkbox", this.fieldset);
        this.login = $("input[name=login]", this.fieldset).textbox({required: true, validType: ["admin_login_nonempty", "admin_unique"], missingMessage: window.R.string.ADMINISTRATORS_ERROR_BAD_LOGIN, width: "60%"});

        this.password = $("input[name=password]", this.fieldset).textbox({required: true, missingMessage: window.R.string.ADMINISTRATORS_ERROR_BAD_PASSWORD, width: "50%"});
        this.confirmPassword = $("input[name=confirmpwd]", this.fieldset).textbox({
            required: true,
            validType: "admin_confirmpwd['#" + this.password.textbox("textbox").attr("id") + "']",
            missingMessage: window.R.string.ADMINISTRATORS_ERROR_BAD_PASSWORD,
            width: "50%"
        });

        this.role = $("select[name=role]", this.fieldset).combobox({required: true, multiple: true, editable: false, width: 170});
        this.system = $("input[name=system]:checkbox", this.fieldset).change($.proxy(function() {
            this.role.combobox(this.isSystem() ? "disable" : "enable").combobox(this.isSystem() ? "disableValidation" : "enableValidation");
        }, this));
    },

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

    getLogin: function() {
        return this.login.textbox("getValue");
    },

    getPassword: function() {
        return this.password.textbox("getValue");
    },

    getEnabled: function() {
        return this.enabled.prop("checked");
    },

    setEnabled: function(value) {
        this.enabled.prop("checked", !!value);
    },

    getRoles: function() {
        return this.role.combobox("options").disabled ? [] : this.role.combobox("getValues");
    },

    setRoles: function(values) {
        this.role.combobox("setValues", values);
    },

    setAvailableRoles: function(values) {
        this.role.combobox("loadData", values);
    },

    isSystem: function() {
        return this.system.is(":checked");
    },

    setSystem: function(value) {
        this.system.prop("checked", !!value).change();
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.system.change();
        this.login.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;
    }
});

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

    start: function (managementService, admin, allowChangeEnabledState) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        this.username = admin.username;
        this.managementService.getAdministrator(this.username)
                .done($.proxy(function (admin) {
                          this.administrator = admin;
                          this._setupDialog(admin, allowChangeEnabledState).show(this);
                      }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var admin = this._createAdministrator(dialog);
        dialog.destroy();
        this.managementService.setAdministrator(this.administrator.username, admin)
                .done($.proxy(function() { this.deferred.resolve(admin); 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;
    },

    onChangePassword: function (dialog) {
        var controller = new jscape.settings.ChangePasswordController();
        controller.start(this.managementService, this.username)
                .done($.proxy(function () {
                          this.managementService.getAdministrator(this.username)
                                  .done($.proxy(function (administrator) {
                                            administrator = $.extend(administrator, this._createAdministrator(dialog));
                                            this._updateDialog(dialog, administrator);
                                        }, this))
                                  .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
                      }, this));
    },

    _createAdministrator: function (dialog) {
        return new jscape.Administrator(dialog.getName(), this.username, dialog.getEnabled(), dialog.getRoles(), dialog.isSystem());
    },

    _updateDialog: function (dialog, admin) {
        dialog.setName(admin.name);
        dialog.setLogin(admin.username);
        dialog.setEnabled(admin.enabled);
        dialog.setRoles(admin.roles);
        dialog.setSystem(admin.systemAdministrator);
    },

    _setupDialog: function (admin, allowChangeEnabledState) {
        var dialog = new jscape.settings.EditAdministratorDialog();
        dialog.setName(admin.name);
        dialog.setLogin(admin.username);
        dialog.setEnabled(admin.enabled);
        dialog.setEnabledChangeAllowed(allowChangeEnabledState);
        dialog.setSystem(admin.systemAdministrator);
        this.managementService.getManagementRoles()
                .done(function (roles) {
                          dialog.setAvailableRoles(roles);
                          dialog.setRoles(admin.roles);
                      });
        return dialog;
    }
});

/** @class */
jscape.settings.EditAdministratorDialog = Class.extend(
    /** @lends {jscape.settings.EditAdministratorDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#ssmsadmin-dialog-edit").clone().attr("id", Math.random().toString(36).substr(2));
        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=administratorfileds]", this.dialog);

        this.name = $("input[name=name]", this.fieldset).textbox({width: "60%"});
        this.login = $("input[name=login]", this.fieldset).textbox({disabled: true, width: "50%"});
        this.enabled = $("input[name=enabled]:checkbox", this.fieldset);
        this.role = $("select[name=role]", this.fieldset).combobox({required: true, multiple: true, editable: false, width: 170});
        this.system = $("input[name=system]:checkbox", this.fieldset).change($.proxy(function() {
            this.role.combobox(this.isSystem() ? "disable" : "enable").combobox(this.isSystem() ? "disableValidation" : "enableValidation");
        }, this));

        this.changePasswordButton = $("a[role=button][name=changepwd]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$ChangePasswordButton, this)});
    },

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

    setName: function(value) {
        this.name.textbox("setValue", value);
    },

    setLogin: function(value) {
        this.login.textbox("setValue", value);
    },

    getEnabled: function() {
        return this.enabled.prop("checked");
    },

    setEnabled: function(value) {
        this.enabled.prop("checked", value);
    },

    setEnabledChangeAllowed: function(value) {
        if (!value) {
            this.enabled.prop("readonly", true).prop("disabled", true).click(function() { return false; });
        }
    },

    getRoles: function() {
        return this.role.combobox("options").disabled ? [] : this.role.combobox("getValues");
    },

    setRoles: function(values) {
        this.role.combobox("setValues", values);
    },

    setAvailableRoles: function(values) {
        this.role.combobox("loadData", values);
    },

    isSystem: function() {
        return this.system.is(":checked");
    },

    setSystem: function(value) {
        this.system.prop("checked", !!value).change();
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.system.change();
        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");
    },

    _onClick$ChangePasswordButton: function() {
        this.listener.onChangePassword(this);
    },

    _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;
    }
});
jscape.settings.EditAdministratorDialog.LISTENER = {
    onChangePassword: $.noop
};

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

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

    onSubmit: function(dialog) {
        var password = dialog.getPassword();
        dialog.destroy();
        this.managementService.changeAdministratorPassword(this.username, password)
                .done($.proxy($.proxy(this._showPasswordChangedMessage, this)))
                .done($.proxy(function() { this.deferred.resolve(); 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.ChangePasswordDialog();
    },

    _showPasswordChangedMessage: function () {
        $.messager.alert(window.R.string.ADMINISTRATORS_PASSWORD_CHANGED_TITLE,
                         window.R.string.ADMINISTRATORS_PASSWORD_CHANGED_MESSAGE,
                         "info");
    }
});

/** @class */
jscape.settings.ChangePasswordDialog = Class.extend(
    /** @lends {jscape.settings.ChangePasswordDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#ssmsadmin-dialog-changepwd").clone().attr("id", Math.random().toString(36).substr(2));
        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=administratorfileds]", this.dialog);

        this.password = $("input[name=password]", this.fieldset).textbox({required: true, missingMessage: window.R.string.ADMINISTRATORS_ERROR_BAD_PASSWORD, width: "50%"});
        this.confirmPassword = $("input[name=confirmpwd]", this.fieldset).textbox({
            required: true,
            validType: "admin_confirmpwd['#" + this.password.textbox("textbox").attr("id") + "']",
            missingMessage: window.R.string.ADMINISTRATORS_ERROR_BAD_PASSWORD, invalidMessage: window.R.string.ADMINISTRATORS_ERROR_PASSWORDS_NOT_MATCH,
            width: "50%"
        });
    },

    getPassword: function() {
        return this.password.textbox("getValue");
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.password.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;
    }
});

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

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

    cancel: function() {
        this.deferred.reject();
        this.deferred = null;
    },

    _confirmDelete: function (admins) {
        var text = $.map(admins, function (admin) { return admin.username; }).join(", ");
        return jscape.ConfirmDialog.confirm(window.R.string.ADMINISTRATORS_CONFIRM_DELETE_TITLE,
                                            window.R.string.ADMINISTRATORS_CONFIRM_DELETE_MESSAGE.supplant({0: text}));
    },

    _deleteConfirmed: function(admins) {
        $.when.apply(jQuery, $.map(admins, $.proxy(function(admin) {
            return this.managementService.deleteAdministrator(admin.username);
        }, this)))
                .done($.proxy(function() { this.deferred.resolve(admins); this.deferred = null; }, this))
                .fail($.proxy(this.cancel, this));
    }
});

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

    start: function(managementService, admin, enabled) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        enabled = !!enabled;
        this.managementService.getAdministrator(admin.username)
                .then($.proxy(function(administrator) {
                                  administrator.enabled = enabled;
                                  return this.managementService.setAdministrator(administrator.username, administrator);
                              }, this))
                .done($.proxy(function() { admin.enabled = enabled; this.deferred.resolve(admin); this.deferred = null; }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    }
});

/** @class */
jscape.settings.AdministratorsView = Class.extend(
    /** @lends {jscape.settings.AdministratorsView.prototype} */
{
    selection: null,
    listener: null,

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

        this.data = $("table[role=grid][aria-label=systemadministrators]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            singleSelect: true,
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onLoadSuccess: $.proxy(this._adjustRowSelection, this),
            onSortColumn: $.proxy(this._adjustButtonsState, this),
            onDblClickRow: $.proxy(this._onClick$EditButton, this),
            columns: [[
                {field: "name", title: $("thead th:nth-child(1)", this.data).text(), sortable: true, width: 130},
                {field: "username", title: $("thead th:nth-child(2)", this.data).text(), sortable: true, width: 130},
                {field: "roles", title: $("thead th:nth-child(3)", this.data).text(), sortable: true, width: 150, formatter: function (value) { return value.join(", ").escapeXml(); }},
                {field: "enabled", title: $("thead th:nth-child(4)", this.data).text(), sortable: true, width: 80, formatter: function (value) { return value ? "enabled" : "disabled"; }, styler: function (value) { return value ? "color:green" : "color:grey"; }}
            ]]
        });

        this.enableButton = $("a[role=button][name=enable]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$EnableButton, this)});
        this.disableButton = $("a[role=button][name=disable]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$DisableButton, 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)});
    },

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

    getAdministrators: function() {
        return this.data.datagrid("getRows");
    },

    setAdministrators: function(items) {
        this.data.datagrid("loading").datagrid("loadData", items).datagrid("loaded");
    },

    selectItem: function(value) {
        this.data.datagrid("selectRow", this.data.datagrid("getRowIndex", value));
    },

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

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

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

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

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

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

    _adjustButtonsState: function() {
        var admin = this.selection.length == 1 ? this.selection[0] : null;

        this.enableButton.linkbutton(admin && !admin.enabled ? "enable" : "disable");
        this.disableButton.linkbutton(admin && admin.enabled ? "enable" : "disable");
        this.editButton.linkbutton(admin != null ? "enable" : "disable");
        this.deleteButton.linkbutton(admin != null ? "enable" : "disable");
    }
});
jscape.settings.AdministratorsView.LISTENER ={
    onAdd: $.noop,
    onEdit: $.noop,
    onDelete: $.noop,
    onEnable: $.noop,
    onDisable: $.noop
};

/** @class */
jscape.settings.ManagerLogServiceController = Class.extend(
    /** @lends {jscape.settings.ManagerLogServiceController.prototype} */
{
    DEFAULT_CONFIG: new jscape.ManagementLogServiceConfiguration(),

    config: null,
    view: null,

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

        this.exportController = new jscape.settings.ExportManagerLogRecordsController();
        this.purgeController = new jscape.settings.PurgeManagerLogRecordsController();
    },

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

    onRefresh: function (params) {
        params = jscape.Page.requestParameters(params);
        return this.managementService.listManagementLogRecords(params.startIndex, params.count, params.query)
                .then(jscape.Page.datagridFormatter());
    },

    onResume: function() {
        this.view.refresh();
        this.managementService.getManagementLogServiceConfiguration()
                .done($.proxy(function(config) { this.config = config; }, this))
                .fail($.proxy(function() { this.config = this.DEFAULT_CONFIG; }, this))
                .always($.proxy(function() { this.view && this._setupView(this.config); }, this));
    },

    hasChanged: function() {
        return this.config != null && !Object._equals(this.config, this._createConfiguration());
    },

    onApply: function() {
        var config = this._createConfiguration();
        return this.managementService.setManagementLogServiceConfiguration(config)
                .done($.proxy(function(config) { this.config = config; }, this));
    },

    onDiscard: function() {
        this._setupView(this.config);
    },

    onPurge: function () {
        this.purgeController.start(this.managementService)
                .always($.proxy(function () { this.view.firstPage(); }, this));
    },

    onExport: function() {
        this.exportController.start(this.managementService);
    },

    _createConfiguration: function() {
        return new jscape.ManagementLogServiceConfiguration(this.view.getLogExpirationPeriod());
    },

    _setupView: function(config) {
        if (config) {
            this.view.setLogExpirationPeriod(config.recordExpirationPeriodMillis);
        }
    }
});

/** @class */
jscape.settings.ManagerLogServiceView = Class.extend(
    /** @lends {jscape.settings.ManagerLogServiceView.prototype} */
{
    TIME_FACTOR: 24 * 60 * 60 * 1000,

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

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

        this.logExpires = $("input[name=logexpires]:checkbox", this.fieldset).change($.proxy(function () {
            var enabled = this.logExpires.is(":checked");
            this.logExpiresPeriod.numberspinner(enabled ? "enable" : "disable").numberspinner(enabled ? "enableValidation" : "disableValidation");
        }, this));
        this.logExpiresPeriod = $("input[name=logexpiresperiod]", this.fieldset).numberspinner({required: true, min: 0, max: 1000, value: 0, width: 80});

        this.records = $("table[role=grid][aria-label=records]", this.fieldset);
        this.records.datagrid({
            fit: true,
            fitColumns: true,
            pagination: true,
            search: true,
            remoteSearch: true,
            remoteSort: true,
            sortName: "date",
            sortOrder: "desc",
            columns: [[
                {field: "date", title: $("thead th:nth-child(1)", this.records).text(), sortable: true, formatter: $.proxy(this._formatDate, this), width: 100},
                {field: "clientHost", title: $("thead th:nth-child(2)", this.records).text(), sortable: true, width: 80},
                {field: "clientPort", title: $("thead th:nth-child(3)", this.records).text(), sortable: true, formatter: function(value) { return String(value || "").escapeXml(); }, width: 60},
                {field: "user", title: $("thead th:nth-child(4)", this.records).text(), sortable: true, width: 100},
                {field: "domain", title: $("thead th:nth-child(5)", this.records).text(), sortable: true, width: 100},
                {field: "action", title: $("thead th:nth-child(6)", this.records).text(), sortable: true, width: 150},
                {field: "description", title: $("thead th:nth-child(7)", this.records).text(), sortable: true, width: 200}
            ]],
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            loader: $.proxy(this._onLoad$Data, this),
            onBeforeLoad: function() { return !initializing; },
            onLoadSuccess: $.proxy(this._adjustRowSelection, this)
        });

        this.applyButton = $("a[role=button][name=apply]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$ApplyButton, this)});
        this.discardButton = $("a[role=button][name=discard]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$DiscardButton, this)});
        this.purgeButton = $("a[role=button][name=purge]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$PurgeButton, this)});
        this.exportButton = $("a[role=button][name=export]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$ExportButton, this)});

        initializing = false;
    },

    getLogExpirationPeriod: function() {
        return this.logExpires.is(":checked") ? parseInt(this.logExpiresPeriod.numberspinner("getValue")) * this.TIME_FACTOR : null;
    },

    setLogExpirationPeriod: function(value) {
        if (value != null) {
            this.logExpiresPeriod.numberspinner("setValue", parseInt(value) / this.TIME_FACTOR).numberspinner("validate");
        }
        this.logExpires.prop("checked", value != null).change();
    },

    show: function(listener) {
        this.listener = listener || jscape.settings.ManagerLogServiceView.LISTENER;
        this.records.datagrid("resize");
        this._adjustRowSelection();
    },

    refresh: function(callback) {
        this.records.datagrid("reload", callback);
    },

    firstPage: function() {
        this.records.datagrid("gotoPage", {page: 1});
    },

    _formatDate: function (value) {
        return $.isNumeric(value) && value != 0 ? new Date(value).toLocaleString() : "";
    },

    _onClick$ApplyButton: function() {
        if (this.fieldset.form("validate")) {
            this.applyButton.linkbutton("disable");
            this.listener.onApply()
                    .always($.proxy(function() { this.applyButton.linkbutton("enable"); }, this));
        }
    },

    _onClick$DiscardButton: function() {
        this.listener.onDiscard();
    },

    _onClick$PurgeButton: function() {
        this.listener.onPurge();
    },

    _onClick$ExportButton: function() {
        this.listener.onExport();
    },

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

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

    _adjustButtonsState: function() {
        var enabled = this.records.datagrid("getData").total > 0;
        this.purgeButton.linkbutton(enabled ? "enable" : "disable");
        this.exportButton.linkbutton(enabled ? "enable" : "disable");
    }
});
jscape.settings.ManagerLogServiceView.LISTENER = {
    onRefresh: $.noop,
    onExport: $.noop,
    onPurge: $.noop,
    onApply: $.noop,
    onDiscard: $.noop
};

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

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

    onSubmit: function(dialog) {
        var filename = dialog.getFilename();
        dialog.destroy();
        this.managementService.exportManagementLogRecords(filename)
                .done($.proxy(function() { this.deferred.resolve(); 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.ExportManagerLogRecordsDialog();
    }
});

/** @class */
jscape.settings.ExportManagerLogRecordsDialog = Class.extend(
    /** @lends {jscape.settings.ExportManagerLogRecordsController.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-managerlogs-export").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=managerlogexportfields]", this.dialog);
        this.filename = $("input[name=filename]", this.fieldset).validatebox({required: true});
    },

    getFilename: function() {
        return this.filename.val();
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.filename.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.PurgeManagerLogRecordsController = Class.extend(
    /** @lends {jscape.settings.PurgeManagerLogRecordsController.prototype} */
{
    start: function(managementService) {
        return this._confirmPurge()
                .then(function() { return managementService.purgeManagementLogRecords(); });
    },

    _confirmPurge: function() {
        return jscape.ConfirmDialog.confirm(window.R.string.MANAGER_LOGS_CONFIRM_PURGE_TITLE,
                                            window.R.string.MANAGER_LOGS_CONFIRM_PURGE_MESSAGE,
                                            "warning");
    }
});