jscape.domain = jscape.domain || {};

/** @class */
jscape.BannedFiles = Class.extend(
    /** @lends {jscape.BannedFiles.prototype} */
{
    /** @constructor */
    initialize: function(a, b) {
        this.enabled = a || false;
        this.rules = b || [];
    }
});
jscape.BannedFiles.fromJSON = function(data) {
    return new jscape.BannedFiles(data && data.enabled, data ? $.map(data.rules, jscape.FileOperationRule.fromJSON) : []);
};

/** @class */
jscape.FileOperationRule = Class.extend(
    /** @lends {jscape.FileOperationRule.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d) {
        this.path = a == "" ? "/" : a;
        this.pattern = b || "";
        this.recursive = c != undefined ? c : true;
        this.scopes = d || [];
    }
});
jscape.FileOperationRule.fromJSON = function(data) {
    var scopes = jscape.FileOperationRule.Scopes.fromJSON(data.scopes);
    return new jscape.FileOperationRule(data.path, data.pattern, !!data.recursive, scopes);
};
/** @class */
jscape.FileOperationRule.Scopes = Class.extend(
    /** @lends {jscape.FileOperationRule.Scopes.prototype} */
{
    /** @constructor */
    initialize: function(code, value) {
        this.code = code;
        this.value = value;
    },

    toJSON: function() {
        return this.value;
    }
});
jscape.FileOperationRule.Scopes.fromJSON = function(data) {
    function _equals(o1, o2) {
        if (o1.length != o2.length) { return false; }
        for (var i = 0; i < o1.length; i++) {
            if (o1[i] !== o2[i]) { return false; }
        }
        return true;
    }
    var ret = $.grep(jscape.FileOperationRule.Scopes.values(), function(scope) { return _equals(data, scope.value)});
    return ret.length ? ret[0] : jscape.FileOperationRule.Scopes.FILE;
};
jscape.FileOperationRule.Scopes.fromCode = function(code) {
    var ret = $.grep(jscape.FileOperationRule.Scopes.values(), function(scope) { return code == scope.code; });
    return ret.length ? ret[0] :  jscape.FileOperationRule.Scopes.FILE;
};
jscape.FileOperationRule.Scopes.values = function() {
    return [jscape.FileOperationRule.Scopes.FILE, jscape.FileOperationRule.Scopes.DIRECTORY, jscape.FileOperationRule.Scopes.FILE_AND_DIRECTORY];
};
jscape.FileOperationRule.Scopes.FILE = new jscape.FileOperationRule.Scopes("FILE", ["File"]);
jscape.FileOperationRule.Scopes.DIRECTORY = new jscape.FileOperationRule.Scopes("DIR", ["Directory"]);
jscape.FileOperationRule.Scopes.FILE_AND_DIRECTORY = new jscape.FileOperationRule.Scopes("FILE_AND_DIR", ["File", "Directory"]);

/** @class */
jscape.domain.BannedFilesScopeFormat = Class.extend(
    /** @lends {jscape.domain.BannedFilesScopeFormat.prototype} */
{
    format: function (scope) {
        switch (scope) {
            case jscape.FileOperationRule.Scopes.FILE:
                return window.R.string.BANNED_FILES_SCOPE_FILE;
            case jscape.FileOperationRule.Scopes.DIRECTORY:
                return window.R.string.BANNED_FILES_SCOPE_DIRECTORY;
            case jscape.FileOperationRule.Scopes.FILE_AND_DIRECTORY:
                return window.R.string.BANNED_FILES_SCOPE_FILE_AND_DIRECTORY;
            default:
                return "";
        }
    }
});

/** @class */
jscape.domain.BannedFilesModule = jscape.domain.DomainModule.extend(
    /** @lends {jscape.domain.BannedFilesModule.prototype} */
{
    onCreate: function () {
        this._super.apply(this, arguments);

        this.controller = new jscape.domain.BannedFilesController();
        this.controller.onCreate(this.managementService, this.domainName);
    },

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

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

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

/** @class */
jscape.domain.BannedFilesController = Class.extend(
    /** @lends {jscape.domain.BannedFilesController.prototype} */
{
    DEFAULT_DESCRIPTOR: new jscape.BannedFiles(false),

    view: null,
    descriptor: null,
    copy: null,
    editIndex: -1,

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

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

        this.addController = new jscape.domain.AddBannedFilesController();
        this.editController = new jscape.domain.EditBannedFilesController();
        this.deleteController = new jscape.domain.DeleteBannedFilesController();
    },

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

    onResume: function () {
        this.managementService.getBannedFiles(this.domainName)
                .done($.proxy(function (descriptor) { this.descriptor = descriptor; }, this))
                .fail($.proxy(function () { this.descriptor = this._copyDescriptor(this.DEFAULT_DESCRIPTOR); }, this))
                .always($.proxy(function () {
                            this.copy = this._copyDescriptor(this.descriptor);
                            this._setupView(this.descriptor);
                        }, this));
    },

    onRefresh: function() {
        return $.Deferred().resolve(this.descriptor.rules).promise();
    },

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

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

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

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

    onApply: function () {
        var descriptor = this._createDescriptor();
        return this.managementService.setBannedFiles(this.domainName, descriptor)
                .done($.proxy(function (descriptor) {
                          this.descriptor = descriptor;
                          this.copy = this._copyDescriptor(this.descriptor);
                          this._setupView(this.descriptor);
                      }, this))
                .done($.proxy(this._fireApplySuccess, this));
    },

    onDiscard: function () {
        this.descriptor = this.copy;
        this.copy = this._copyDescriptor(this.descriptor);
        this._setupView(this.descriptor);
    },

    _ruleAdded: function (rule) {
        this.descriptor.rules.push(rule);
        this.view.setRules(this.descriptor.rules);
        this.view.selectRule(rule);
    },

    _ruleEdited: function (index, rule) {
        this.descriptor.rules[index] = rule;
        this.view.setRules(this.descriptor.rules);
        this.view.selectRule(rule);
    },

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

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

    _createDescriptor: function () {
        var descriptor = this._copyDescriptor(this.descriptor);
        descriptor.enabled = this.view.getEnabled();
        return descriptor;
    },

    _copyDescriptor: function(descriptor) {
        var rules = descriptor ? $.map(descriptor.rules, function(rule) { return $.extend(new jscape.FileOperationRule(), rule); }) : [];
        return new jscape.BannedFiles(descriptor && descriptor.enabled, rules);
    },

    _setupView: function (descriptor) {
        if (descriptor && this.view) {
            this.view.setEnabled(descriptor.enabled);
            this.view.setRules(descriptor.rules);
        }
    },

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

    _initValidators: function() {
        var v = new jscape.Validator();
        v.nonEmptyRule("bannedfiles_path", window.R.string.BANNED_FILES_ERROR_BAD_PATH);
        v.rule("bannedfiles_pattern", $.proxy(this._patternValid, this), window.R.string.BANNED_FILES_ERROR_BAD_PATTERN);
        v.rule("bannedfiles_unique", $.proxy(this._ruleNotExists, this), window.R.string.BANNED_FILES_ERROR_ALREADY_EXISTS);
    },

    _patternValid: function(value) {
        var response = this.managementService.testBannedFilesPattern(this.domainName, value);
        return response && "true" === response.trim();
    },

    _ruleNotExists: function (_, params) {
        if (params.length > 1) {
            var path = $(params[0]).val();
            var pattern = $(params[1]).val();
            var excludeIndex = this.editIndex;
            return $.grep(this.descriptor.rules, function (rule, index) {
                return excludeIndex != index && rule.path == path && rule.pattern == pattern;
            }).length == 0;
        }
        return true;
    }
});

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

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

        this.scopeFormat = new jscape.domain.BannedFilesScopeFormat();

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

        this.enabled = $("[name=enabled]:checkbox", this.fieldset).change($.proxy(this._onChange$Enabled, this));

        this.data = $("table[role=grid][aria-label=bannedfiles]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            singleSelect: true,
            pagination: true,
            remoteSort: false,
            search: true,
            columns: [[
                {field: "path", title: $("thead th:nth-child(1)", this.data).text(), width: 100},
                {field: "recursive", title: $("thead th:nth-child(2)", this.data).text(), width: 40},
                {field: "pattern", title: $("thead th:nth-child(3)", this.data).text(), width: 120},
                {field: "scopes", title: $("thead th:nth-child(4)", this.data).text(), width: 80, formatter: $.proxy(this._formatScope, this)}
            ]],
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onClickRow: $.proxy(function(index) { if (!this.getEnabled()) { this.data.datagrid("unselectRow", index); }}, this),
            onDblClickRow: $.proxy(function() { if (this.getEnabled()) { this._onClick$EditButton(); }}, this),
            loader: $.proxy(this._onLoad$Data, this),
            onBeforeLoad: function () { return !initializing; },
            onLoadSuccess: $.proxy(this._adjustRowSelection, 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)});

        initializing = false;
    },

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

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

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

    selectRule: function(rule) {
        this.data.datagrid("selectRow", this.data.datagrid("getRowIndex", rule));
    },

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

    show: function(listener) {
        this.listener = listener || jscape.domain.BannedFilesView.LISTENER;
        this._adjustRowSelection();
        this.enabled.change();
    },

    _formatScope: function(value) {
        return this.scopeFormat.format(value);
    },

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

    _onChange$Enabled: function () {
        var panel = this.data.datagrid("getPanel");
        if (this.getEnabled()) {
            panel.children("div.datagrid-mask").remove();
        } else {
            if (!panel.children("div.datagrid-mask").length) {
                $("<div class=\"datagrid-mask\" style=\"display:block\"></div>").appendTo(panel);
            }
        }
        this._adjustButtonsState();
    },

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

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

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

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

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

    _setupView: function() {
    },

    _createRule: function(dialog) {
        return new jscape.FileOperationRule(dialog.getPath(),
                                            dialog.getPattern(),
                                            dialog.getRecursive(),
                                            dialog.getScope());
    }
});

/** @class */
jscape.domain.AddBannedFilesController = jscape.domain.BannedFilesRuleController.extend(
    /** @lends {jscape.domain.AddBannedFilesController.prototype} */
{
    /** @override */
    _setupView: function() {
        var dialog = new jscape.domain.BannedFilesRuleDialog();
        dialog.setTitle(window.R.string.BANNED_FILES_DIALOG_ADD_TITLE);
        dialog.setPath("/");
        dialog.setRecursive(true);
        dialog.setScopes(jscape.FileOperationRule.Scopes.values());
        dialog.setScope(jscape.FileOperationRule.Scopes.FILE);
        dialog.show(this);
    }
});

/** @class */
jscape.domain.EditBannedFilesController = jscape.domain.BannedFilesRuleController.extend(
    /** @lends {jscape.domain.EditBannedFilesController.prototype} */
{
    /** @override */
    start: function(rule) {
        this.rule = rule;
        return this._super();
    },

    /** @override */
    _setupView: function () {
        var dialog = new jscape.domain.BannedFilesRuleDialog();
        dialog.setTitle(window.R.string.BANNED_FILES_DIALOG_EDIT_TITLE);
        dialog.setPath(this.rule.path);
        dialog.setPattern(this.rule.pattern);
        dialog.setRecursive(this.rule.recursive);
        dialog.setScopes(jscape.FileOperationRule.Scopes.values());
        dialog.setScope(this.rule.scopes);
        dialog.show(this);
    }
});

/** @class */
jscape.domain.BannedFilesRuleDialog = Class.extend(
    /** @lends {jscape.domain.BannedFilesRuleDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-bannedfiles-rule").clone().attr("id", new Date().getTime().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", disabled: this.dialog.find("fieldset:first").is(":disabled"), 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=bannedfilesrulefields]", this.dialog);

        var pathId = "bf_path_" + new Date().getTime().toString(36);
        var patternId = "bf_pattern_" + new Date().getTime().toString(36);
        this.path = $("input[name=path]", this.fieldset).attr("id", pathId);
        this.pattern = $("input[name=pattern]", this.fieldset).attr("id", patternId);
        this.path.textbox({
            required: true,
            validType: ["bannedfiles_path", "bannedfiles_unique['#{0}','#{1}']".supplant({0: pathId, 1: patternId})],
            missingMessage: window.R.string.BANNED_FILES_ERROR_BAD_PATH,
            width: "60%"
        });
        this.pattern.textbox({
            required: true,
            validType: ["bannedfiles_pattern", "bannedfiles_unique['#{0}','#{1}']".supplant({0: pathId, 1: patternId})],
            missingMessage: window.R.string.BANNED_FILES_ERROR_BAD_PATTERN,
            width: "60%"
        });

        this.recursive = $("input[name=recursive]:checkbox", this.fieldset);
        this.scope = $("select[name=scope]", this.fieldset).combobox({required: true, editable: false, width: 120, panelHeight: "auto"});
    },

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

    getPath: function() {
        return this.path.textbox("getValue");
    },

    setPath: function(value) {
        this.path.textbox("setValue", value);
    },

    getPattern: function() {
        return this.pattern.textbox("getValue");
    },

    setPattern: function(value) {
        this.pattern.textbox("setValue", value);
    },

    getRecursive: function() {
        return this.recursive.is(":checked");
    },

    setRecursive: function(value) {
        this.recursive.prop("checked", !!value);
    },

    setScopes: function(values) {
        var format = new jscape.domain.BannedFilesScopeFormat();
        values = $.map(values, function(value) { return {text: format.format(value), value: value.code}; });
        this.scope.combobox("loadData", values);
    },

    getScope: function() {
        return jscape.FileOperationRule.Scopes.fromCode(this.scope.combobox("getValue"));
    },

    setScope: function(value) {
        if (value && value.code) {
            this.scope.combobox("select", value.code);
        }
    },

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

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

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

/** @class */
jscape.domain.DeleteBannedFilesController = Class.extend(
    /** @lends {jscape.domain.DeleteBannedFilesController.prototype} */
{
    start: function(rules) {
        var text = $.map(rules, function(rule) { return rule.pattern; }).join(", ");
        return jscape.ConfirmDialog.confirm(
                window.R.string.BANNED_FILES_CONFIRM_DELETE_TITLE,
                window.R.string.BANNED_FILES_CONFIRM_DELETE_MESSAGE.supplant({0: text}));
    }
});