const fields = {
  connectionState: document.getElementById("connection-state"),
  stateDot: document.getElementById("state-dot"),
  robotName: document.getElementById("robot-name"),
  speedRatio: document.getElementById("speed-ratio"),
  motionState: document.getElementById("motion-state"),
  serverVersion: document.getElementById("server-version"),
  clientVersion: document.getElementById("client-version"),
  setupState: document.getElementById("setup-state"),
  enabledState: document.getElementById("enabled-state"),
  j519Status: document.getElementById("j519-status"),
  j519Sequence: document.getElementById("j519-sequence"),
  capturedAt: document.getElementById("captured-at"),
  dof: document.getElementById("dof"),
  joints: document.getElementById("joints"),
  pose: document.getElementById("pose"),
  trajectories: document.getElementById("trajectories"),
  refresh: document.getElementById("refresh"),
  linearStep: document.getElementById("linear-step"),
  angularStep: document.getElementById("angular-step"),
  jogStatus: document.getElementById("jog-status"),
  jogButtons: Array.from(document.querySelectorAll(".jog-button"))
};

const axisIndexes = {
  x: 0,
  y: 1,
  z: 2,
  w: 3,
  p: 4,
  r: 5
};

// 点动状态集中保存，确保按住按钮时不会并发发送多条 MovePose 请求。
const jogState = {
  timer: null,
  activeButton: null,
  inFlight: false,
  lastSnapshot: null
};

function formatArray(values) {
  if (!Array.isArray(values) || values.length === 0) {
    return "--";
  }

  return values.map(value => Number(value).toFixed(4)).join(", ");
}

function formatNullableBool(value) {
  if (value === true) {
    return "是";
  }

  if (value === false) {
    return "否";
  }

  return "--";
}

function formatJ519Status(snapshot) {
  if (snapshot.j519Status === null || snapshot.j519Status === undefined) {
    return "--";
  }

  const status = Number(snapshot.j519Status).toString(16).padStart(2, "0").toUpperCase();
  return `0x${status} accept=${formatNullableBool(snapshot.j519AcceptsCommand)} received=${formatNullableBool(snapshot.j519ReceivedCommand)} sysrdy=${formatNullableBool(snapshot.j519SystemReady)} motion=${formatNullableBool(snapshot.j519RobotInMotion)}`;
}

function setDot(connectionState) {
  fields.stateDot.className = "dot";
  if (connectionState === "Connected") {
    fields.stateDot.classList.add("good");
  } else if (connectionState === "NotConfigured") {
    fields.stateDot.classList.add("bad");
  }
}

function clampStep(input, min, max) {
  const value = Number(input.value);
  if (!Number.isFinite(value)) {
    input.value = String(min);
    return min;
  }

  const clamped = Math.min(Math.max(value, min), max);
  input.value = String(clamped);
  return clamped;
}

function getStepForAxis(axis) {
  // 平移和姿态使用不同单位，但共享 0.1 到 10 的现场可调范围。
  return axis === "x" || axis === "y" || axis === "z"
    ? clampStep(fields.linearStep, 0.1, 10)
    : clampStep(fields.angularStep, 0.1, 10);
}

function setJogStatus(message, isError) {
  fields.jogStatus.textContent = message;
  fields.jogStatus.classList.toggle("error", Boolean(isError));
}

async function loadSnapshotForJog() {
  const response = await fetch("/api/status/snapshot", { cache: "no-store" });
  if (!response.ok) {
    throw new Error(`状态快照读取失败: HTTP ${response.status}`);
  }

  const payload = await response.json();
  jogState.lastSnapshot = payload.snapshot;
  return payload.snapshot;
}

function buildJogPose(snapshot, axis, direction) {
  // 每次点动都从最新 TCP 位姿出发，只修改一个轴，避免连续按压时累积本地误差。
  const sourcePose = Array.isArray(snapshot.cartesianPose) ? snapshot.cartesianPose : [];
  if (sourcePose.length < 6) {
    throw new Error("当前 TCP 位姿不足 6 维，无法点动。");
  }

  const pose = sourcePose.slice(0, 6).map(Number);
  if (pose.some(value => !Number.isFinite(value))) {
    throw new Error("当前 TCP 位姿包含非数值，无法点动。");
  }

  const axisIndex = axisIndexes[axis];
  pose[axisIndex] = Number((pose[axisIndex] + direction * getStepForAxis(axis)).toFixed(6));
  return {
    x: pose[0],
    y: pose[1],
    z: pose[2],
    w: pose[3],
    p: pose[4],
    r: pose[5]
  };
}

async function sendJog(button) {
  // MovePose 底层会生成完整直角轨迹；前端这里只负责构造增量目标位姿。
  if (jogState.inFlight) {
    return;
  }

  const axis = button.dataset.axis;
  const direction = Number(button.dataset.direction);
  if (!Object.prototype.hasOwnProperty.call(axisIndexes, axis) || !Number.isFinite(direction)) {
    return;
  }

  jogState.inFlight = true;
  try {
    const snapshot = await loadSnapshotForJog();
    const pose = buildJogPose(snapshot, axis, direction);
    const response = await fetch("/move_pose/", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(pose)
    });

    if (!response.ok) {
      throw new Error(`MovePose 调用失败: HTTP ${response.status}`);
    }

    setJogStatus(`${axis.toUpperCase()}${direction > 0 ? "+" : "-"} 已发送`, false);
    await refreshStatus();
  } catch (error) {
    setJogStatus(error instanceof Error ? error.message : "点动失败", true);
    stopJog();
  } finally {
    jogState.inFlight = false;
  }
}

function startJog(event) {
  const button = event.currentTarget;
  if (!button || button.disabled) {
    return;
  }

  event.preventDefault();
  stopJog();
  jogState.activeButton = button;
  button.classList.add("active");
  sendJog(button);
  jogState.timer = window.setInterval(function () {
    sendJog(button);
  }, 250);
}

function stopJog() {
  if (jogState.timer !== null) {
    window.clearInterval(jogState.timer);
    jogState.timer = null;
  }

  if (jogState.activeButton) {
    jogState.activeButton.classList.remove("active");
    jogState.activeButton = null;
  }
}

async function refreshStatus() {
  fields.refresh.disabled = true;
  try {
    const response = await fetch("/api/status/snapshot", { cache: "no-store" });
    const payload = await response.json();
    const snapshot = payload.snapshot;
    jogState.lastSnapshot = snapshot;

    fields.connectionState.textContent = snapshot.connectionState;
    fields.robotName.textContent = payload.robotName || "--";
    fields.speedRatio.textContent = Number(snapshot.speedRatio).toFixed(2);
    fields.motionState.textContent = snapshot.isInMotion ? "是" : "否";
    fields.serverVersion.textContent = payload.serverVersion;
    fields.clientVersion.textContent = payload.clientVersion;
    fields.setupState.textContent = payload.isSetup ? "是" : "否";
    fields.enabledState.textContent = snapshot.isEnabled ? "是" : "否";
    fields.j519Status.textContent = formatJ519Status(snapshot);
    fields.j519Sequence.textContent = snapshot.j519Sequence ?? "--";
    fields.capturedAt.textContent = new Date(snapshot.capturedAt).toLocaleString();
    fields.dof.textContent = payload.degreesOfFreedom;
    fields.joints.textContent = formatArray(snapshot.jointPositions);
    fields.pose.textContent = formatArray(snapshot.cartesianPose);
    fields.trajectories.textContent = payload.uploadedTrajectories.length > 0
      ? payload.uploadedTrajectories.join(", ")
      : "--";
    fields.trajectories.classList.toggle("empty", payload.uploadedTrajectories.length === 0);
    setDot(snapshot.connectionState);
  } finally {
    fields.refresh.disabled = false;
  }
}

fields.refresh.addEventListener("click", refreshStatus);
fields.linearStep.addEventListener("change", function () {
  clampStep(fields.linearStep, 0.1, 10);
});
fields.angularStep.addEventListener("change", function () {
  clampStep(fields.angularStep, 0.1, 10);
});
fields.jogButtons.forEach(function (button) {
  button.addEventListener("pointerdown", startJog);
  button.addEventListener("pointerup", stopJog);
  button.addEventListener("pointercancel", stopJog);
  button.addEventListener("pointerleave", stopJog);
});
window.addEventListener("blur", stopJog);
window.addEventListener("keyup", function (event) {
  if (event.key === "Escape") {
    stopJog();
  }
});
refreshStatus();
window.setInterval(refreshStatus, 2000);
