var usage = 'node benchmark/compare.js ' +
            '<node-binary1> <node-binary2> ' +
            '[--html] [--red|-r] [--green|-g] ' +
            '[-- <type> [testFilter]]';

var show = 'both';
var nodes = [];
var html = false;
var benchmarks;

for (var i = 2; i < process.argv.length; i++) {
  var arg = process.argv[i];
  switch (arg) {
    case '--red': case '-r':
      show = show === 'green' ? 'both' : 'red';
      break;
    case '--green': case '-g':
      show = show === 'red' ? 'both' : 'green';
      break;
    case '--html':
      html = true;
      break;
    case '-h': case '-?': case '--help':
      console.log(usage);
      process.exit(0);
      break;
    case '--':
      benchmarks = [];
      break;
    default:
      if (Array.isArray(benchmarks))
        benchmarks.push(arg);
      else
        nodes.push(arg);
      break;
  }
}

if (!html) {
  var start = '';
  var green = '\033[1;32m';
  var red = '\033[1;31m';
  var reset = '\033[m';
  var end = '';
} else {
  var start = '<pre style="background-color:#333;color:#eee">';
  var green = '<span style="background-color:#0f0;color:#000">';
  var red = '<span style="background-color:#f00;color:#fff">';
  var reset = '</span>';
  var end = '</pre>';
}

var runBench = process.env.NODE_BENCH || 'bench';

if (nodes.length !== 2)
  return console.error('usage:\n  %s', usage);

var spawn = require('child_process').spawn;
var results = {};
var toggle = 1;
var r = (+process.env.NODE_BENCH_RUNS || 1) * 2;

run();
function run() {
  if (--r < 0)
    return compare();
  toggle = ++toggle % 2;

  var node = nodes[toggle];
  console.error('running %s', node);
  var env = {};
  for (var i in process.env)
    env[i] = process.env[i];
  env.NODE = node;

  var out = '';
  var child;
  if (Array.isArray(benchmarks) && benchmarks.length)
    child = spawn(node, ['benchmark/common.js'].concat(benchmarks), { env: env });
  else
    child = spawn('make', [runBench], { env: env });
  child.stdout.setEncoding('utf8');
  child.stdout.on('data', function(c) {
    out += c;
  });

  child.stderr.pipe(process.stderr);

  child.on('close', function(code) {
    if (code) {
      console.error('%s exited with code=%d', node, code);
      process.exit(code);
    } else {
      out.trim().split(/\r?\n/).forEach(function(line) {
        line = line.trim();
        if (!line)
          return;

        var s = line.split(':');
        var num = +s.pop();
        if (!num && num !== 0)
          return;

        line = s.join(':');
        var res = results[line] = results[line] || {};
        res[node] = res[node] || [];
        res[node].push(num);
      });

      run();
    }
  });
}

function compare() {
  // each result is an object with {"foo.js arg=bar":12345,...}
  // compare each thing, and show which node did the best.
  // node[0] is shown in green, node[1] shown in red.
  var maxLen = -Infinity;
  var util = require('util');
  console.log(start);

  Object.keys(results).map(function(bench) {
    var res = results[bench];
    var n0 = avg(res[nodes[0]]);
    var n1 = avg(res[nodes[1]]);

    var pct = ((n0 - n1) / n1 * 100).toFixed(2);

    var g = n0 > n1 ? green : '';
    var r = n0 > n1 ? '' : red;
    var c = r || g;

    if (show === 'green' && !g || show === 'red' && !r)
      return;

    var r0 = util.format('%s%s: %d%s', g, nodes[0], n0.toPrecision(5), g ? reset : '');
    var r1 = util.format('%s%s: %d%s', r, nodes[1], n1.toPrecision(5), r ? reset : '');
    var pct = c + pct + '%' + reset;
    var l = util.format('%s: %s %s', bench, r0, r1);
    maxLen = Math.max(l.length + pct.length, maxLen);
    return [l, pct];
  }).filter(function(l) {
    return l;
  }).forEach(function(line) {
    var l = line[0];
    var pct = line[1];
    var dotLen = maxLen - l.length - pct.length + 2;
    var dots = ' ' + new Array(Math.max(0, dotLen)).join('.') + ' ';
    console.log(l + dots + pct);
  });
  console.log(end);
}

function avg(list) {
  if (list.length >= 3) {
    list = list.sort();
    var q = Math.floor(list.length / 4) || 1;
    list = list.slice(q, -q);
  }
  return list.reduce(function(a, b) {
    return a + b;
  }, 0) / list.length;
}
