jscape.server = jscape.server || {};

/** @class */
jscape.server.ServerDashboardApplication = jscape.Application.extend(
    /** @lends {jscape.server.ServerDashboardApplication.prototype} */
{
    /** @override */
    onReady: function() {
        this._super();
        this.view.show(this);
    },

    /** @override */
    _createView: function() {
        this._super();
        this.view = new jscape.server.ServerDashboardView();
    }
});

/** @class */
jscape.server.ServerDashboardView = jscape.DashboardView.extend(
    /** @lends {jscape.server.ServerDashboardView.prototype} */
{
    /** @constructor */
    initialize: function () {
        this._super($("#modules[role=tablist]"));

        this.modules.tabs({fit: true, border: true, tabPosition: "top", onSelect: $.proxy(this._onSelect$Tab, this)});
        this.modules.find("div[role=tablist]")
                .filter(function () { return $(this).parents("div.dialog").length == 0; })
                .each($.proxy(function (_, tablist) {
                          $(tablist).tabs({
                              border: false,
                              onSelect: $.proxy(this._onSelect$Tab, this)
                          });
                      }, this));

        this.modules.find(">div.tabs-header>div.tabs-wrap")
                .hover(
                function () { $(this).css({overflowY: "auto", overflowX: "hidden"}); },
                function () { $(this).css({overflowY: "", overflowX: ""}); }
        );
    },

    /** @override */
    show: function() {
        this._super.apply(this, arguments);
        this.modules.tabs("unselect", 0).tabs("select", 0);
    }
});

/** @class */
jscape.server.DashboardModule = jscape.ApplicationModule.extend(
    /** @lends {jscape.server.DashboardModule.prototype} */
{
    /** @const */
    DELAY: 5 * 1000,

    timerId: null,
    maxMemory: null,

    onCreate: function() {
        this._super.apply(this, arguments);

        this.view = new jscape.server.DashboardView();
    },

    onStart: function() {
        this.view.show(this);
        this._loadMaxMemory();
    },

    onResume: function() {
        this._poll();
    },

    onPause: function() {
        this._stopPoll();
    },

    _poll: function (delay) {
        this._stopPoll();
        this.timerId = setTimeout($.proxy(function () {
            this.timerId = null;
            this._loadData()
                    .done($.proxy(this._poll, this, this.DELAY))
                    .fail($.proxy(this._stopPoll, this));
        }, this), delay || 0);
    },

    _stopPoll: function () {
        if (this.timerId) {
            clearTimeout(this.timerId);
        }
    },

    _loadData: function () {
        var deferred = $.Deferred();
        $.when(this.managementService.getServerThreadsStatistics(),
               this.managementService.getServerMemoryStatistics())
                .done($.proxy(function(threads, memory) { this._setServerGraph(threads, memory); deferred.resolve(); }, this))
                .fail(deferred.reject);

        this.managementService.getServerConnectionsStatistics().done($.proxy(this._setConnectionsGraph, this)).fail(deferred.reject);
        this.managementService.getServerUploadsStatistics().done($.proxy(this._setUploadsGraph, this)).fail(deferred.reject);
        this.managementService.getServerDownloadsStatistics().done($.proxy(this._setDownloadsGraph, this)).fail(deferred.reject);

        return deferred.resolve().promise();
    },

    _loadMaxMemory: function () {
        return this.managementService.getServerStatistics()
                .done($.proxy(function (stats) { this.maxMemory = parseInt(stats.maxMemory) || null; }, this));
    },

    _setServerGraph: function(threads, memory) {
        var threadsData = new Array(threads.length);
        for (var i = 0; i < threads.length; i++) {
            threadsData[i] = this._plotEntry(threads[i], "amount");
        }

        var len = memory.length;
        var heapMemory = new Array(len);
        var allocatedMemory = new Array(len);
        var minDate, maxDate;

        for (var i = 0; i < len; i++) {
            var date = memory[i].date;
            heapMemory[i] = this._plotEntry(memory[i], "heap");
            allocatedMemory[i] = this._plotEntry(memory[i], "allocated");
            minDate = minDate ? Math.min(minDate, date) : date;
            maxDate = maxDate ? Math.max(minDate, date) : date;
        }

        var maxMemory = [];
        if (this.maxMemory) {
            maxMemory.push([minDate, this.maxMemory]);
            maxMemory.push([maxDate, this.maxMemory]);
        }

        this.view.setServerStats(threadsData, heapMemory, allocatedMemory, maxMemory);
    },

    _setConnectionsGraph: function(measures) {
        var len = measures.length;
        var connections = new Array(len);
        for (var i = 0; i < len; i++) {
            connections[i] = this._plotEntry(measures[i], "amount");
        }
        connections.push([$.now(), null]);
        this.view.setConnections(connections);
    },

    _setUploadsGraph: function (measures) {
        var len = measures.length;
        var files = new Array(len);
        var bytes = new Array(len);

        for (var i = 0; i < len; ++i) {
            files[i] = this._plotEntry(measures[i], "amount");
            bytes[i] = this._plotEntry(measures[i], "bytes");
        }
        files.push([$.now(), null]);
        bytes.push([$.now(), null]);
        this.view.setUploads(files, bytes);
    },

    _setDownloadsGraph: function (measures) {
        var len = measures.length;
        var files = new Array(len);
        var bytes = new Array(len);

        for (var i = 0; i < len; ++i) {
            files[i] = this._plotEntry(measures[i], "amount");
            bytes[i] = this._plotEntry(measures[i], "bytes");
        }
        files.push([$.now(), null]);
        bytes.push([$.now(), null]);
        this.view.setDownloads(files, bytes);
    },

    _plotEntry: function(measure, key) {
        return measure && measure.date ? [measure.date, measure[key] != -1 ? measure[key] : null] : null;
    }
});

/** @class */
jscape.server.DashboardView = Class.extend(
    /** @lends {jscape.server.DashboardView.prototype} */
{
    /** @constructor */
    initialize: function() {
        var defaultOptions = {
            series: {lines: {show: true}, shadowSize: 0},
            grid: {borderWidth: 1, borderColor: "#95B8E7"},
            legend: {position: "ne", margin: [30, 5]},
            selection: {mode: "x"}
        };
        var timeAxe = {mode: "time", timezone: "browser"};
        var amountAxe = {min: 0, minTickSize: 1, tickDecimals: 0, position: "right"};
        var fileSizeAxe = {min: 0, minTickSize: 1024, position: "left", tickFormatter: function (value) { return jscape.SizeFormat.format(value); }};

        this.serverPlot = new jscape.server.DashboardView.Plot($("#server-plot"), $.extend(true, {}, defaultOptions, {
            xaxes: [timeAxe],
            yaxes: [amountAxe, fileSizeAxe],
            legend: {noColumns: 4}
        }));

        this.connectionsPlot = new jscape.server.DashboardView.Plot($("#connections-plot"), $.extend(true, {}, defaultOptions, {
            xaxis: $.extend({}, timeAxe, {minTickSize: [1, "minute"]}),
            yaxis: {min: 0, minTickSize: 1, tickDecimals: 0}
        }));

        this.uploadsPlot = new jscape.server.DashboardView.Plot($("#uploads-plot"), $.extend(true, {}, defaultOptions, {
            xaxes: [timeAxe],
            yaxes: [amountAxe, fileSizeAxe],
            legend: {noColumns: 2}
        }));

        this.downloadsPlot = new jscape.server.DashboardView.Plot($("#downloads-plot"), $.extend(true, {}, defaultOptions, {
            xaxes: [timeAxe],
            yaxes: [amountAxe, fileSizeAxe],
            legend: {noColumns: 2}
        }));
    },

    show: function(listener) {
        this.listener = listener || jscape.server.DashboardView.LISTENER;
        this.serverPlot.show(this);
        this.connectionsPlot.show(this);
        this.uploadsPlot.show(this);
        this.downloadsPlot.show(this);
    },

    setServerStats: function(threads, heapMemory, allocatedMemory, maxMemory) {
        var data = [
            {label: window.R.string.GRAPH_LEGEND_THREADS, data: threads, yaxis: 1},
            {label: window.R.string.GRAPH_LEGEND_MEMORY_HEAP, data: heapMemory, yaxis: 2, lines: {fill: true}},
            {label: window.R.string.GRAPH_LEGEND_MEMORY_ALLOCATED, data: allocatedMemory, yaxis: 2, lines: {fill: true}},
            {label: window.R.string.GRAPH_LEGEND_MEMORY_MAX, data: maxMemory, yaxis: 2, color: "#F00"}
        ];
        this.serverPlot.setData(data);
    },

    setConnections: function(connections) {
        this.connectionsPlot.setData([{label: window.R.string.GRAPH_LEGEND_CONNECTIONS, data: connections}]);
    },

    setUploads: function(files, bytes) {
        var data = [
            {label: window.R.string.GRAPH_LEGEND_FILES, data: files, yaxis: 1},
            {label: window.R.string.GRAPH_LEGEND_BYTES, data: bytes, yaxis: 2}
        ];
        this.uploadsPlot.setData(data);
    },

    setDownloads: function(files, bytes) {
        var data = [
            {label: window.R.string.GRAPH_LEGEND_FILES, data: files, yaxis: 1},
            {label: window.R.string.GRAPH_LEGEND_BYTES, data: bytes, yaxis: 2}
        ];
        this.downloadsPlot.setData(data);
    }
});
jscape.server.DashboardView.LISTENER = {};

/** @class */
jscape.server.DashboardView.Plot = Class.extend(
    /** @lends {jscape.server.DashboardView.Plot.prototype} */
{
    DAYS_RANGE: {lowerBound: -4, upperBound: 1},

    options: null,

    /** @constructor */
    initialize: function(placeholder, options) {
        this.plot = $.plot(placeholder, [], options);
        placeholder.bind("plotselected", $.proxy(this._onZoomIn, this));

        $("<div />")
                .css({position: "absolute", right: "60px", top: "54px", padding: "2px", cursor: "pointer", verticalAlign: "top", border: "1px solid #DEDEDE"})
                .text("Zoom Out")
                .click($.proxy(this._onZoomOut, this))
                .appendTo(placeholder);
    },

    show: function(listener) {
        this.listener = listener;
    },

    setData: function (data) {
        this.plot.setData(data);
        this.plot.setupGrid();
        this.plot.draw();
    },

    _onZoomIn: function(e, ranges) {
        this._zoom(ranges.xaxis.from, ranges.xaxis.to);
    },

    _onZoomOut: function(e) {
        e && e.preventDefault();
        var min = Number.MAX_VALUE, max = Number.MIN_VALUE;
        $.each(this.plot.getXAxes(), $.proxy(function(_, axis) {
            min = Math.min(min, axis.datamin);
            max = Math.max(max, axis.datamax);
        }, this));
        this._zoom(min, max);
    },

    _zoom: function(min, max) {
        $.each(this.plot.getXAxes(), function (_, axis) {
            var opts = axis.options;
            opts.min = min;
            opts.max = max;
        });
        this.plot.setupGrid();
        this.plot.draw();
        this.plot.clearSelection();
    }
});

/** @class */
jscape.server.StatisticsModule = jscape.ApplicationModule.extend(
    /** @lends {jscape.server.StatisticsModule.prototype} */
{
    DELAY: 15 * 1000,

    onCreate: function() {
        this._super.apply(this, arguments);

        this.view = new jscape.server.StatisticsView();
    },

    onStart: function() {
        this.view.show(this);

        this.managementService.getServerParameters()
                .done($.proxy(function (params) {
                          this.view.setParameters(params);
                      }, this));
    },

    onResume: function() {
        this._poll();
    },

    onPause: function() {
        this._stopPoll();
    },

    _poll: function (delay) {
        this._stopPoll();
        this.timerId = setTimeout($.proxy(function () {
            this.timerId = null;
            this._loadStatistics()
                    .done($.proxy(this._poll, this, this.DELAY))
                    .fail($.proxy(this._stopPoll, this));
        }, this), delay || 0);
    },

    _stopPoll: function () {
        if (this.timerId) {
            clearTimeout(this.timerId);
        }
    },

    _loadStatistics: function () {
        return this.managementService.getServerStatistics()
                .done($.proxy(function (stats) {
                          this.view.setStatistics(stats);
                      }, this));
    }
});

/** @class */
jscape.server.StatisticsView = Class.extend(
    /** @lends {jscape.server.StatisticsView.prototype} */
{
    DATE_FORMAT: new jscape.DateFormat("{MM}-{dd}-{yy}"),

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

        this.data = $("table[role=grid][aria-label=statistics]", this.fieldset).propertygrid({
            fit: true,
            fitColumns: true,
            showGroup: true,
            loadFilter: $.proxy(this._loadFilter, this)
        });
    },

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

    setParameters: function(params) {
        var data = this.data.propertygrid("getData");
        this.data.propertygrid("loadData",
                               $.extend(data || {}, {
                                   version: params.appVersion,
                                   instanceId: params.appInstanceId,
                                   license: params.license,
                                   timeZone: params.timeZone
                               }));
    },

    setStatistics: function (stats) {
        var data = this.data.propertygrid("getData");
        this.data.propertygrid("loadData",
                               $.extend(data || {}, {
                                   memory: {used: (stats.totalMemory - stats.freeMemory), total: stats.maxMemory},
                                   threads: stats.threads || 0,
                                   uptime: stats.uptime || 0,
                                   managementConnections: stats.managementConnections,
                                   domainStatistics: stats.domainStatistics || []
                               }));
    },

    _loadFilter: function(data) {
        var rows = [
            {name: window.R.string.SERVER_VERSION, value: data.version || ""},
            {name: window.R.string.SERVER_INSTANCE_ID, value: data.instanceId || ""},
            {name: window.R.string.SERVER_LICENSE_TYPE, value: data.license ? data.license.type : "", group: window.R.string.SERVER_LICENSE},
            {name: window.R.string.SERVER_LICENSE_OWNER, value: data.license ? data.license.owner : "", group: window.R.string.SERVER_LICENSE},
            {name: window.R.string.SERVER_LICENSE_HOST, value: this._formatBinding(data.license ? data.license.bindingHost : ""), group: window.R.string.SERVER_LICENSE},
            {name: window.R.string.SERVER_LICENSE_MAC, value: this._formatBinding(data.license ? data.license.bindingMac : ""), group: window.R.string.SERVER_LICENSE},
            {name: window.R.string.SERVER_LICENSE_EXPIRES, value: this._formatExpirationDate(data.license ? data.license.expirationDate : ""), group: window.R.string.SERVER_LICENSE},
            {name: window.R.string.SERVER_DOMAINS, value: data.domainStatistics ? data.domainStatistics.length : "", group: window.R.string.SERVER_STATISTICS},
            {name: window.R.string.SERVER_UPTIME, value: this._formatUptime(data.uptime), group: window.R.string.SERVER_STATISTICS},
            {name: window.R.string.SERVER_TIME_ZONE, value: data.timeZone || ""},
            {name: window.R.string.SERVER_MANAGEMENT_CONNECTIONS, value: data.managementConnections || "", group: window.R.string.SERVER_STATISTICS},
            {name: window.R.string.SERVER_CONNECTIONS, value: this._formatCurrentConnections(data.domainStatistics), group: window.R.string.SERVER_STATISTICS},
            {name: window.R.string.SERVER_MEMORY, value: this._formatMemoryUsage(data.memory), group: window.R.string.SERVER_STATISTICS},
            {name: window.R.string.SERVER_THREADS, value: data.threads || "", group: window.R.string.SERVER_STATISTICS}
        ];
        return {total: rows.length, rows: rows};
    },

    _formatCurrentConnections: function (domainStatistics) {
        if (domainStatistics) {
            var connections = 0;
            for (var i = 0; i < domainStatistics.length; ++i) {
                connections += domainStatistics[i].currentSessions;
            }
            return connections;
        }
        return "";
    },

    _formatBinding: function(value) {
        return value == null ? window.R.string.SERVER_LICENSE_NON_BINDING : value;
    },

    _formatExpirationDate: function(value) {
        if (value == null) {
            return window.R.string.SERVER_LICENSE_NON_EXPIRING;
        }
        return !isNaN(value) ? this.DATE_FORMAT.format(new Date(value)) : "";
    },

    _formatMemoryUsage: function(memory) {
        return memory
                ? $.isNumeric(memory.used) && $.isNumeric(memory.total)
                       ? "{used} of {total}".supplant({used: this._formatSize(memory.used), total: this._formatSize(memory.total)})
                       : ""
                : "";
    },

    _formatUptime: function(value) {
        if (value) {
            var time = new jscape.Hours(value);
            var pattern = [];
            if (time.hours) {
                pattern.push("{hours} hrs.");
            }
            if (time.minutes) {
                pattern.push("{minutes} min.");
            }
            if (time.seconds) {
                pattern.push("{seconds} sec.");
            }
            return pattern.join(" ").supplant(time);
        }
        return "";
    },

    _formatSize: function(value) {
        return new jscape.SizeFormat().format(value);
    }
});
jscape.server.StatisticsView.LISTENER = {};

/** @class */
jscape.server.InterfacesModule = jscape.ApplicationModule.extend(
    /** @lends {jscape.server.InterfacesModule.prototype} */
{
    onCreate: function() {
        this._super.apply(this, arguments);

        this.view = new jscape.server.InterfacesView();
    },

    onStart: function() {
        this.view.show(this);

        this.managementService.getServerParameters()
                        .done($.proxy(function (params) {
                                  this.view.setInterfaces(params.interfaces);
                              }, this));
    }
});
/** @class */
jscape.server.InterfacesView = Class.extend(
    /** @lends {jscape.server.InterfacesView.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.fieldset = $("fieldset[name=serverinterfacesfields]");

        this.data = $("table[role=grid][aria-label=interfaces]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            singleSelect: true,
            columns: [[
                {field: "name", title: $("thead th:nth-child(1)", this.data).text(), sortable: true},
                {field: "displayName", title: $("thead th:nth-child(2)", this.data).text(), sortable: true},
                {field: "addresses", title: $("thead th:nth-child(3)", this.data).text(), sortable: true, width: 200, formatter: function(rowData) { return rowData.join(", ").escapeXml(); }}
            ]]
        });
    },

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

    setInterfaces: function(values) {
        this.data.datagrid("loading", values)
                .datagrid("loadData", values)
                .datagrid("loaded", values);
    }
});
jscape.server.InterfacesView.LISTENER = {};