const MESSAGES = {
  COMPILE: "COMPILE",
  COMPILE_RESULT: "COMPILE_RESULT",
  EVAL: "EVAL",
  EVAL_STATUS: "EVAL_RESULT",
};

// -- the "real" code --

const state = new Observable();

const prepareSource = (text) => text.replaceAll(/\/\/.*/g, "").trim();

const main = () => {
  let program;

  state.subscribe((msg) => {
    if (msg.type == MESSAGES.COMPILE) {
      const { value } = msg;
      const source = prepareSource(value);

      try {
        const ast = parser.parse(source);
        const { js, godelSequence } = compile(ast);

        state.notify({
          type: MESSAGES.COMPILE_RESULT,
          success: true,
          js,
          godelSequence,
        });
      } catch (e) {
        console.error(e);
        state.notify({
          type: MESSAGES.COMPILE_RESULT,
          error: e.toString(),
        });
      }
    }
    if (msg.type == MESSAGES.EVAL) {
      const source = compiledEditorEl.getValue();
      try {
        const result = eval(source);
        state.notify({
          type: MESSAGES.EVAL_RESULT,
          success: true,
          value: result,
        });
      } catch (e) {
        state.notify({
          type: MESSAGES.EVAL_RESULT,
          error: e.toString(),
        });
      }
    }
  });
};
main();

// -- a bit of some hacky ui code --

const codeMirrorConfig = {
  lineNumbers: true,
};
const instructionsEl = document.getElementById("instructions");
const instructionsEditorEl = CodeMirror.fromTextArea(
  instructionsEl,
  codeMirrorConfig
);

const compileButton = document.getElementById("compile");
const evalButton = document.getElementById("eval");
const evalStatusEl = document.getElementById("eval_status");
const compileStatusEl = document.getElementById("compile_status");
const compiledEl = document.getElementById("compiled");
const compiledEditorEl = CodeMirror.fromTextArea(compiledEl, codeMirrorConfig);
const godelSequenceEl = document.getElementById("godel_sequence");
const godelNumberEl = document.getElementById("godel_number");
const godelNumberComputeBtn = document.getElementById("godel_number_comp");
const copyStateButton = document.getElementById("copy");

// hackily copy the program state and put it in the clipboard
copyStateButton.addEventListener("click", () => {
  const instructions = btoa(instructionsEditorEl.getValue());

  navigator.clipboard
    .writeText(
      window.location.href.split("?")[0] + `?instructions=${instructions}`
    )
    .then(() => alert("copied to clipboard"));
});

state.subscribe((msg) => {
  if (msg.type == MESSAGES.COMPILE_RESULT) {
    evalStatusEl.classList.remove("error");
    evalStatusEl.classList.remove("success");
    evalStatusEl.innerHTML = "";

    if (msg.success) {
      const { js, godelSequence } = msg;
      compiledEditorEl.setValue(js);

      godelSequenceEl.innerHTML = `[${godelSequence.join(", ")}]`;
      godelNumberComputeBtn.style.display = "inline";

      compileStatusEl.classList.add("success");
      compileStatusEl.classList.remove("error");
      compileStatusEl.innerHTML = `Successful compile at ${new Date().toLocaleString()}!`;
    } else if (msg.error) {
      compiledEditorEl.setValue("");
      godelSequenceEl.innerHTML = "";
      godelNumberEl.innerHTML = "";
      godelNumberComputeBtn.style.display = "none";

      compileStatusEl.classList.remove("success");
      compileStatusEl.classList.add("error");
      compileStatusEl.innerHTML = msg.error;
    }
  }
});

state.subscribe((msg) => {
  if (msg.type == MESSAGES.COMPILE_RESULT) {
    if (msg.success) {
      const { godelSequence } = msg;

      godelNumberComputeBtn.onclick = () => {
        godelNumberEl.innerHTML = "working...";

        const worker = new Worker("js/godelWorker.js");

        worker.addEventListener("message", (e) => {
          const godelNumber = e.data;
          godelNumberEl.innerHTML = godelNumber.toString();
        });

        worker.postMessage(godelSequence);
      };
    } else if (msg.error) {
      godelNumberComputeBtn.onclick = () => {};
    }
  }
});

state.subscribe((msg) => {
  if (msg.type == MESSAGES.EVAL_RESULT) {
    if (msg.success) {
      evalStatusEl.classList.add("success");
      evalStatusEl.classList.remove("error");
      evalStatusEl.innerHTML = `Result: ${msg.value}`;
    } else if (msg.error) {
      evalStatusEl.classList.remove("success");
      evalStatusEl.classList.add("error");
      evalStatusEl.innerHTML = msg.error;
    }
  }
});

const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("instructions")) {
  instructionsEditorEl.setValue(atob(urlParams.get("instructions")));
}

compileButton.addEventListener("click", () => {
  state.notify({
    type: MESSAGES.COMPILE,
    value: instructionsEditorEl.getValue(),
  });
});

evalButton.addEventListener("click", () => {
  state.notify({
    type: MESSAGES.EVAL,
  });
});
