/**
 * Global flag that indicates if any bits should be transmitted. This flag
 * is used to stop the transmission of bits.
 *
 * @type {Boolean}
 */
let isTransmitting = false;

/**
 * Array that contains all currently running web workers.
 *
 * @type {Worker[]}
 */
let workers = [];

/**
 * @type {HTMLInputElement}
 */
const textInput = document.getElementById("textInput");

/**
 * @type {HTMLButtonElement}
 */
const startSendingButton = document.getElementById("start-sending-button");

/**
 * @type {HTMLButtonElement}
 */
const stopSendingButton = document.getElementById("stop-sending-button");

/**
 * @type {HTMLButtonElement}
 */
const startCalibrationButton = document.getElementById("start-calibration-button");

/**
 * @type {HTMLButtonElement}
 */
const stopCalibrationButton = document.getElementById("stop-calibration-button");

// Add event listeners to the buttons
startCalibrationButton.addEventListener("click", startCalibration);
stopCalibrationButton.addEventListener("click", stopCalibration);
startSendingButton.addEventListener("click", startSending);
stopSendingButton.addEventListener("click", stopSending);

/**
 * Starts the web workers and add them to the global worker array.
 */
function startWorkers() {
  for (let i = 0; i < Constants.NUMBER_OF_WORKERS; i++) {
    workers.push(new Worker("./scripts/Worker.js"));
  }
}

/**
 * Terminates all currently running workers and clear out the worker array.
 */
function stopWorkers() {
  for (const worker of workers) {
    worker.terminate();
  }

  workers = [];
}

/**
 * Starts the calibration process by simply starting all web workers.
 */
async function startCalibration() {
  Utility.setMessage("Calibration currently ongoing.");

  startCalibrationButton.classList.add(Constants.HIDE_CLASS);
  stopCalibrationButton.classList.remove(Constants.HIDE_CLASS);

  isTransmitting = true;
  while (isTransmitting) {
    for (let i = 0; i < Constants.CALIBRATION_SIGNAL.length; i++) {
      if (!isTransmitting) {
        stopWorkers();
        return;
      }

      await transmitBit(Constants.CALIBRATION_SIGNAL[i], Constants.CLOCK_TIME);
    }
  }
}

/**
 * Stops the calibration process by stopping all currently running web workers.
 */
function stopCalibration() {
  Utility.setMessage("");

  stopCalibrationButton.classList.add(Constants.HIDE_CLASS);
  startCalibrationButton.classList.remove(Constants.HIDE_CLASS);

  isTransmitting = false;
}

/**
 * Starts sending the message in the input after validating that the input is
 * valid.
 */
function startSending() {
  // Validate the input
  textInput.setCustomValidity("");
  if (!textInput.checkValidity()) {
    if (textInput.validity.valueMissing) {
      textInput.setCustomValidity("Please enter between 1 and 16 characters.");
    }

    if (textInput.validity.patternMismatch) {
      textInput.setCustomValidity("Please only enter valid ASCII characters.");
    }

    textInput.reportValidity();
    return;
  }

  Utility.setMessage(`Sending message: ${textInput.value}`);
  console.log(`Start sending message: ${textInput.value}`);
  startSendingButton.classList.add(Constants.HIDE_CLASS);
  stopSendingButton.classList.remove(Constants.HIDE_CLASS);

  const packet = new Packet(textInput.value);
  console.log(packet);
  const signal = Utility.manchesterEncode(packet.getData());

  isTransmitting = true;
  transmitSignal(signal);
}

/**
 * Stop the sending process.
 */
function stopSending() {
  console.log("Stop Sending");
  Utility.setMessage("");

  stopSendingButton.classList.add(Constants.HIDE_CLASS);
  startSendingButton.classList.remove(Constants.HIDE_CLASS);
  isTransmitting = false;
}

/**
 * Transmits the given bit array until the sending is stopped. The transmission
 * is stopped when the global isTransmitting flag is set to false.
 *
 * @param {Number[]} signal
 */
async function transmitSignal(signal) {
  while (isTransmitting) {
    // Send the preamble
    for (const bit of Constants.PREAMBLE) {
      if (!isTransmitting) {
        stopWorkers();
        return;
      }

      console.log(`Sending preamble: ${bit}`);
      await transmitBit(bit, Constants.PREAMBLE_CLOCK_TIME);
    }

    for (let i = 0; i < signal.length; i++) {
      if (!isTransmitting) {
        stopWorkers();
        return;
      }

      console.log(`Sending symbol ${i + 1} of ${signal.length} of packet: ${signal[i]}`);
      await transmitBit(signal[i], Constants.CLOCK_TIME);
    }
  }
}

/**
 * Starts or stops the web workers depending on the bit value and then waits
 * for a duration of clockTime.
 *
 * @param {Number} bit
 */
function transmitBit(bit, clockTime) {
  if (bit === 1) {
    startWorkers();
    return new Promise((resolve) => setTimeout(resolve, clockTime));
  }

  stopWorkers();
  return new Promise((resolve) => setTimeout(resolve, clockTime));
}
