(function () {
  const root = document.querySelector(".workbench");
  const fields = new Map(
    Array.from(document.querySelectorAll("[data-field]")).map((item) => [item.dataset.field, item])
  );
  const tabButtons = Array.from(document.querySelectorAll("[data-tab]"));

  const sources = {
    grl: "proc main()\n  run_path pick_place\nend",
    abb: "MODULE A120Smoke\n  PROC main()\n    MoveJ home,v100,fine,tool0;\n  ENDPROC\nENDMODULE",
    fanuc: "/PROG A120SMOKE\n/MN\n  1:J P[1] 40% FINE ;\n/END",
    kuka: "DEF A120SMOKE()\n  PTP HOME\nEND"
  };

  const state = {
    command: "ready",
    mode: "auto",
    motors: "on",
    pc: 0,
    joints: [0, 0, 0, 0, 0, 0],
    tcp: [0, 0, 580, 0, 0, 0],
    di1: false,
    do1: false,
    wait: "none",
    queue: [],
    trace: ["ready"],
    diagnostics: ["ready"],
    report: "A120-REPORT",
    tab: "grl"
  };

  function formatArray(values) {
    return `[${values.map((value) => Number(value).toFixed(value % 1 === 0 ? 0 : 2)).join(", ")}]`;
  }

  function setField(name, value) {
    const item = fields.get(name);
    if (item) item.textContent = value;
  }

  function render() {
    root?.setAttribute("data-last-command", state.command);
    root?.setAttribute("data-running", String(state.command === "running"));
    setField("state", state.command);
    setField("mode", state.mode);
    setField("motors", state.motors);
    setField("pc", `main:${state.pc}`);
    setField("joints", formatArray(state.joints));
    setField("tcp", formatArray(state.tcp));
    setField("log", state.trace.at(-1) ?? "ready");
    setField("diagnostics", state.diagnostics.at(-1) ?? "ready");
    setField("io", `do[1]=${state.do1}, di[1]=${state.di1}`);
    setField("wait", state.wait);
    setField("queue", state.queue.length ? state.queue.join(" -> ") : "empty");
    setField("trace", `${state.trace.length} events`);
    setField("reports", state.report);
    setField("editor", sources[state.tab]);

    for (const button of tabButtons) {
      button.setAttribute("aria-pressed", String(button.dataset.tab === state.tab));
    }
  }

  function pushTrace(message) {
    state.trace.push(message);
    state.diagnostics.push(message);
  }

  function advanceProgram(stepSize) {
    state.pc = Math.min(state.pc + stepSize, 4);
    state.joints = state.joints.map((value, index) => Number((value + (index + 1) * 1.5 * stepSize).toFixed(2)));
    state.tcp = [
      Number((state.tcp[0] + 12 * stepSize).toFixed(2)),
      Number((state.tcp[1] + 4 * stepSize).toFixed(2)),
      state.tcp[2],
      state.tcp[3],
      state.tcp[4],
      state.tcp[5]
    ];
  }

  function handleCommand(command) {
    state.command = command === "run" ? "running" : command;
    if (command === "load") {
      state.pc = 0;
      state.queue = ["movej home", "run_path pick_place"];
      state.wait = "none";
      pushTrace("program loaded");
    } else if (command === "run") {
      advanceProgram(2);
      state.do1 = true;
      state.wait = state.di1 ? "satisfied" : "waiting di[1]";
      state.queue = state.di1 ? ["movel place"] : ["wait di[1]", "movel place"];
      pushTrace(state.di1 ? "running path" : "waiting for di[1]");
    } else if (command === "pause") {
      pushTrace("program paused");
    } else if (command === "step") {
      state.command = "paused";
      advanceProgram(1);
      pushTrace(`stepped to main:${state.pc}`);
    } else if (command === "stop") {
      state.queue = [];
      pushTrace("program stopped");
    } else if (command === "reset") {
      state.command = "ready";
      state.pc = 0;
      state.joints = [0, 0, 0, 0, 0, 0];
      state.tcp = [0, 0, 580, 0, 0, 0];
      state.do1 = false;
      state.wait = "none";
      state.queue = [];
      pushTrace("controller reset");
    } else if (command === "export") {
      state.report = "A120-REPORT exported";
      pushTrace("delivery package exported");
    }
    render();
  }

  for (const button of document.querySelectorAll("[data-command]")) {
    button.addEventListener("click", () => handleCommand(button.dataset.command ?? "ready"));
  }

  for (const button of tabButtons) {
    button.addEventListener("click", () => {
      state.tab = button.dataset.tab ?? "grl";
      pushTrace(`${state.tab.toUpperCase()} source selected`);
      render();
    });
  }

  document.querySelector("[data-io='di1']")?.addEventListener("click", () => {
    state.di1 = !state.di1;
    state.wait = state.di1 ? "satisfied" : "waiting di[1]";
    pushTrace(`di[1]=${state.di1}`);
    render();
  });

  render();
})();
