var wota = use('internal/wota');
var nota = use('internal/nota');
var json = use('json');
var jswota = use('jswota')
var os   = use('internal/os');

if (length(arg) != 2) {
  log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
  $stop()
}

var lib_name = arg[0];
var scenario_name = arg[1];

def libraries = [
  {
    name: "wota",
    encode: wota.encode,
    decode: wota.decode,
    getSize(encoded) {
      return length(encoded);
    }
  },
  {
    name: "nota",
    encode: nota.encode,
    decode: nota.decode,
    getSize(encoded) {
      return length(encoded);
    }
  },
  {
    name: "json",
    encode: json.encode,
    decode: json.decode,
    getSize(encodedStr) {
      return length(encodedStr);
    }
  }
];

def benchmarks = [
  {
    name: "empty",
    data: [{}, {}, {}, {}],
    iterations: 10000
  },
  {
    name: "integers",
    data: [0, 42, -1, 2023],
    iterations: 100000
  },
  {
    name: "floats",
    data: [0.1, 1e-50, 3.14159265359],
    iterations: 100000
  },
  {
    name: "strings",
    data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
    iterations: 100000
  },
  {
    name: "objects",
    data: [
      { a:1, b:2.2, c:"3", d:false },
      { x:42, y:null, z:"test" }
    ],
    iterations: 50000
  },
  {
    name: "nested",
    data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
    iterations: 50000
  },
  {
    name: "large_array",
    data: [ array(1000, i => i) ],
    iterations: 1000
  },
];

function measureTime(fn) {
  var start = os.now();
  fn();
  var end   = os.now();
  return (end - start);
}

function runBenchmarkForLibrary(lib, bench) {
  var encodedList = [];
  var totalSize = 0;
  var i = 0
  var j = 0
  var e = null

  var encodeTime = measureTime(() => {
    for (i = 0; i < bench.iterations; i++) {
      for (j = 0; j < length(bench.data); j++) {
        e = lib.encode(bench.data[j]);
        if (i == 0) {
          push(encodedList, e);
          totalSize += lib.getSize(e);
        }
      }
    }
  });

  var decodeTime = measureTime(() => {
    for (i = 0; i < bench.iterations; i++) {
      arrfor(encodedList, lib.decode)
    }
  });

  return { encodeTime, decodeTime, totalSize };
}

var lib = libraries[find(libraries, l => l.name == lib_name)];
var bench = benchmarks[find(benchmarks, b => b.name == scenario_name)];

if (!lib) {
  log.console('Unknown library:', lib_name);
  log.console('Available libraries:', text(array(libraries, l => l.name), ', '));
  $stop()
}

if (!bench) {
  log.console('Unknown scenario:', scenario_name);
  log.console('Available scenarios:', text(array(benchmarks, b => b.name), ', '));
  $stop()
}

var bench_result = runBenchmarkForLibrary(lib, bench);
var encodeTime = bench_result.encodeTime;
var decodeTime = bench_result.decodeTime;
var totalSize = bench_result.totalSize;

var totalOps = bench.iterations * length(bench.data);
var result = {
  lib: lib_name,
  scenario: scenario_name,
  encodeTime: encodeTime,
  decodeTime: decodeTime,
  totalSize: totalSize,
  totalOps: totalOps,
  encodeOpsPerSec: totalOps / encodeTime,
  decodeOpsPerSec: totalOps / decodeTime,
  encodeNsPerOp: (encodeTime / totalOps) * 1e9,
  decodeNsPerOp: (decodeTime / totalOps) * 1e9
};

log.console(result);

$stop()
