'use strict';

/* globals angular,document */

// Awesome canvas graph built with help from Kent Dodds
// https://github.com/kentcdodds/ng-stats
// Stats generated by eep.js
// https://github.com/darach/eep-js

angular.module('batarang.app.perf', [])
  .controller('PerfController', ['$scope', '$timeout', '$window', PerfController]);

function PerfController($scope, $timeout, $window) {

  $scope.watchTimings = [];
  $scope.numWatchers = 0;
  $scope.last30Digests = {};
  $scope.last30Seconds = {};
  $scope.last5Seconds = {};

  // used for graph
  var noDigestSteps = 0;
  var opts = {
    digestTimeThreshold: 16,
    watchCountThreshold: 2000
  };
  var graphSz = {width: 130, height: 40};
  var canvasEl = document.querySelector('canvas');
  var cvs = angular.element(canvasEl).attr(graphSz)[0];
  // </graph vars>

  // eep
  var eep = $window.eep;
  // TODO implement rolling windows over time:
  var last30Seconds = new eep.EventWorld.make().windows().sliding(eep.Stats.sum, 30);
  var last5Seconds = new eep.EventWorld.make().windows().sliding(eep.Stats.sum, 5);
  var last30Digests = {
    watchers: new eep.EventWorld.make().windows().sliding(eep.Stats.mean, 30),
    time: new eep.EventWorld.make().windows().sliding(eep.Stats.mean, 30)
  };

  last30Digests.watchers.on('emit', function (avg) {
    $scope.last30Digests.watchers = avg;
  });
  last30Digests.time.on('emit', function (avg) {
    $scope.last30Digests.time = avg;
  });
  last5Seconds.on('emit', function (count) {
    $scope.last5Seconds.digests = count;
  });
  last30Seconds.on('emit', function (count) {
    $scope.last30Seconds.digests = count;
  });

  var digests = 0;
  setInterval(() => {
    last30Seconds.enqueue(digests);
    last5Seconds.enqueue(digests);
    digests = 0;
  }, 1000);


  $scope.$on('scope:digest', function (e, digestData) {

    last30Digests.watchers.enqueue(digestData.events.length);
    last30Digests.time.enqueue(digestData.time);
    digests++;

    $scope.lastDigestTime = digestData.time;

    var reducedWatches = digestData.events.reduce(function (prev, next) {
      if (!prev[next.watch]) {
        prev[next.watch] = {
          time: next.time,
          count: 1
        };
      } else {
        prev[next.watch].time += next.time;
        prev[next.watch].count++;
      }
      return prev;
    }, {});

    $scope.watchTimings = Object.keys(reducedWatches)
    .filter(function (key) { return reducedWatches[key].time; })
    .map(function (key) {
      return {
        text: key.trim().replace(/\s{2,}/g, ' '),
        time: reducedWatches[key].time,
        count: reducedWatches[key].count
      };
    })
    .sort(function (a, b) {
      return b.time - a.time;
    });

    $scope.numWatchers = digestData.events.length;
    addDataToCanvas(digestData.events.length, digestData.time);
  });

  function getColor(metric, threshold) {
    if (metric > threshold) {
      return 'red';
    } else if (metric > 0.7 * threshold) {
      return 'orange';
    }
    return 'green';
  }

  function addDataToCanvas(watchCount, digestLength) {
    var digestColor = getColor(digestLength, opts.digestTimeThreshold);
    var watchColor = getColor(watchCount, opts.watchCountThreshold);

    // color the sliver if this is the first step
    var ctx = cvs.getContext('2d');
    if (noDigestSteps > 0) {
      noDigestSteps = 0;
      ctx.fillStyle = '#333';
      ctx.fillRect(graphSz.width - 1, 0, 1, graphSz.height);
    }

    // mark the point on the graph
    ctx.fillStyle = digestColor;
    ctx.fillRect(graphSz.width - 1, Math.max(0, graphSz.height - digestLength), 2, 2);
  }

  var shiftTimeout;

  // Shift the canvas to the left.
  function shiftLeft() {
    if ($scope.enabled) {
      shiftTimeout = $timeout(shiftLeft, 250);
      var ctx = cvs.getContext('2d');
      var imageData = ctx.getImageData(1, 0, graphSz.width - 1, graphSz.height);
      ctx.putImageData(imageData, 0, 0);
      ctx.fillStyle = ((noDigestSteps++) > 2) ? 'black' : '#333';
      ctx.fillRect(graphSz.width - 1, 0, 1, graphSz.height);
    }
  }

  // TODO: clear canvas when enable is toggled
  $scope.$watch('enabled', function (n) {
    if (n) { shiftLeft(); }
    else { $timeout.cancel(shiftTimeout); }
  });
}
