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

/** @class */
jscape.FailoverServiceConfiguration = Class.extend(
    /** @lends {jscape.FailoverServiceConfiguration.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d) {
        this.managerConfiguration = a;
        this.ipSubstitutor = b;
        this.enabled = !!c || false;
        this.forceStart = !!d || false;
    },

    clone: function() {
        return $.extend(true, new jscape.FailoverServiceConfiguration(), this);
    }
});
jscape.FailoverServiceConfiguration.fromJSON = function(data) {
    return data ? new jscape.FailoverServiceConfiguration(jscape.ManagerConfiguration.fromJSON(data.managerConfiguration),
                                                          jscape.IpSubstitutor.fromJSON(data.ipSubstitutor),
                                                          data.enabled,
                                                          data.forceStart)
           : null;
};

/** @class */
jscape.ManagerConfiguration = Class.extend(
    /** @lends {jscape.ManagerConfiguration.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d, e) {
        this.host = a || "";
        this.port = parseInt(b) || 0;
        this.timeout = parseInt(c) || 0;
        this.username = d || "";
        this.password = e || "";
    }
});
jscape.ManagerConfiguration.fromJSON = function(data) {
    return data ? new jscape.ManagerConfiguration(data.host, data.port, data.timeout, data.username, data.password) : null;
};

/** @class */
jscape.IpSubstitutor = Class.extend(
    /** @lends {jscape.IpSubstitutor.prototype} */
{
    /** @constructor */
    initialize: function(rules) {
        this.rules = rules || [];
    }
});
jscape.IpSubstitutor.fromJSON = function(data) {
    return data ? new jscape.IpSubstitutor($.map(data.rules || [], jscape.IpSubstitutor.Rule.fromJSON)) : null;
};

/** @class */
jscape.IpSubstitutor.Rule = Class.extend(
    /** @lends {jscape.IpSubstitutor.Rule.prototype} */
{
    /** @constructor */
    initialize: function(a, b) {
        this.ip = a || "";
        this.substitution = b || "";
    }
});
jscape.IpSubstitutor.Rule.fromJSON = function(data) {
    return data ? new jscape.IpSubstitutor.Rule(data.ip, data.substitution) : null;
};


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

        this.controller = new jscape.settings.FailoverServiceController();
        this.controller.onCreate(managementService, new jscape.FailoverService(managementService));
    },

    onStart: function () {
        this.controller.onStart();
    },

    onPause: function () {
        if (this.controller.hasChanged()) {
            this._confirmAndApply([this.controller]);
        }
    },

    onResume: function() {
        this.controller.onResume();
    }
});

/** @class */
jscape.FailoverService = Class.extend(
    /** @lends {jscape.FailoverService.prototype} */
{
    SYNC_STATE_PERIOD: 3 * 1000,
    TIMEOUT_PERIOD: 60 * 1000,

    deferred: null,
    timerId: null,
    timeoutId: null,

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

    syncState: function(host, port, timeout, username, password, substitutor, forceStart) {
        var config = {
            host: host,
            port: port,
            timeoutMillis: timeout,
            username: username,
            password: password,
            substitutor: substitutor,
            forceStart: forceStart
        };
        this.deferred = $.Deferred();
        this.managementService.syncFailoverServerState(config)
                .done($.proxy(function () {
                          this.timeoutId = setInterval($.proxy(this.cancel, this), this.TIMEOUT_PERIOD);
                          this._poll();
                      }, this))
                .fail($.proxy(this.cancel, this));
        return this.deferred.promise();
    },

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

    _poll: function() {
        this.timerId = setTimeout($.proxy(function () {
            this.managementService.failoverSyncComplete()
                    .done($.proxy(function (complete) {
                              if (complete) {
                                  this.deferred.resolve();
                                  this.deferred = null;
                              } else {
                                  this._poll();
                              }
                          }, this))
                    .fail($.proxy(this.cancel, this));
        }, this), this.SYNC_STATE_PERIOD);
    },

    _stopTimer: function() {
        if (this.timerId != null) {
            clearInterval(this.timerId);
            this.timerId = null;
        }
        if (this.timeoutId != null) {
            clearTimeout(this.timeoutId);
            this.timeoutId = null;
        }
    }
});

/** @class */
jscape.settings.FailoverServiceController = Class.extend(
    /** @lends {jscape.settings.FailoverServiceController.prototype} */
{
    DEFAULT_CONFIG: new jscape.FailoverServiceConfiguration(new jscape.ManagerConfiguration("localhost", 10880, 10000), new jscape.IpSubstitutor()),

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

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

        this.addRuleController = new jscape.settings.AddIpSubstitutionController();
        this.editRuleController = new jscape.settings.EditIpSubstitutionController();
        this.deleteRuleController = new jscape.settings.DeleteIpSubstitutionController();
    },

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

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

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

    onApply: function() {
        return this.managementService.setFailoverServiceConfiguration(this._createConfiguration())
                .done($.proxy(function (config) {
                          this.config = config;
                          this.rules = this._copySubstitutor(config).rules;
                          this._setupView(this.config, this.rules);
                      }, this))
                .done($.proxy(this._fireApplySuccess, this));
    },

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

    onSyncServerState: function() {
        return this.failoverService.syncState(this.view.getHost(),
                                              this.view.getPort(),
                                              this.view.getTimeout(),
                                              this.view.getUsername(),
                                              this.view.getPassword(),
                                              new jscape.IpSubstitutor(this.rules),
                                              this.view.isForceStart())
                .done($.proxy(this._fireSyncComplete, this));
    },

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

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

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

    _ruleAdded: function (rule) {
        this.rules.push(rule);
        this.substitutionView.setRules(this.rules);
        this.substitutionView.selectItem(rule);
    },

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

    _ruleDeleted: function (rules) {
        var index = -1;
        while (rules.length) {
            index = $.inArray(rules.pop(), this.rules);
            this.rules.splice(index, 1);
        }
        this.substitutionView.setRules(this.rules);

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

    _createConfiguration: function() {
        return new jscape.FailoverServiceConfiguration(new jscape.ManagerConfiguration(this.view.getHost(),
                                                                                       this.view.getPort(),
                                                                                       this.view.getTimeout(),
                                                                                       this.view.getUsername(),
                                                                                       this.view.getPassword()),
                                                       new jscape.IpSubstitutor(this.rules),
                                                       this.view.isEnabled(),
                                                       this.view.isForceStart());
    },

    _copySubstitutor: function(config) {
        return $.extend(true, {}, config ? config.ipSubstitutor : {});
    },

    _setupView: function(config, rules) {
        if (config) {
            this.view.setEnabled(config.enabled);
            this.view.setForceStart(config.forceStart);
            var manager = config.managerConfiguration;
            if (manager) {
                this.view.setHost(manager.host);
                this.view.setPort(manager.port);
                this.view.setTimeout(manager.timeout);
                this.view.setUsername(manager.username);
                this.view.setPassword(manager.password);
            }
        }

        this.substitutionView.setRules(rules || []);
    },

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

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

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

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

        this.host = $("input[name=host]", this.fieldset).textbox({required: true, missingMessage: window.R.string.FAILOVER_ERROR_BAD_HOST, width: "50%"});
        this.port = $("input[name=port]", this.fieldset).numberspinner({required: true, min: 1, max: 65535, width: 100});
        this.timeout = $("input[name=timeout]", this.fieldset).numberspinner({min: 1, max: 999, width: 60});
        this.username = $("input[name=username]", this.fieldset).textbox({required: true, missingMessage: window.R.string.FAILOVER_ERROR_BAD_USERNAME, width:"40%"});
        this.password = $("input[name=password]", this.fieldset).textbox({required: true, type: "password", missingMessage: window.R.string.FAILOVER_ERROR_BAD_PASSWORD, width:"40%"});
        this.enabled = $("input[name=enabled]:checkbox", this.fieldset).change($.proxy(this._adjustFieldState, this));
        this.forceStart = $("input[name=forcestart]:checkbox", this.fieldset);

        this.syncButton = $("a[role=button][name=synchronize]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$SyncButton, 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.FailoverSettingsView.LISTENER;
        this.enabled.change();
    },

    isEnabled: function () {
        return this.enabled.is(":checked");
    },

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

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

    setHost: function(value) {
        this.host.textbox("setValue", value);
    },

    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");
    },

    getUsername: function() {
        return this.username.textbox("getValue");
    },

    setUsername: function(value) {
        this.username.textbox("setValue", value);
    },

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

    setPassword: function(value) {
        this.password.textbox("setValue", value);
    },

    isForceStart: function() {
        return this.forceStart.is(":checked");
    },

    setForceStart: function(value) {
        this.forceStart.prop("checked", !!value);
    },

    _onClick$SyncButton: function () {
        if (this.fieldset.form("validate")) {
            this.syncButton.linkbutton("disable");
            this._toggleButtonText(this.syncButton);

            this.listener.onSyncServerState()
                    .always($.proxy(function () {
                                this.syncButton.linkbutton("enable");
                                this._toggleButtonText(this.syncButton);
                            }, this));
        }
    },

    _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();
    },

    _adjustFieldState: function () {
        var enabled = this.isEnabled();
        this.port.numberspinner(enabled ? "enableValidation" : "disableValidation");
        $.each([this.host, this.username, this.password], function(_, input) {
            input.textbox(enabled ? "enableValidation" : "disableValidation");
        });

        this.fieldset.form("validate");
    },

    _toggleButtonText: function (button) {
        var text = button.text();
        button.html(button.html().replace(text, button.data("toggle-text") || text));
        button.data("toggle-text", text);
    }
});
jscape.settings.FailoverSettingsView.LISTENER = {
    onSyncServerState: $.noop,
    onApply: $.noop,
    onDiscard: $.noop
};

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

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

        this.data = $("table[role=grid][aria-label=ipsubstitution]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            ctrlSelect: true,
            onSelect: $.proxy(this._adjustRowSelection, this),
            onSortColumn: $.proxy(this._adjustRowSelection, this),
            onLoadSuccess: $.proxy(this._adjustRowSelection, this),
            onDblClickRow: $.proxy(this._onClick$EditButton, this),
            columns: [[
                {field: "ip", title: $("thead th:nth-child(1)", this.data).text(), sortable: true, width: 100},
                {field: "substitution", title: $("thead th:nth-child(2)", this.data).text(), sortable: true, resizable: true, width: 100}
            ]]
        });

        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.IpSubstitutionView.LISTENER;
        this.data.datagrid("resize");
        this._adjustButtonsState();
    },

    setRules: function(values) {
        this.selection = [];
        this.data.datagrid("loading").datagrid("loadData", values).datagrid("loaded");
    },

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

    _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() {
        this.editButton.linkbutton(this.selection.length == 1 ? "enable" : "disable");
        this.deleteButton.linkbutton(this.selection.length != 0 ? "enable" : "disable");
    }
});
jscape.settings.IpSubstitutionView.LISTENER = {
    onAddRule: $.noop,
    onEditRule: $.noop,
    onDeleteRule: $.noop,
    onApply: $.noop,
    onDiscard: $.noop
};

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

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

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

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

    _createRule: function(dialog) {
        return new jscape.IpSubstitutor.Rule(dialog.getIp(), dialog.getSubstitution());
    },

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

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

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

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

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

    _createRule: function(dialog) {
        return new jscape.IpSubstitutor.Rule(dialog.getIp(), dialog.getSubstitution());
    },

    _setupDialog: function(rule) {
        var dialog = new jscape.settings.EditIpSubstitutionDialog();
        dialog.setIp(rule.ip);
        dialog.setSubstitution(rule.substitution);
        return dialog;
    }
});

/** @class */
jscape.settings.IpSubstitutionDialog = Class.extend(
    /** @lends {jscape.settings.IpSubstitutionDialog.prototype} */
{
    /** @constructor */
    initialize: function(dialog) {
        this.dialog = dialog.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=failoveripsubstitutionrulefields]", this.dialog);

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

    getIp: function() {
        return this.ip.textbox("getValue");
    },

    setIp: function(value) {
        this.ip.textbox("setValue", value);
    },

    getSubstitution: function() {
        return this.substitution.textbox("getValue");
    },

    setSubstitution: function(value) {
        this.substitution.textbox("setValue", value);
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.ip.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.AddIpSubstitutionDialog = jscape.settings.IpSubstitutionDialog.extend(
    /** @lends {jscape.settings.AddIpSubstitutionDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-failover-addrule"));
    }
});

/** @class */
jscape.settings.EditIpSubstitutionDialog = jscape.settings.IpSubstitutionDialog.extend(
    /** @lends {jscape.settings.EditIpSubstitutionDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-failover-editrule"));
    }
});

/** @class */
jscape.settings.DeleteIpSubstitutionController = Class.extend(
    /** @lends {jscape.settings.DeleteIpSubstitutionController.prototype} */
{
    start: function(rules) {
        var deferred = $.Deferred();
        this._confirmDelete(rules)
                .done(function() { deferred.resolve(rules); })
                .fail(function() { deferred.reject(); });
        return deferred.promise();
    },

    _confirmDelete: function(rules) {
        var text = $.map(rules || [], function(rule) { return rule.ip; }).join(", ");
        return jscape.ConfirmDialog.confirm(window.R.string.IP_SUBSTITUTION_CONFIRM_DELETE_TITLE,
                                            window.R.string.IP_SUBSTITUTION_CONFIRM_DELETE_MESSAGE.supplant({0: text}));
    }
});