/**
 * Christopher L Merrill, Copyright 2015
 */
var module = angular.module('FlotLineChart.module', ['events.module' , 'metrics.module']);

var samples_to_display = 151;

module.controller('FlotLineChart', ['$scope', 'initialMetricsService', 'eventSubscriptionService', function($scope, initialMetricsService, eventSubscriptionService)
    {
    $scope.new_values = [];
    $scope.initial_values = null;

    var elapsed = 0;
    $scope.addPoint = function()
        {
        $scope.new_values.push([elapsed, Math.round(Math.random() * 10)]);
        elapsed += 2000;
        };

    function sampleToDatapoint(sample)
        {
        var point = [];
        point[0] = sample.metrics[METRIC_IDS["ELAPSED"]];
        point[1] = sample.metrics[METRIC_IDS[$scope.metric_name]];
        return point;
        }

    initialMetricsService.getInitialSamples(samples_to_display, function(sample_list)
        {
        if (sample_list !== null)
            {
            var points = [];
            for (var i = 0; i < sample_list.length; i++)
                points.push(sampleToDatapoint(sample_list[i]));
            $scope.initial_values = points;
            }

        // don't subscribe to new metrics until we have initial metrics (or know there aren't any).
        eventSubscriptionService.subscribe('SummaryMetrics', function(message)
            {
            var datapoint = sampleToDatapoint(JSON.parse(message.data));
            $scope.new_values.push(datapoint);
            });
        });
    }]);

module.directive('flotLineChart', function()
    {
    return {
        restrict: 'A',
        link: function(scope, elem, attrs)
            {
            // basic parameters
            scope.metric_name = attrs["metric"];

            var num_samples_to_plot = attrs["samples"];
            if (typeof num_samples_to_plot === 'undefined')
                num_samples_to_plot = samples_to_display;

            var decimal_places = attrs["decimals"];
            if (typeof decimal_places === 'undefined')
                decimal_places = 0;

            var formatter = null;
            var units = attrs["units"];
            if (typeof units !== 'undefined')
                {
                if (units === 'bandwidth')
                    formatter = toBandwidth;
                else if (units === 'duration')
                    formatter = toDuration;
                }

            var sample_period = 2000;
            var points = null;
            var points_to_plot = [];  // a version of the array with intermediate null values removed (for more pleasant lines with null values)
            var chart_arrays = [[[-4000,-1], [-2000, -1]]];  // initial data (to force rescale of y-axis labels later)

            // general chart options
            var opts  =
                {
                xaxis:
                    {
                    ticks: 6,
                    tickFormatter: function(value)
                        {
                        if (points === null)
                            return "";
                        else
                            return toHHMMSS(value);
                        }
                    },
                yaxis:
                    {
                    tickDecimals: decimal_places,
                    autoscaleMargin: 0.1,
                    min: -10,  // these will be changed after the first datapoint arrives
                    max: -1,
                    },
                grid:
                    {
                    margin: { top:10, left:20, bottom:20}
                    }
                };

            if (formatter !== null)
                opts.yaxis.tickFormatter = formatter;

            var series_options = {};  // the options for the (only) data series
            series_options.color = "red";
            series_options.shadowSize = 0;

            // show chart in initial state, before any data arrives
            $.plot(elem, chart_arrays, opts);
            elem.show();
            updateChart();


            function addNewPoint(value)
                {
                // if this is the first point received
                if (points === null)
                    {
                    // are there initial values ready?
                    if (scope.inital_values === null)
                        points = [];
                    else
                        {
                        points = scope.initial_values;
                        scope.initial_values = null;
                        }

                    // setup blank data points
                    series_options.data = points_to_plot;
                    chart_arrays[0]=series_options;
                    var next_elapsed = 0;
                    for (var i = 0; i < num_samples_to_plot; i++)
                        {
                        if (typeof points[i] === 'undefined')
                            {
                            points[i] = [next_elapsed, null];
                            next_elapsed += sample_period;
                            }
                        else
                            next_elapsed = points[i][0] + sample_period;
                        }
                    opts.yaxis.min = 0;
                    delete opts.yaxis.max;
                    }

                var elapsed = value[0];
                if (elapsed >= points[points.length - 1][0] + sample_period)  // if adding to the end
                    {
                    points.shift();
                    points.push(value);
                    updateChart();
                    }
                else
                    {
                    for (var index = 0; index < num_samples_to_plot; index++)
                        if (elapsed == points[index][0])
                            {
                            points[index][1] = value[1];
                            break;
                            }
                    updateChart();
                    }
                }

            function updateChart()
                {
                runNullPointRemoval();
                $.plot(elem, chart_arrays, opts);
                }

            function runNullPointRemoval()
                {
                if (points === null)
                    return;
                points_to_plot.length = 0;  // this clears the array

                var i;  // counter

                // find the first non-null sample
                var first_non_null = -1;
                for (i = 0; i < points.length; i++)
                    if (points[i] !== null)
                        {
                        first_non_null = i;
                        break;
                        }

                // if none are not-null, plot the entire array
                if (first_non_null === -1)
                    {
                    for (i = 0; i < points.length; i++)
                        points_to_plot[i] = points[i];
                    series_options.data = points_to_plot;
                    return;
                    }

                // find the last non-null
                var last_non_null = points.length;
                for (i = points.length - 1; i >= 0; i--)
                    if (points[i][1] !== null)
                        {
                        last_non_null = i;
                        break;
                        }

                // now, copy the points, removing interior null values (keep the nulls on the ends, for consistent chart scrolling).
                for (i = 0; i < points.length; i++)
                    {
                    if (i <= first_non_null || i >= last_non_null)
                        points_to_plot.push(points[i]);
                    else if (points[i][1] !== null)
                        points_to_plot.push(points[i]);
                    }
                }

            scope.redraw = function()
                {
                updateChart(chart_arrays);
                };
            scope.$watchCollection("new_values", function(newval)
                {
                // pop off the first sample. Note that this changes the array, thus triggering another callback to this method
                var point = newval.shift();
                if (typeof point !== "undefined")
                    addNewPoint(point);
                });

        }
    };
});

function toHHMMSS(duration)
    {
    var sec_num = duration / 1000;
    var hours   = Math.floor(sec_num / 3600);
    var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
    var seconds = sec_num - (hours * 3600) - (minutes * 60);

    if (hours === 0)
        hours = null;
    else if (hours < 10)
        hours = "0"+hours;
    if (minutes < 10)
        minutes = "0"+minutes;
    if (seconds < 10)
        seconds = "0"+seconds;
    var time = minutes+':'+seconds;
    if (hours !== null)
        time = hours+':'+ time;
    return time;
    }

