/* global Chart:true */

'use strict';

angular.module('Application').controller('GraphsController', ['$scope', '$location', 'Client', function ($scope, $location, Client) {
    Client.onReady(function () { if (!Client.getUserInfo().admin) $location.path('/'); });

    $scope.diskUsage = {};
    $scope.memoryUsageSystem = [];
    $scope.memoryUsageApps = [];
    $scope.activeApp = null;
    $scope.memoryChart = null;

    $scope.installedApps = Client.getInstalledApps();

    function bytesToGigaBytes(value) {
        return (value/1024/1024/1024).toFixed(2);
    }

    function bytesToMegaBytes(value) {
        return (value/1024/1024).toFixed(2);
    }

    // http://stackoverflow.com/questions/1484506/random-color-generator-in-javascript
    function getRandomColor() {
        var letters = '0123456789ABCDEF'.split('');
        var color = '#';
        for (var i = 0; i < 6; i++ ) {
            color += letters[Math.floor(Math.random() * 16)];
        }
        return color;
    }

    function renderDisk(type, free, reserved, used) {
        $scope.diskUsage[type] = {
            used: bytesToGigaBytes(used.datapoints[0][0] + reserved.datapoints[0][0]),
            free: bytesToGigaBytes(free.datapoints[0][0]),
            sum: bytesToGigaBytes(used.datapoints[0][0] + reserved.datapoints[0][0] + free.datapoints[0][0])
        };

        var tmp = [{
            value: $scope.diskUsage[type].used,
            color: "#2196F3",
            highlight: "#82C4F8",
            label: "Used"
        }, {
            value: $scope.diskUsage[type].free,
            color:"#27CE65",
            highlight: "#76E59F",
            label: "Free"
        }];

        var ctx = $('#' + type + 'DiskUsageChart').get(0).getContext('2d');
        var myChart = new Chart(ctx);
        myChart.Doughnut(tmp);
    }

    $scope.setMemoryApp = function (app, color) {
        $scope.activeApp = app;

        var timePeriod = 12 * 60;    // in minutes
        var timeBucketSize = 60;    // in minutes

        var target;
        if (app === 'system') target = 'summarize(collectd.localhost.memory.memory-used, "' + timeBucketSize + 'min", "avg")';
        else target = 'summarize(collectd.localhost.table-' + app.id + '-memory.gauge-rss, "' + timeBucketSize + 'min", "avg")';

        Client.graphs([target], '-' + timePeriod + 'min', function (error, result) {
            if (error) return console.log(error);

            // translate the data from bytes to MB
            var data = result[0].datapoints.map(function (d) { return parseInt((d[0] / 1024 / 1024).toFixed(2)); });
            var labels = data.map(function (d, index) {
                var dateTime = new Date(Date.now() - ((timePeriod - (index * timeBucketSize)) * 60 *1000));
                return ('0' + dateTime.getHours()).slice(-2) + ':00';
            });

            var tmp = {
                labels: labels,
                datasets: [{
                    label: 'Memory',
                    fillColor: color || "#82C4F8",
                    strokeColor: color || "#2196F3",
                    pointColor: color || "rgba(151,187,205,1)",
                    pointStrokeColor: "#ffffff",
                    pointHighlightFill: color || "#82C4F8",
                    pointHighlightStroke: color || "#82C4F8",
                    data: data
                }]
            };

            var ctx = $('#memoryAppChart').get(0).getContext('2d');
            var chart = new Chart(ctx);

            var options = {
                scaleOverride: true,
                scaleSteps: 10,
                scaleStepWidth: $scope.activeApp === 'system' ? 200 : ($scope.activeApp.manifest.memoryLimit ? parseInt($scope.activeApp.manifest.memoryLimit/10000000,10) : 20),
                scaleStartValue: 0
            };

            if ($scope.memoryChart) $scope.memoryChart.destroy();
            $scope.memoryChart = chart.Line(tmp, options);
        });
    };

    $scope.updateDiskGraphs = function () {
        // https://graphite.readthedocs.io/en/latest/render_api.html#paths-and-wildcards
        // on scaleway, for some reason docker devices are collected as part of collectd
        // until we figure why just hardcode popular disk devices - https://www.mjmwired.net/kernel/Documentation/devices.txt
        Client.disks(function (error, disks) {
            if (error) return console.log(error);

            // We have to see if this is sufficient for all server configurations
            var appDataDiskName = disks.appsDataDisk.slice(disks.appsDataDisk.lastIndexOf('/') + 1)

            Client.graphs([
                'absolute(collectd.localhost.df-' + appDataDiskName + '.df_complex-free)',
                'absolute(collectd.localhost.df-' + appDataDiskName + '.df_complex-reserved)',
                'absolute(collectd.localhost.df-' + appDataDiskName + '.df_complex-used)'
            ], '-1min', function (error, data) {
                if (error) return console.log(error);

                renderDisk('system', data[0], data[1], data[2]);
            });
        });
    };

    $scope.updateMemorySystemChart = function () {
        var targets = [];
        var targetsInfo = [];

        targets.push('summarize(collectd.localhost.memory.memory-used, "1min", "avg")');
        targetsInfo.push({ label: 'System', color: '#2196F3' });

        targets.push('summarize(sum(collectd.localhost.memory.memory-buffered, collectd.localhost.memory.memory-cached), "1min", "avg")');
        targetsInfo.push({ label: 'Cached', color: '#f0ad4e' });

        targets.push('summarize(collectd.localhost.memory.memory-free, "1min", "avg")');
        targetsInfo.push({ label: 'Free', color: '#27CE65' });

        Client.graphs(targets, '-1min', function (error, result) {
            if (error) return console.log(error);

            $scope.memoryUsageSystem = result.map(function (data, index) {
                return {
                    value: bytesToMegaBytes(data.datapoints[0][0]),
                    color: targetsInfo[index].color,
                    highlight: targetsInfo[index].color,
                    label: targetsInfo[index].label
                };
            });

            var ctx = $('#memoryUsageSystemChart').get(0).getContext('2d');
            var chart = new Chart(ctx).Doughnut($scope.memoryUsageSystem);

            $('#memoryUsageSystemChart').get(0).onclick = function (event) {
                $scope.setMemoryApp('system');
            };
        });
    };

    // poor man's async
    function asyncForEach(items, handler, callback) {
        var cur = 0;

        if (items.length === 0) return callback();

        (function iterator() {
            handler(items[cur], function () {
                if (cur >= items.length-1) return callback();
                ++cur;

                iterator();
            });
        })();
    }

    $scope.updateMemoryAppsChart = function () {
        var targets = [];
        var targetsInfo = [];

        $scope.installedApps.forEach(function (app) {
            targets.push('summarize(collectd.localhost.table-' + app.id + '-memory.gauge-rss, "1min", "avg")');
            targetsInfo.push({
                label: app.location || 'bare domain',
                color: getRandomColor(),
                app: app
            });
        });

        // we split up the request, to avoid too large query strings into graphite
        var tmp = [];
        var aggregatedResult= [];

        while (targets.length > 0) tmp.push(targets.splice(0, 10));

        asyncForEach(tmp, function (targets, callback) {
            Client.graphs(targets, '-1min', function (error, result) {
                if (error) return callback(error);

                aggregatedResult = aggregatedResult.concat(result);

                callback(null);
            });
        }, function (error) {
            if (error) return console.log(error);

            $scope.memoryUsageApps = aggregatedResult.map(function (data, index) {
                return {
                    value: bytesToMegaBytes(data.datapoints[0][0]),
                    color: targetsInfo[index].color,
                    highlight: targetsInfo[index].color,
                    label: targetsInfo[index].label
                };
            });

            var ctx = $('#memoryUsageAppsChart').get(0).getContext('2d');
            var chart = new Chart(ctx).Doughnut($scope.memoryUsageApps);

            $('#memoryUsageAppsChart').get(0).onclick = function (event) {
                var activeBars = chart.getSegmentsAtEvent(event);

                // dismiss non chart clicks
                if (!activeBars || !activeBars[0]) return;

                // try to find the app for this segment
                var selectedDataInfo = targetsInfo.filter(function (info) { return info.label === activeBars[0].label; })[0];
                if (selectedDataInfo) $scope.setMemoryApp(selectedDataInfo.app, selectedDataInfo.color);
            };
        });
    };

    Client.onReady($scope.updateDiskGraphs);
    Client.onReady($scope.updateMemorySystemChart);
    Client.onReady($scope.updateMemoryAppsChart);
    Client.onReady($scope.setMemoryApp.bind(null, 'system'));

    $('.modal-backdrop').remove();
}]);
