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

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

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

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

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

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

/** @class */
jscape.settings.DbDriversController = Class.extend(
    /** @lends {jscape.settings.DbDriversController.prototype} */
{
    drivers: null,
    copy: null,
    view: null,

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

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

        this.addController = new jscape.settings.AddDbDriverController();
        this.editController = new jscape.settings.EditDbDriverController();
        this.deleteController = new jscape.settings.DeleteDbDriverController();
    },

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

    onResume: function () {
        this.managementService.getDbDrivers()
                .done($.proxy(function (drivers) { this.drivers = drivers; }, this))
                .fail($.proxy(function () { this.drivers = []; }, this))
                .always($.proxy(function () {
                            this.copy = this._copyDrivers(this.drivers);
                            this.view && this._setupView(this.drivers);
                        }, this));
    },

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

    onApply: function() {
        var drivers = this.drivers;
        return this.managementService.setDbDrivers(drivers)
                .done($.proxy(function () {
                          this.drivers = drivers;
                          this.copy = this._copyDrivers(drivers);
                      }, this))
                .done($.proxy(this._fireApplySuccess, this));
    },

    onDiscard: function() {
        this.drivers = this.copy;
        this.copy = this._copyDrivers(this.drivers);
        this._setupView(this.drivers);
        return $.Deferred().resolve().promise();
    },

    onAddDriver: function () {
        this.addController.start()
                .done($.proxy(this._driverAdded, this));
    },

    onEditDriver: function (item) {
        var index = $.inArray(item, this.drivers);
        if (index != -1) {
            this.editController.start(item)
                    .done($.proxy(this._driverEdited, this, index));
        }
    },

    onDeleteDriver: function (items) {
        this.deleteController.start(items)
                .done($.proxy(this._driverDeleted, this, items));
    },

    onDriverUp: function (item) {
        var index = $.inArray(item, this.drivers);
        if (index > 0) {
            this.drivers.splice(index - 1, 2, this.drivers[index], this.drivers[index - 1]);
            this.view.setDrivers(this.drivers);
            this.view.selectItem(item);
        }
    },

    onDriverDown: function (item) {
        var index = $.inArray(item, this.drivers);
        if (index != -1 && index < this.drivers.length - 1) {
            this.drivers.splice(index, 2, this.drivers[index + 1], this.drivers[index]);
            this.view.setDrivers(this.drivers);
            this.view.selectItem(item);
        }
    },

    _driverAdded: function (driver) {
        this.drivers.push(driver);
        this.view.setDrivers(this.drivers);
        this.view.selectItem(driver);
    },

    _driverEdited: function (index, driver) {
        this.drivers[index] = driver;
        this.view.setDrivers(this.drivers);
        this.view.selectItem(driver);
    },

    _driverDeleted: function (items) {
        var index = -1;
        while (items.length) {
            index = $.inArray(items.pop(), this.drivers);
            this.drivers.splice(index, 1);
        }
        this.view.setDrivers(this.drivers);

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

    _copyDrivers: function(drivers) {
        return $.extend(true, [], drivers);
    },

    _setupView: function(drivers) {
        this.view.setDrivers(drivers);
    },

    _initValidators: function() {
        var v = new jscape.Validator();
        v.nonEmptyRule("db_driver_nonempty", window.R.string.DB_DRIVERS_BAD_CLASS);
        v.rule("db_driver_notexists", $.proxy(this._driverNotExists, this), window.R.string.DB_DRIVERS_ERROR_CLASS_EXISTS);
    },

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

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

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

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

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

        this.data = $("table[role=grid][aria-label=jdbcdrivers]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            singleSelect: true,
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onSortColumn: $.proxy(this._adjustRowSelection, this),
            onDblClickRow: $.proxy(this._onClick$EditButton, this),
            onLoadSuccess: $.proxy(this._adjustRowSelection, this),
            columns: [[{field: "_class", title: $("thead th:nth-child(1)", this.data).text(), formatter: function(_, rowData) { return String(rowData).escapeXml(); }, sortable: true, width: 200}]]
        });

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

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

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

    selectItem: function (value) {
        var index = $.inArray(value, this.data.datagrid("getRows"));
        this.data.datagrid("selectRow", index);
    },

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

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

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

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

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

    _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 rows = this.data.datagrid("getRows");
        var index = this.selection.length == 1 ? $.inArray(this.selection[0], rows) : -1;

        this.moveUpButton.linkbutton(index > 0 && index <= rows.length - 1 ? "enable" : "disable");
        this.moveDownButton.linkbutton(index >= 0 && index < rows.length - 1 ? "enable" : "disable");
        this.editButton.linkbutton(this.selection.length == 1 ? "enable" : "disable");
        this.deleteButton.linkbutton(this.selection.length != 0 ? "enable" : "disable");
    }
});
jscape.settings.DbDriversView.LISTENER = {
    onAddDriver: $.noop,
    onEditDriver: $.noop,
    onDeleteDriver: $.noop,
    onDriverUp: $.noop,
    onDriverDown: $.noop,
    onApply: $.noop,
    onDiscard: $.noop
};

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

        this.className = $("input[name=classname]", this.fieldset).textbox({required: true, validType: ["db_driver_nonempty", "db_driver_notexists"], missingMessage: window.R.string.DB_DRIVERS_ERROR_BAD_CLASS, invalidMessage: window.R.string.DB_DRIVERS_ERROR_BAD_CLASS, width: "60%"});
    },

    getClassName: function() {
        return this.className.textbox("getValue");
    },

    setClassName: function(value) {
        var opts = this.className.textbox("textbox").validatebox("options");
        opts.validType = ["db_driver_nonempty", "db_driver_notexists['{0}']".supplant({0: value || ""})];
        this.className.textbox("setValue", value);
    },

    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.className.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.AddDbDriverController = Class.extend(
    /** @lends {jscape.settings.AddDbDriverController.prototype} */
{
    deferred: null,

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

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

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

    _setupDialog: function() {
        var dialog = new jscape.settings.DbDriverDialog();
        dialog.setTitle(window.R.string.DB_DRIVERS_ADD_TITLE);
        return dialog;
    }
});

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

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

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

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

    _setupDialog: function(driver) {
        var dialog = new jscape.settings.DbDriverDialog();
        dialog.setTitle(window.R.string.DB_DRIVERS_EDIT_TITLE);
        dialog.setClassName(driver);
        return dialog;
    }
});

/** @class */
jscape.settings.DeleteDbDriverController = Class.extend(
    /** @lends {jscape.settings.DeleteDbDriverController.prototype} */
{
    start: function (drivers) {
        return jscape.ConfirmDialog.confirm(window.R.string.DB_DRIVERS_CONFIRM_DELETE_TITLE,
                                            window.R.string.DB_DRIVERS_CONFIRM_DELETE_MESSAGE.supplant({0: drivers.join(", ")}));
    }
});