/* global dcodeIO, Plotly */
let jobId = 0;
let currentTrace = 0;
let plotlyDiv;
const workers = [];
async function run(messageLength, numWorkers = 1, difficulty = 100, ttl = 72) {
  const timestamp = Math.floor(Date.now() / 1000);
  const pubKey =
    '05ec8635a07a13743516c7c9b3412f3e8252efb7fcaf67eb1615ffba62bebc6802';
  const message = randomString(messageLength);
  const messageBuffer = dcodeIO.ByteBuffer.wrap(
    message,
    'utf8'
  ).toArrayBuffer();
  const data = dcodeIO.ByteBuffer.wrap(messageBuffer).toString('base64');
  const promises = [];
  const t0 = performance.now();
  for (let w = 0; w < numWorkers; w += 1) {
    const worker = new Worker('../../js/util_worker.js');
    workers.push(worker);
    jobId += 1;
    const increment = numWorkers;
    const index = w;
    worker.postMessage([
      jobId,
      'calcPoW',
      timestamp,
      ttl * 60 * 60 * 1000,
      pubKey,
      data,
      false,
      difficulty,
      increment,
      index,
    ]);
    const p = new Promise(resolve => {
      worker.onmessage = nonce => {
        resolve(nonce);
      };
    });
    promises.push(p);
  }
  await Promise.race(promises);
  const t1 = performance.now();
  const duration = (t1 - t0) / 1000;
  addPoint(duration);
  // clean up
  workers.forEach(worker => worker.terminate());
}

async function runPoW({
  iteration,
  difficulty,
  numWorkers,
  messageLength = 50,
  ttl = 72,
}) {
  const name = `W:${numWorkers} - NT: ${difficulty} - L:${messageLength} - TTL:${ttl}`;
  Plotly.addTraces(plotlyDiv, {
    y: [],
    type: 'box',
    boxpoints: 'all',
    name,
  });
  for (let i = 0; i < iteration; i += 1) {
    // eslint-disable-next-line no-await-in-loop
    await run(messageLength, numWorkers, difficulty, ttl);
  }
  currentTrace += 1;

  // eslint-disable-next-line
  console.log(`done for ${name}`);
}

function randomString(length) {
  let text = '';
  const possible =
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  for (let i = 0; i < length; i += 1) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
}

function addPoint(duration) {
  Plotly.extendTraces(plotlyDiv, { y: [[duration]] }, [currentTrace]);
}
async function startMessageLengthRun() {
  const iteration0 = parseFloat(document.getElementById('iteration0').value);
  const difficulty0 = parseFloat(document.getElementById('difficulty0').value);
  const numWorkers0 = parseFloat(document.getElementById('numWorkers0').value);
  const messageLengthStart0 = parseFloat(
    document.getElementById('messageLengthStart0').value
  );
  const messageLengthStop0 = parseFloat(
    document.getElementById('messageLengthStop0').value
  );
  const messageLengthStep0 = parseFloat(
    document.getElementById('messageLengthStep0').value
  );
  const TTL0 = parseFloat(document.getElementById('TTL0').value);
  for (
    let l = messageLengthStart0;
    l < messageLengthStop0;
    l += messageLengthStep0
  ) {
    // eslint-disable-next-line no-await-in-loop
    await runPoW({
      iteration: iteration0,
      difficulty: difficulty0,
      numWorkers: numWorkers0,
      messageLength: l,
      ttl: TTL0,
    });
  }
}
async function startNumWorkerRun() {
  const iteration1 = parseFloat(document.getElementById('iteration1').value);
  const difficulty1 = parseFloat(document.getElementById('difficulty1').value);
  const numWorkersStart1 = parseFloat(
    document.getElementById('numWorkersStart1').value
  );
  const numWorkersEnd1 = parseFloat(
    document.getElementById('numWorkersEnd1').value
  );
  const messageLength1 = parseFloat(
    document.getElementById('messageLength1').value
  );
  const TTL1 = parseFloat(document.getElementById('TTL1').value);
  for (
    let numWorkers = numWorkersStart1;
    numWorkers <= numWorkersEnd1;
    numWorkers += 1
  ) {
    // eslint-disable-next-line no-await-in-loop
    await runPoW({
      iteration: iteration1,
      difficulty: difficulty1,
      numWorkers,
      messageLength: messageLength1,
      ttl: TTL1,
    });
  }
}
async function startDifficultyRun() {
  const iteration2 = parseFloat(document.getElementById('iteration2').value);
  const messageLength2 = parseFloat(
    document.getElementById('messageLength2').value
  );
  const numWorkers2 = parseFloat(document.getElementById('numWorkers2').value);
  const difficultyStart2 = parseFloat(
    document.getElementById('difficultyStart2').value
  );
  const difficultyStop2 = parseFloat(
    document.getElementById('difficultyStop2').value
  );
  const difficultyStep2 = parseFloat(
    document.getElementById('difficultyStep2').value
  );
  const TTL2 = parseFloat(document.getElementById('TTL2').value);
  for (let n = difficultyStart2; n < difficultyStop2; n += difficultyStep2) {
    // eslint-disable-next-line no-await-in-loop
    await runPoW({
      iteration: iteration2,
      difficulty: n,
      numWorkers: numWorkers2,
      messageLength: messageLength2,
      ttl: TTL2,
    });
  }
}
async function starTTLRun() {
  const iteration3 = parseFloat(document.getElementById('iteration3').value);
  const difficulty3 = parseFloat(document.getElementById('difficulty3').value);
  const messageLength3 = parseFloat(
    document.getElementById('messageLength3').value
  );
  const numWorkers3 = parseFloat(document.getElementById('numWorkers3').value);
  const TTLStart3 = parseFloat(document.getElementById('TTLStart3').value);
  const TTLStop3 = parseFloat(document.getElementById('TTLStop3').value);
  const TTLStep3 = parseFloat(document.getElementById('TTLStep3').value);
  for (let ttl = TTLStart3; ttl < TTLStop3; ttl += TTLStep3) {
    // eslint-disable-next-line no-await-in-loop
    await runPoW({
      iteration: iteration3,
      difficulty: difficulty3,
      numWorkers: numWorkers3,
      messageLength: messageLength3,
      ttl,
    });
  }
}

// eslint-disable-next-line no-unused-vars
async function start(index) {
  const data = [];
  const layout = {};
  const options = {
    responsive: true,
  };
  plotlyDiv = `plotly${index}`;
  currentTrace = 0;
  window.chart = Plotly.newPlot(plotlyDiv, data, layout, options);
  workers.forEach(worker => worker.terminate());

  switch (index) {
    case 0:
      await startMessageLengthRun();
      break;
    case 1:
      await startNumWorkerRun();
      break;
    case 2:
      await startDifficultyRun();
      break;
    case 3:
      await starTTLRun();
      break;
    default:
      break;
  }
}