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

/** @class */
jscape.Report = Class.extend(
    /** @lends {jscape.Report.prototype} */
{
    /** @constructor */
    initialize: function (a, b, c, d, e, f, g, h) {
        this.name = a || "";
        this.date = parseInt(b) || 0;
        this.description = c || "";
        this.searchName = d || null;
        this.reRunSearch = e !== undefined ? !!e : false;
        this.metricses = f || [];
        this.progress = parseInt(g) || 0;
        this.errorMessage = h || null;
    },

    succeed: function() {
        return this.progress === 100 && !this.failed();
    },

    failed: function() {
        return this.errorMessage && this.errorMessage.trim().length != 0;
    }
});
jscape.Report.fromJSON = function(data) {
    return data ? $.extend(new jscape.Report(), data) : null;
};

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

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

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

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

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

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

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

        this.addController = new jscape.domain.AddReportController();
        this.editController = new jscape.domain.EditReportController();
        this.deleteController = new jscape.domain.DeleteReportController();
        this.viewController = new jscape.domain.ViewReportController();
    },

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

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

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

    onAddReport: function() {
        this.addController.start(this.managementService, this.domainName)
                .done($.proxy(this._reportAdded, this));
    },

    onEditReport: function(report) {
        var index = $.inArray(report, this.view.getReports());
        if (index != -1) {
            this.editController.start(this.managementService, this.domainName, report)
                    .done($.proxy(this._reportEdited, this, index));
        }
    },

    onDeleteReport: function(reports) {
        this.deleteController.start(this.managementService, this.domainName, reports)
                .done($.proxy(this._reportDeleted, this, reports));
    },

    onViewReport: function(report) {
        this.viewController.start(report);
    },

    onReRunReport: function(report) {
        var index = $.inArray(report, this.view.getReports());
        if (index != -1) {
            report.progress = 0;
            report.errorMessage = null;
            this.managementService.reRunReport(this.domainName, report.name)
                    .done($.proxy(this._reportEdited, this, index));
        }
    },

    _reportAdded: function (report) {
        this._recordAdded(report);
    },

    _reportEdited: function (index, report) {
        this._recordEdited(report);
    },

    _reportDeleted: function (reports) {
        this._recordDeleted(reports);
    },

    _initValidators: function() {
        var v = new jscape.Validator();
        v.nonEmptyRule("report_name_nonempty", window.R.string.REPORTS_ERROR_BAD_NAME);
        v.rule("report_unique", $.proxy(this._reportNotExists, this), window.R.string.REPORTS_ERROR_ALREDY_EXISTS);
    },

    _reportNotExists: function(value) {
        return $.grep(this.view.getReports(), function(report) { return report.name == value; }).length == 0;
    }
});


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

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

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

        this.data = $("table[role=grid][aria-label=reports]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            ctrlSelect: true,
            pagination: true,
            search: true,
            remoteSearch: true,
            sortName: "name",
            columns: [[
                {field: "name", title: $("thead th:nth-child(1)", this.data).text(), sortable: true, width: 100},
                {field: "date", title: $("thead th:nth-child(2)", this.data).text(), sortable: true, width: 150, align: "right", finder: "datebox", formatter: function(value) { return new Date(value).toLocaleString(); }},
                {field: "description", title: $("thead th:nth-child(3)", this.data).text(), sortable: false, width: 250},
                {field: "searchName", title: $("thead th:nth-child(4)", this.data).text(), sortable: true, width: 150, sorter: function(a, b) { return a == b ? 0 : (a || "" > b || "" ? 1 : -1); }},
                {field: "status", title: $("thead th:nth-child(5)", this.data).text(), sortable: false, searchable: false, width: 70, formatter: $.proxy(function(_, row) { return this._formatStatus(row); }, this), styler: function(_, report) { return report.failed() ? "color:red" : report.succeed() ? "color:green" : "color:black"; }}
            ]],
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onDblClickRow: $.proxy(function() { this.viewButton.triggerHandler("click"); }, 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.viewButton = $("a[role=button][name=view]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$ViewButton, this)});
        this.runButton = $("a[role=button][name=run]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$RunButton, this)});

        initializing = false;
    },

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

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

    _formatStatus: function(report) {
        return report.failed()
               ? window.R.string.REPORTS_STATUS_FAILED.supplant({0: String(report.errorMessage || "").escapeXml()})
               : report.succeed()
                  ? window.R.string.REPORTS_STATUS_SUCCEED
                  : window.R.string.REPORTS_STATUS_PROGRESS.supplant({0: report.progress});
    },

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

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

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

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

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

    _onClick$RunButton: function() {
        var index = this.data.datagrid("getRowIndex", this.selection[0]);
        this.listener.onReRunReport(this.selection[0]);

        this.data.datagrid("refreshRow", index);
        this._adjustButtonsState();
    },

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

    _adjustButtonsState: function() {
        var succeed = this.selection.length == 1 && this.selection[0].succeed();
        var failed = this.selection.length == 1 && this.selection[0].failed();
        this.editButton.linkbutton(succeed || failed ? "enable" : "disable");
        this.deleteButton.linkbutton(succeed || failed ? "enable" : "disable");
        this.viewButton.linkbutton(succeed ? "enable" : "disable");
        this.runButton.linkbutton(succeed || failed ? "enable" : "disable");
    }
});
jscape.domain.ReportsView.LISTENER = {
    onRefresh: $.noop,
    onAddReport: $.noop,
    onEditReport: $.noop,
    onDeleteReport: $.noop,
    onViewReport: $.noop,
    onReRunReport: $.noop
};

/** @class */
jscape.domain.AddReportController = Class.extend(
    /** @lends {jscape.domain.AddReportController.prototype} */
{
    start: function(managementService, domainName) {
        this.managementService = managementService;
        this.domainName = domainName;
        this.deferred = $.Deferred();
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var report = this._createReport(dialog);
        dialog.destroy();

        this.managementService.addReport(this.domainName, report)
                .done($.proxy(function(report) { this.deferred.resolve(report); 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;
    },

    _createReport: function(dialog) {
        return new jscape.Report(dialog.getName(),
                                 $.now(),
                                 dialog.getDescription(),
                                 dialog.getSearchName(),
                                 dialog.isReRunSearch(),
                                 dialog.getMetrics());
    },

    _setupDialog: function () {
        var dialog = new jscape.domain.AddReportDialog();
        dialog.selectAllMetrics();
        this.managementService.getLogSearches(this.domainName)
                .done($.proxy(function (searches) {
                          dialog.setSearches($.map(searches || [], function (search) { return search.name; }));
                      }, this));
        return dialog;
    }
});

/** @class */
jscape.domain.EditReportController = Class.extend(
    /** @lends {jscape.domain.EditReportController.prototype} */
{
    start: function(managementService, domainName, report) {
        this.managementService = managementService;
        this.domainName = domainName;
        this.reportName = report.name;
        this.deferred = $.Deferred();
        this.managementService.getReport(this.domainName, this.reportName)
                .done($.proxy(function(report) { this._setupDialog(report).show(this); }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var report = this._createReport(dialog);
        dialog.destroy();

        this.managementService.setReport(this.domainName, report.name, report)
                .done($.proxy(function() { this.deferred.resolve(report); }, this))
                .fail($.proxy($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this)));
    },

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

    _createReport: function(dialog) {
        return new jscape.Report(this.reportName,
                                 $.now(),
                                 dialog.getDescription(),
                                 dialog.getSearchName(),
                                 dialog.isReRunSearch(),
                                 dialog.getMetrics());
    },

    _setupDialog: function (report) {
        var dialog = new jscape.domain.EditReportDialog();
        dialog.setReportName(report.name);
        dialog.setDescription(report.description);
        dialog.setMetrics(report.metricses);
        dialog.setReRunSearch(report.reRunSearch);
        this.managementService.getLogSearches(this.domainName)
                .done(function (searches) {
                          searches = $.map(searches || [], function (search) { return search.name; });
                          dialog.setSearches(searches);
                          dialog.setSearchName(report.searchName);
                      });
        return dialog;
    }
});

/** @class */
jscape.domain.ReportDialog = Class.extend(
    /** @lends {jscape.domain.ReportDialog.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=reportfields]", this.dialog);

        this.description = $("textarea[name=description]", this.fieldset).textbox({multiline: true, width: "60%", height: 60});

        this.useSearch = $("input[name=usesearch]:checkbox", this.fieldset);
        this.useSearch.change($.proxy(function() {
            var enabled = this.useSearch.is(":checked");
            this.searches.combobox(enabled ? "enable" : "disable").combobox(enabled ? "enableValidation" : "disableValidation");
            this.reRunSearch.prop("disabled", !enabled);
        }, this));
        this.searches = $("select[name=searchname]", this.fieldset);
        this.searches.combobox({
            required: true,
            editable: true,
            limitToList: true,
            disabled: true,
            width: 150,
            panelHeight: "auto",
            onLoadSuccess: $.proxy(function (data) {
                if (data.length == 0) {
                    this.useSearch.prop("disabled", true).prop("checked", false).change();
                }
            }, this)
        });

        this.reRunSearch = $("input[name=rerunsearch]:checkbox", this.fieldset).prop("disabled", true);

        this.metrics = $("table[role=grid][aria-label=metrics]", this.fieldset);
        this.metrics.datagrid({
            fit: true,
            fitColumns: true,
            singleSelect: false,
            checkOnSelect: true,
            selectOnCheck: true,
            remoteSort: false,
            onLoadSuccess: function(data) { $(this).datagrid("getPanel").find("div.datagrid-header-check input:checkbox").prop("disabled", data.rows.length == 0); },
            idField: "value",
            columns: [[
                    {field: "value", checkbox: true, sortable: false},
                    {field: "displayName", title: $("thead th:nth-child(1)", this.metrics).text(), sortable: false, width: 100, formatter: function(_, row) { return row.value.replace(/([A-Z])/g, " $1").trim().escapeXml(); }}
                ]]
            });
    },

    getDescription: function() {
        return this.description.textbox("getValue");
    },

    setDescription: function(value) {
        this.description.textbox("setValue", value);
    },

    getSearchName: function() {
        return this.useSearch.is(":checked") ? this.searches.combobox("getValue") : null;
    },

    setSearchName: function(value) {
        if (value) {
            this.searches.combobox("setValue", value);
        }
        this.useSearch.prop("checked", value != null).change();
    },

    setSearches: function(values) {
        this.searches.combobox("loadData", values);
    },

    isReRunSearch: function() {
        return this.reRunSearch.is(":checked") && !this.reRunSearch.is(":disabled");
    },

    setReRunSearch: function(value) {
        this.reRunSearch.prop("checked", !!value);
    },

    getMetrics: function() {
        return $.map(this.metrics.datagrid("getChecked"), function(entry) { return entry.value; });
    },

    setMetrics: function(values) {
        if (values && values.length) {
            for (var i = 0; i < values.length; i++) {
                this.metrics.datagrid("selectRecord", values[i]);
            }
        } else {
            this.metrics.datagrid("clearChecked");
        }
    },

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

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

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

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

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

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

/** @class */
jscape.domain.AddReportDialog = jscape.domain.ReportDialog.extend(
    /** @lends {jscape.domain.AddReportDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-reports-add"));

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

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

    selectAllMetrics: function() {
        this.metrics.datagrid("checkAll");
    }
});

/** @class */
jscape.domain.EditReportDialog = jscape.domain.ReportDialog.extend(
    /** @lends {jscape.domain.EditReportDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-reports-edit"));

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

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

/** @class */
jscape.domain.ViewReportController = Class.extend(
    /** @lends {jscape.domain.ViewReportController.prototype} */
{
    start: function(report) {
        var url = window.location.pathname + "/reports/{report}/".supplant({report: encodeURIComponent(report.name)});
        window.open(url, "jscape-report-result");
    }
});


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

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

    _confirmDelete: function (reports) {
        var text = $.map(reports, function (report) { return report.name; }).join(", ");
        return jscape.ConfirmDialog.confirm(
                window.R.string.REPORTS_CONFIRM_DELETE_TITLE,
                window.R.string.REPORTS_CONFIRM_DELETE_MESSAGE.supplant({0: text}));
    },

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