import {
  CANVAS_W, CANVAS_H, MIN_SIZE, ELEMENT_DEFS, MODE_LABELS, PITCH_SHAPES,
  defaultLayout, normalizeLayout, syncReticleBox, defById,
  MAX_CUSTOM_RETICLES, CUSTOM_RETICLE_ID_MIN, CUSTOM_RETICLE_ID_MAX, BUILTIN_STYLE0,
  RETICLE_SCALE_MIN, RETICLE_SCALE_MAX
} from "./defaults.js";
import {
  createFb, fbClear, fbSet, fbFillRect, fbRect, fbHLine, fbVLine, fbText, fbBlit
} from "./pixelfont.js";
import {
  drawBuiltinStyle, drawCustomPixels, RETICLE_CX, RETICLE_CY
} from "./reticle_draw.js";

const STORAGE_KEY = "dsd-p-layout-draft-v2";

let layout = normalizeLayout(loadDraft() || defaultLayout());
let selectedId = "range";
let drag = null;
let zoom = 1;
const fb = createFb();
const selMask = createFb();

const canvas = document.getElementById("screen");
const ctx = canvas.getContext("2d", { alpha: false });
ctx.imageSmoothingEnabled = false;
const sidebar = document.getElementById("sidebar");
const toastEl = document.getElementById("toast");

function toast(msg) {
  toastEl.textContent = msg;
  toastEl.classList.add("show");
  setTimeout(() => toastEl.classList.remove("show"), 1800);
}

function applyZoom(z) {
  zoom = Math.max(1, Math.min(8, z | 0));
  canvas.width = CANVAS_W * zoom;
  canvas.height = CANVAS_H * zoom;
  canvas.style.width = `${CANVAS_W * zoom}px`;
  canvas.style.height = `${CANVAS_H * zoom}px`;
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.imageSmoothingEnabled = false;
  document.querySelectorAll("#zoomGroup button").forEach((btn) => {
    btn.classList.toggle("active", Number(btn.dataset.zoom) === zoom);
  });
  const hint = document.getElementById("zoomHint");
  if (hint) {
    hint.textContent = zoom === 1
      ? "128×64 实机像素预览（1:1）"
      : `128×64 位图 · 最近邻 ${zoom}× 放大`;
  }
  redraw();
}

function loadDraft() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY) || localStorage.getItem("dsd-p-layout-draft");
    return raw ? JSON.parse(raw) : null;
  } catch {
    return null;
  }
}

function saveDraft() {
  syncReticleBox(layout);
  localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
}

function elById(id) {
  return layout.elements.find((e) => e.id === id);
}

function clampEl(el) {
  const def = defById(el.id);
  if (!def) return;
  if (!def.modes.includes(el.mode)) el.mode = def.modes[0];
  const modeKey = el.mode === "none" ? "none" : el.mode;
  const min = (MIN_SIZE[el.id] && MIN_SIZE[el.id][modeKey]) || { w: 4, h: 4 };
  if (el.id === "reticle") {
    syncReticleBox(layout);
    return;
  }
  el.w = Math.max(min.w, Math.min(CANVAS_W, el.w | 0));
  el.h = Math.max(min.h, Math.min(CANVAS_H, el.h | 0));
  el.x = Math.max(0, Math.min(CANVAS_W - el.w, el.x | 0));
  el.y = Math.max(0, Math.min(CANVAS_H - el.h, el.y | 0));
  el.digitScale = Math.max(1, Math.min(3, el.digitScale | 0 || 1));
  el.shape = Math.max(0, Math.min(3, el.shape | 0 || 0));
}

function demoText(el) {
  switch (el.id) {
    case "range": return "12.4";
    case "heading": return el.mode === "numeric" ? "248°" : "NE";
    case "stability": return el.mode === "numeric" ? "72" : "";
    case "height": return "128";
    case "temp": return "24";
    case "ammo": return el.mode === "numeric" ? "30" : "";
    case "pitch": return el.mode === "numeric" ? "12" : "";
    default: return "?";
  }
}

function markSelRect(x, y, w, h) {
  fbRect(selMask, x, y, w, h, 1);
}

function drawPitchGraphic(el) {
  const { x, y, w, h, shape } = el;
  const thick = Math.max(1, h);
  if (shape === 1) {
    fbFillRect(fb, x, y, w, thick, 1);
  } else if (shape === 2) {
    const seg = Math.max(2, Math.floor(w / 3));
    fbFillRect(fb, x, y, seg, thick, 1);
    fbFillRect(fb, x + w - seg, y, seg, thick, 1);
    const rx = CANVAS_W - x - w;
    fbFillRect(fb, rx, y, seg, thick, 1);
    fbFillRect(fb, rx + w - seg, y, seg, thick, 1);
  } else if (shape === 3) {
    const arm = Math.max(2, Math.min(w, thick + 2));
    fbHLine(fb, x, y, w, 1);
    fbVLine(fb, x, y, arm, 1);
    fbVLine(fb, x + w - 1, y, arm, 1);
    const rx = CANVAS_W - x - w;
    fbHLine(fb, rx, y, w, 1);
    fbVLine(fb, rx, y, arm, 1);
    fbVLine(fb, rx + w - 1, y, arm, 1);
  } else {
    fbFillRect(fb, x, y, w, thick, 1);
    fbFillRect(fb, CANVAS_W - x - w, y, w, thick, 1);
  }
}

function drawElementPreview(el) {
  if (!el.enabled && el.id !== "reticle") return;
  const { x, y, w, h, mode, id } = el;

  if (id === "reticle") {
    const box = layout.reticle.scaleBox || 9;
    const style = layout.reticle.defaultStyle | 0;
    const setPx = (x, y) => fbSet(fb, x, y, 1);
    if (style >= CUSTOM_RETICLE_ID_MIN) {
      const custom = layout.reticles.find((r) => r.id === style);
      drawCustomPixels(setPx, RETICLE_CX, RETICLE_CY, custom?.pixels || []);
    } else {
      // Built-in: same as firmware boxScale = scaleBox/9
      drawBuiltinStyle(setPx, RETICLE_CX, RETICLE_CY, style, box / 9);
    }
    if (selectedId === id) markSelRect(x, y, w, h);
    return;
  }

  if (id === "pitch" && mode === "graphic") {
    drawPitchGraphic(el);
  } else if (mode === "graphic") {
    if (id === "stability") {
      fbFillRect(fb, x, y, w, h, 1);
    } else if (id === "ammo") {
      fbRect(fb, x, y, w, h, 1);
      const fill = Math.max(1, Math.floor(h * 0.6));
      fbFillRect(fb, x, y + h - fill, w, fill, 1);
    } else if (id === "heading") {
      fbText(fb, x, y, "NE", 1);
    }
  } else if (mode === "numeric") {
    fbText(fb, x, y, demoText(el), el.digitScale || 1);
  }

  if (selectedId === id) markSelRect(x, y, Math.max(w, 1), Math.max(h, 1));
}

function redraw() {
  fbClear(fb);
  fbClear(selMask);

  // Content margin guide (LEFT_MARGIN=24, DISPLAY_WIDTH=80) — 1px dashed feel via sparse dots
  for (let y = 0; y < 64; y += 2) {
    fbSet(fb, 24, y, 1);
    fbSet(fb, 103, y, 1);
  }

  for (const el of layout.elements) drawElementPreview(el);

  // Ensure canvas buffer size matches zoom (OLED pixels → screen blocks)
  if (canvas.width !== CANVAS_W * zoom || canvas.height !== CANVAS_H * zoom) {
    canvas.width = CANVAS_W * zoom;
    canvas.height = CANVAS_H * zoom;
    canvas.style.width = `${CANVAS_W * zoom}px`;
    canvas.style.height = `${CANVAS_H * zoom}px`;
  }

  fbBlit(
    fb,
    canvas,
    zoom,
    [230, 240, 232],
    [0, 0, 0],
    [70, 160, 100],
    selMask
  );
}

function hitPad(el) {
  // Ensure small elements (esp. heading) remain clickable
  const pad = 2;
  return {
    x0: el.x - pad,
    y0: el.y - pad,
    x1: el.x + Math.max(el.w, 8) + pad,
    y1: el.y + Math.max(el.h, 8) + pad
  };
}

function hitTest(px, py) {
  for (let i = layout.elements.length - 1; i >= 0; i--) {
    const el = layout.elements[i];
    if (!el.enabled) continue;
    const b = hitPad(el);
    if (px >= b.x0 && px < b.x1 && py >= b.y0 && py < b.y1) return el;
  }
  return null;
}

function canvasPos(ev) {
  const r = canvas.getBoundingClientRect();
  return {
    x: Math.max(0, Math.min(CANVAS_W - 1, Math.floor((ev.clientX - r.left) * CANVAS_W / r.width))),
    y: Math.max(0, Math.min(CANVAS_H - 1, Math.floor((ev.clientY - r.top) * CANVAS_H / r.height)))
  };
}

canvas.addEventListener("pointerdown", (ev) => {
  const p = canvasPos(ev);
  const hit = hitTest(p.x, p.y);
  if (!hit) return;
  selectedId = hit.id;
  const def = defById(hit.id);
  if (def && def.movable) {
    drag = { id: hit.id, ox: p.x - hit.x, oy: p.y - hit.y };
    canvas.setPointerCapture(ev.pointerId);
  } else {
    drag = null;
  }
  renderSidebar();
  redraw();
});

canvas.addEventListener("pointermove", (ev) => {
  if (!drag) return;
  const def = defById(drag.id);
  if (!def || !def.movable) return;
  const p = canvasPos(ev);
  const el = elById(drag.id);
  el.x = p.x - drag.ox;
  el.y = p.y - drag.oy;
  clampEl(el);
  redraw();
});

canvas.addEventListener("pointerup", () => {
  if (drag) {
    drag = null;
    saveDraft();
    renderSidebar();
  }
});

function nextCustomId() {
  const used = new Set(layout.reticles.map((r) => r.id));
  for (let id = CUSTOM_RETICLE_ID_MIN; id <= CUSTOM_RETICLE_ID_MAX; id++) {
    if (!used.has(id)) return id;
  }
  return -1;
}

function renderSidebar() {
  syncReticleBox(layout);
  const parts = [];
  parts.push("<h2>元素</h2>");
  for (const def of ELEMENT_DEFS) {
    const el = elById(def.id);
    if (!el) continue;
    clampEl(el);
    const active = selectedId === el.id ? " active" : "";
    const showMode = def.modes.length > 1;
    const showDigit = def.digit && el.mode === "numeric";
    const showSize = def.size;
    const showPos = def.movable;
    const modes = def.modes
      .map((m) => `<option value="${m}" ${el.mode === m ? "selected" : ""}>${MODE_LABELS[m] || m}</option>`)
      .join("");
    const minSz = MIN_SIZE[el.id]?.[el.mode === "none" ? "none" : el.mode] || {};
    let shapeRow = "";
    if (def.shapes && el.mode === "graphic") {
      const opts = PITCH_SHAPES.map((s) =>
        `<option value="${s.id}" ${el.shape === s.id ? "selected" : ""}>${s.label}</option>`
      ).join("");
      shapeRow = `<div class="row"><label>形状</label><select data-act="shape">${opts}</select></div>`;
    }
    parts.push(`
      <div class="el-card${active}" data-id="${el.id}">
        <div class="title">
          <input type="checkbox" data-act="en" ${el.enabled ? "checked" : ""} />
          <span>${def.label}</span>
          ${!def.movable ? '<span style="color:var(--muted);font-size:11px;font-weight:400">（位置固定）</span>' : ""}
        </div>
        ${showMode ? `<div class="row"><label>模式</label><select data-act="mode">${modes}</select></div>` : ""}
        ${shapeRow}
        ${showPos ? `<div class="row"><label>位置</label>
          <input type="number" data-act="x" value="${el.x}" />
          <input type="number" data-act="y" value="${el.y}" /></div>` : ""}
        ${showSize ? `<div class="row"><label>尺寸</label>
          <input type="number" data-act="w" value="${el.w}" />
          <input type="number" data-act="h" value="${el.h}" />
          <span style="color:var(--muted);font-size:11px">最小 ${minSz.w || "?"}×${minSz.h || "?"}</span></div>` : ""}
        ${showDigit ? `<div class="row"><label>字号</label>
          <input type="number" data-act="digitScale" min="1" max="3" value="${el.digitScale || 1}" /></div>` : ""}
      </div>
    `);
  }

  parts.push("<h2>准星</h2>");
  parts.push(`
    <div class="el-card reticle-tools">
      <div class="row">
        <label>像素盒</label>
        <input type="number" id="scaleBox" min="${RETICLE_SCALE_MIN}" max="${RETICLE_SCALE_MAX}" value="${layout.reticle.scaleBox || 9}" />
        <span style="color:var(--muted);font-size:11px">${RETICLE_SCALE_MIN}–${RETICLE_SCALE_MAX}，每格=1 OLED 像素</span>
      </div>
      <div class="row">
        <label>样式</label>
        <select id="styleSel"></select>
        <button type="button" id="btnAddStyle">+ 自定义</button>
        <button type="button" id="btnDelStyle" class="danger">删除</button>
      </div>
      <div class="pix-scroll"><canvas id="pixCanvas"></canvas></div>
      <p style="color:var(--muted);font-size:11px;margin:6px 0 0">点阵区可滚动；每格=1 OLED 像素（相对中心）。像素盒最大 60×60。</p>
    </div>
  `);

  sidebar.innerHTML = parts.join("");

  sidebar.querySelectorAll(".el-card[data-id]").forEach((card) => {
    const id = card.getAttribute("data-id");
    card.addEventListener("click", (ev) => {
      if (ev.target.closest("input,select,button")) return;
      selectedId = id;
      renderSidebar();
      redraw();
    });
    card.querySelectorAll("[data-act]").forEach((inp) => {
      inp.addEventListener("change", () => {
        const el = elById(id);
        const act = inp.getAttribute("data-act");
        if (act === "en") el.enabled = inp.checked;
        else if (act === "mode") el.mode = inp.value;
        else el[act] = Number(inp.value);
        clampEl(el);
        saveDraft();
        renderSidebar();
        redraw();
      });
    });
  });

  const styleSel = document.getElementById("styleSel");
  for (let i = 0; i < 16; i++) {
    const o = document.createElement("option");
    o.value = String(i);
    o.textContent = `内置 ${i}`;
    styleSel.appendChild(o);
  }
  for (const r of layout.reticles) {
    const o = document.createElement("option");
    o.value = String(r.id);
    o.textContent = r.name || `自定义 ${r.id}`;
    styleSel.appendChild(o);
  }
  styleSel.value = String(layout.reticle.defaultStyle || 0);

  document.getElementById("scaleBox").addEventListener("change", (e) => {
    layout.reticle.scaleBox = Math.max(
      RETICLE_SCALE_MIN,
      Math.min(RETICLE_SCALE_MAX, Number(e.target.value) || 9)
    );
    syncReticleBox(layout);
    saveDraft();
    renderSidebar();
    redraw();
  });

  styleSel.addEventListener("change", () => {
    layout.reticle.defaultStyle = Number(styleSel.value);
    saveDraft();
    renderPixGrid();
    redraw();
  });

  document.getElementById("btnAddStyle").addEventListener("click", () => {
    if (layout.reticles.length >= MAX_CUSTOM_RETICLES) {
      toast("自定义准星最多 10 个");
      return;
    }
    const id = nextCustomId();
    if (id < 0) return;
    layout.reticles.push({ id, name: `自定义${id}`, pixels: [[0, 0], [-1, 0], [1, 0], [0, -1], [0, 1]] });
    layout.reticle.defaultStyle = id;
    saveDraft();
    renderSidebar();
    redraw();
    toast(`已添加样式 ${id}`);
  });

  document.getElementById("btnDelStyle").addEventListener("click", () => {
    const id = layout.reticle.defaultStyle;
    if (id < CUSTOM_RETICLE_ID_MIN) {
      toast("内置样式不可删除");
      return;
    }
    layout.reticles = layout.reticles.filter((r) => r.id !== id);
    layout.reticle.defaultStyle = 0;
    saveDraft();
    renderSidebar();
    redraw();
  });

  renderPixGrid();
}

function renderPixGrid() {
  const c = document.getElementById("pixCanvas");
  if (!c) return;

  const box = Math.max(RETICLE_SCALE_MIN, Math.min(RETICLE_SCALE_MAX, layout.reticle.scaleBox || 9));
  const half = Math.floor(box / 2);
  const x0 = -half;
  const y0 = -half;
  const styleId = layout.reticle.defaultStyle;
  const custom = layout.reticles.find((r) => r.id === styleId);
  const editable = !!custom;

  const cell = box > 40 ? 7 : box > 24 ? 8 : 10;
  const gap = 1;
  const stride = cell + gap;
  const pxSize = box * stride + gap;

  c.width = pxSize;
  c.height = pxSize;
  c.style.width = `${pxSize}px`;
  c.style.height = `${pxSize}px`;

  let pixels = custom?.pixels || [];
  if (!custom && styleId < 16) {
    const pts = [];
    drawBuiltinStyle(
      (x, y) => pts.push([x - RETICLE_CX, y - RETICLE_CY]),
      RETICLE_CX,
      RETICLE_CY,
      styleId,
      box / 9
    );
    pixels = pts;
  }
  const on = new Set(pixels.map(([a, b]) => `${a},${b}`));

  const pctx = c.getContext("2d", { alpha: false });
  pctx.imageSmoothingEnabled = false;
  pctx.fillStyle = "#2a3036";
  pctx.fillRect(0, 0, pxSize, pxSize);
  for (let row = 0; row < box; row++) {
    for (let col = 0; col < box; col++) {
      const ox = x0 + col;
      const oy = y0 + row;
      const sx = gap + col * stride;
      const sy = gap + row * stride;
      pctx.fillStyle = on.has(`${ox},${oy}`) ? "#d8f0e0" : "#0a0c0e";
      pctx.fillRect(sx, sy, cell, cell);
      if (ox === 0 && oy === 0) {
        pctx.strokeStyle = "#5ecf8e";
        pctx.lineWidth = 1;
        pctx.strokeRect(sx + 0.5, sy + 0.5, cell - 1, cell - 1);
      }
    }
  }

  // Store hit params for event handlers
  c._pixHit = { box, x0, y0, gap, stride, editable };

  if (c.dataset.bound === "1") return;
  c.dataset.bound = "1";

  let painting = false;
  let paintMode = "set";

  const hitCell = (ev) => {
    const h = c._pixHit;
    if (!h) return null;
    const r = c.getBoundingClientRect();
    const mx = (ev.clientX - r.left) * (c.width / r.width);
    const my = (ev.clientY - r.top) * (c.height / r.height);
    const col = Math.floor((mx - h.gap) / h.stride);
    const row = Math.floor((my - h.gap) / h.stride);
    if (col < 0 || row < 0 || col >= h.box || row >= h.box) return null;
    return { ox: h.x0 + col, oy: h.y0 + row };
  };

  const applyPaint = (ox, oy) => {
    const cur = layout.reticles.find((r) => r.id === layout.reticle.defaultStyle);
    if (!cur) return;
    const idx = cur.pixels.findIndex(([a, b]) => a === ox && b === oy);
    if (paintMode === "set") {
      if (idx < 0 && cur.pixels.length < 128) cur.pixels.push([ox, oy]);
    } else if (idx >= 0) {
      cur.pixels.splice(idx, 1);
    }
    saveDraft();
    renderPixGrid();
    redraw();
  };

  c.addEventListener("contextmenu", (e) => e.preventDefault());
  c.addEventListener("pointerdown", (ev) => {
    if (!c._pixHit?.editable) return;
    const cellHit = hitCell(ev);
    if (!cellHit) return;
    painting = true;
    c.setPointerCapture(ev.pointerId);
    const cur = layout.reticles.find((r) => r.id === layout.reticle.defaultStyle);
    if (!cur) return;
    const idx = cur.pixels.findIndex(([a, b]) => a === cellHit.ox && b === cellHit.oy);
    if (ev.button === 2 || ev.shiftKey) paintMode = "clear";
    else paintMode = idx >= 0 ? "clear" : "set";
    applyPaint(cellHit.ox, cellHit.oy);
  });
  c.addEventListener("pointermove", (ev) => {
    if (!painting || !c._pixHit?.editable) return;
    const cellHit = hitCell(ev);
    if (!cellHit) return;
    applyPaint(cellHit.ox, cellHit.oy);
  });
  c.addEventListener("pointerup", () => { painting = false; });
  c.addEventListener("pointercancel", () => { painting = false; });
}

function validateLayout(doc) {
  if (!doc || (doc.product !== "dsd" && doc.product !== "dsd_g1") || doc.schema !== 1) {
    throw new Error("产品/版本不匹配");
  }
  if (!doc.canvas || doc.canvas.w !== 128 || doc.canvas.h !== 64) throw new Error("画布须为 128×64");
  if (!Array.isArray(doc.elements)) throw new Error("缺少 elements");
  if (doc.reticles && doc.reticles.length > 10) throw new Error("自定义准星过多");
}

document.getElementById("btnExport").addEventListener("click", () => {
  layout = normalizeLayout(layout);
  layout.elements.forEach(clampEl);
  validateLayout(layout);
  const blob = new Blob([JSON.stringify(layout, null, 2)], { type: "application/json" });
  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = "layout.json";
  a.click();
  URL.revokeObjectURL(a.href);
  saveDraft();
  toast("已导出 layout.json");
});

document.getElementById("btnImport").addEventListener("click", () => {
  document.getElementById("fileImport").click();
});

document.getElementById("fileImport").addEventListener("change", async (ev) => {
  const file = ev.target.files?.[0];
  if (!file) return;
  try {
    const doc = JSON.parse(await file.text());
    validateLayout(doc);
    layout = normalizeLayout(doc);
    layout.elements.forEach(clampEl);
    saveDraft();
    renderSidebar();
    redraw();
    toast("导入成功");
  } catch (e) {
    toast(String(e.message || e));
  }
  ev.target.value = "";
});

document.getElementById("btnReset").addEventListener("click", () => {
  layout = defaultLayout();
  syncReticleBox(layout);
  saveDraft();
  renderSidebar();
  redraw();
  toast("已恢复默认布局");
});

renderSidebar();
document.getElementById("zoomGroup").addEventListener("click", (ev) => {
  const btn = ev.target.closest("button[data-zoom]");
  if (!btn) return;
  applyZoom(Number(btn.dataset.zoom));
});
applyZoom(Math.max(2, zoom));
