// =====================================================
// Utilities
// =====================================================
const $ = (sel) => document.querySelector(sel);

function toast(msg) {
  const t = $("#toast");
  if (!t) return;
  t.textContent = msg;
  t.classList.add("show");
  setTimeout(() => t.classList.remove("show"), 1100);
}

function wrapIndex(i, len){
  if (len <= 0) return 0;
  return ((i % len) + len) % len;
}


function clamp(n, min, max) {
  return Math.max(min, Math.min(max, n));
}

function deepMerge(a = {}, b = {}) {
  const out = JSON.parse(JSON.stringify(a));
  for (const k of Object.keys(b || {})) {
    if (b[k] && typeof b[k] === "object" && !Array.isArray(b[k])) {
      out[k] = deepMerge(out[k] || {}, b[k]);
    } else {
      out[k] = b[k];
    }
  }
  return out;
}

// ✅ 統一預設 offsets（避免舊 preset / 不完整資料導致 s 變 1）
const DEFAULT_OFFSETS = {
  head: { x: 0, y: -95, s: 0.3 },
  body: { x: -5, y: 61, s: 0.6 },
  legs: { x: 8, y: 300, s: 0.88 },
  acc: { x: 0, y: 0, s: 1 }
};

function normalizeOffsets(input = {}) {
  return deepMerge(DEFAULT_OFFSETS, input || {});
}

// =====================================================
// Load Manifest + resolve inherit
// =====================================================
let MANIFEST = null;

async function loadManifest() {
  const res = await fetch("./manifest.json");
  if (!res.ok) throw new Error("manifest.json 載入失敗");
  MANIFEST = await res.json();
  resolveInheritAll();
}

function resolveInheritAll(){
  for (const gender of Object.keys(MANIFEST || {})){
    const poses = MANIFEST[gender]?.poses || {};
    for (const poseKey of Object.keys(poses)){
      // ✅ tuning 不是 pose，跳過
      if (poseKey === "tuning") continue;
      poses[poseKey] = resolvePose(gender, poseKey);
    }
  }
}


function resolvePose(gender, poseKey) {
  const poses = MANIFEST[gender]?.poses || {};
  const pose = poses[poseKey];
  if (!pose) throw new Error(`Pose missing: ${gender}/${poseKey}`);
  if (!pose.inherit) return pose;

  const baseKey = pose.inherit;
  const base = resolvePose(gender, baseKey);

  // merge: base + override
  return {
    head: pose.head || base.head,
    body: pose.body || base.body,
    legs: pose.legs || base.legs,
    acc: pose.acc || base.acc
  };
}

// =====================================================
// State
// =====================================================

const RANGE = {
  head: { x: [-300, 300], y: [-300, 300], s: [0.2, 1.4] },
  body: { x: [-400, 400], y: [-500, 500], s: [0.2, 1.6] },
  legs: { x: [-500, 500], y: [-900, 1200], s: [0.2, 1.8] }, // ✅ 腿拉大
  acc:  { x: [-600, 600], y: [-600, 600], s: [0.2, 2.0] }
};

function setRange(inputEl, min, max, step = 1){
  inputEl.min = String(min);
  inputEl.max = String(max);
  inputEl.step = String(step);
}

const state = {
  gender: "female",
  pose: "walking",

  pick: { head: 0, body: 0, legs: 0, acc: 0 },

  // ✅ 永遠用 normalizeOffsets 確保 x/y/s 齊全
  offsets: normalizeOffsets({}),

  // ✅ 角色整體上移（stage 裡往上）
  global: { y: 0 },

  tuningTarget: "head",
  headBehind: true,

  // carousel scroll offset per part
  car: { head: 0, body: 0, legs: 0, acc: 0 }
};

// =====================================================
// Elements
// =====================================================
const character = $("#character");
const layerHead = $("#layerHead");
const layerBody = $("#layerBody");
const layerLegs = $("#layerLegs");
const layerAcc = $("#layerAcc");

const track = {
  head: $("#trackHead"),
  body: $("#trackBody"),
  legs: $("#trackLegs"),
  acc: $("#trackAcc")
};

const offsetX = $("#offsetX");
const offsetY = $("#offsetY");
const offsetS = $("#offsetS");
const offsetSVal = $("#offsetSVal");
const offsetXVal = $("#offsetXVal");
const offsetYVal = $("#offsetYVal");

// =====================================================
// Data getters
// =====================================================
function currentPosePack() {
  return MANIFEST?.[state.gender]?.poses?.[state.pose];
}

function currentList(part) {
  return currentPosePack()?.[part] || [];
}



// =====================================================
// ✅ Apply tuning from manifest (gender + pose override)
// =====================================================
function getGenderTuning(gender){
  // ✅ 支援兩種結構：
  // A: MANIFEST[gender].tuning
  // B: MANIFEST[gender].poses.tuning  (你 female 目前是這種)
  return (
    MANIFEST?.[gender]?.tuning ||
    MANIFEST?.[gender]?.poses?.tuning ||
    null
  );
}


function applyTuningFromManifest() {
  const t = getGenderTuning(state.gender);
  if (!t) {
    // 沒 tuning 就不動（但至少確保 offsets 完整）
    state.offsets = normalizeOffsets(state.offsets);
    state.global = state.global || { y: 0 };
    if (character) character.style.transform = `translateY(${state.global.y || 0}px)`;
    return;
  }

  // base tuning
  let merged = {
    global: t.global || { y: 0 },
    parts: t.parts || {},
    poses: t.poses || {}
  };

  // pose override
  const poseOver = merged.poses?.[state.pose] || {};
  const finalT = deepMerge({ global: merged.global, parts: merged.parts }, poseOver);

  state.global = finalT.global || { y: 0 };

  // ✅ offsets：用 DEFAULT_OFFSETS 當底，再覆蓋 tuning parts
  const base = normalizeOffsets({});
  const partsOver = finalT.parts || {};

  state.offsets = normalizeOffsets({
    head: { ...base.head, ...(partsOver.head || {}) },
    body: { ...base.body, ...(partsOver.body || {}) },
    legs: { ...base.legs, ...(partsOver.legs || {}) },
    acc:  { ...base.acc,  ...(partsOver.acc  || {}) }
  });

  if (character) character.style.transform = `translateY(${state.global.y || 0}px)`;
}


// =====================================================
// Render: character layers
// =====================================================
function applyMeta(imgEl, partKey) {
  if (!imgEl) return;
  const o = state.offsets?.[partKey] || DEFAULT_OFFSETS[partKey];

  const x = Number(o.x || 0);
  const y = Number(o.y || 0);
  const s = Number(o.s ?? 1);

  // ✅ 關鍵：scale 一定有值，不會突然變大
  imgEl.style.transform = `translate(calc(-50% + ${x}px), calc(-50% + ${y}px)) scale(${s})`;
}

function setLayerSources(){
  const pack = currentPosePack();

  const headItem = pack.head?.[state.pick.head];
  const bodyItem = pack.body?.[state.pick.body];
  const legsItem = pack.legs?.[state.pick.legs];
  const accItem  = (pack.acc && pack.acc.length) ? pack.acc?.[state.pick.acc] : null;

  // head/body/legs：正常設
  layerHead.src = headItem?.src || "";
  layerBody.src = bodyItem?.src || "";
  layerLegs.src = legsItem?.src || "";

  // ✅ acc：沒有就「移除 src + 隱藏」(避免破圖 icon)
  if (accItem?.src) {
    layerAcc.style.display = "";
    layerAcc.src = accItem.src;
  } else {
    layerAcc.removeAttribute("src");   // 關鍵：避免顯示破圖 icon
    layerAcc.style.display = "none";   // 關鍵：不佔位
  }

  applyMeta(layerHead, "head");
  applyMeta(layerBody, "body");
  applyMeta(layerLegs, "legs");
  // acc 只有在顯示時才套 meta（避免你之後做 pointer/scale 出怪事）
  if (layerAcc.style.display !== "none") applyMeta(layerAcc, "acc");

  character.classList.toggle("head-front", !state.headBehind);
  character.classList.toggle("head-behind", state.headBehind);

  const btn = $("#btnHeadBehind");
  if (btn) btn.textContent = `頭在後：${state.headBehind ? "ON" : "OFF"}`;
}

function setGender(nextGender){
  if (!MANIFEST?.[nextGender]) {
    console.warn("Gender not found in manifest:", nextGender);
    return;
  }

  state.gender = nextGender;

  // reset picks & carousel position
  state.pick = { head: 0, body: 0, legs: 0, acc: 0 };
  state.car  = { head: 0, body: 0, legs: 0, acc: 0 };

  // ✅ 每次切 gender 都重新套用 tuning（不然你會沿用上一個 gender 的縮放/位移）
  applyTuningFromManifest();

  renderAllThumbs();
  setLayerSources();
  syncTuningUI();
}



function renderCharacterAnimated() {
  if (!character) return setLayerSources();
  
}

// =====================================================
// Render: carousel thumbs
// =====================================================
function applyCarouselTransform(part){
  const list = currentList(part);
  const trackEl = track[part];
  const viewport = trackEl.closest(".car-viewport");
  if (!viewport) return;

  const maxOffset = Math.max(0, list.length - 1);
  state.car[part] = clamp(state.car[part], 0, maxOffset);

  // ✅ 一次一個：每步 = viewport 寬度
  const step = viewport.getBoundingClientRect().width;
  trackEl.style.transform = `translateX(${-state.car[part] * step}px)`;
}


function slideCarousel(part, dir){
  const list = currentList(part);
  if (!list.length) return;

  // ✅ 左右鍵直接切換造型 index
  const next = clamp(state.pick[part] + dir, 0, list.length - 1);
  state.pick[part] = next;

  // 同步 carousel 顯示到同一格
  state.car[part] = next;

  renderAllThumbs();
  setLayerSources(); // 瞬間套用
}

function renderThumbs(part){
  const list = currentList(part);
  const trackEl = track[part];
  trackEl.innerHTML = "";

  // 找到這一列的 prev/next 按鈕（用 data-part）
  const prevBtn = document.querySelector(`.car-btn[data-part="${part}"][data-dir="-1"]`);
  const nextBtn = document.querySelector(`.car-btn[data-part="${part}"][data-dir="1"]`);

  // acc 空的：顯示 None + 禁用按鈕
  if (!list.length){
    const empty = document.createElement("div");
    empty.className = "empty-item";
    empty.textContent = "None";
    trackEl.appendChild(empty);
    if (prevBtn) prevBtn.disabled = true;
    if (nextBtn) nextBtn.disabled = true;
    return;
  }

  if (prevBtn) prevBtn.disabled = false;
  if (nextBtn) nextBtn.disabled = false;

  // ✅ 只顯示目前選到的那一個（橫向長方形按鈕感）
  const idx = wrapIndex(state.pick[part], list.length);
  state.pick[part] = idx;

  const item = list[idx];

  const btn = document.createElement("button");
  btn.className = "thumb thumb-wide is-active";
  const img = document.createElement("img");
  img.src = item.src;
  img.alt = `${part}-${idx+1}`;
  btn.appendChild(img);

  // 點「中間這張」也可以當作下一張（可選）
  btn.addEventListener("click", () => {
    state.pick[part] = wrapIndex(state.pick[part] + 1, list.length);
    setLayerSources();
    renderAllThumbs();
  });

  trackEl.appendChild(btn);
}


function renderAllThumbs() {
  renderThumbs("head");
  renderThumbs("body");
  renderThumbs("legs");
  renderThumbs("acc");
}

// =====================================================
// Tuning UI (微調定位點)
// =====================================================
function syncTuningUI(){
  const t = state.tuningTarget;
  const o = state.offsets[t];

  const r = RANGE[t] || RANGE.body;

  // ✅ 依部位更新 range
  setRange(offsetX, r.x[0], r.x[1], 1);
  setRange(offsetY, r.y[0], r.y[1], 1);
  setRange(offsetS, r.s[0], r.s[1], 0.01);

  // ✅ 套值
  offsetX.value = String(o.x);
  offsetY.value = String(o.y);
  offsetS.value = String(o.s);

  offsetXVal.textContent = String(o.x);
  offsetYVal.textContent = String(o.y);
  offsetSVal.textContent = String(o.s);

  document.querySelectorAll(".chip").forEach(ch => {
    ch.classList.toggle("is-active", ch.dataset.target === t);
  });
}


function onOffsetChange(){
  const t = state.tuningTarget;

  state.offsets[t].x = Number(offsetX.value);
  state.offsets[t].y = Number(offsetY.value);
  state.offsets[t].s = Number(offsetS.value);

  offsetXVal.textContent = offsetX.value;
  offsetYVal.textContent = offsetY.value;
  offsetSVal.textContent = Number(offsetS.value).toFixed(2);

  // ✅ 拖拉直接更新
  setLayerSources();
}


if (offsetX) offsetX.addEventListener("input", onOffsetChange);
if (offsetY) offsetY.addEventListener("input", onOffsetChange);
if (offsetS) offsetS.addEventListener("input", onOffsetChange);

document.querySelectorAll(".chip").forEach((ch) => {
  ch.addEventListener("click", () => {
    state.tuningTarget = ch.dataset.target;
    syncTuningUI();
  });
});

// =====================================================
// Preset per gender + pose
// =====================================================
function presetKey() {
  return `preset_${state.gender}_${state.pose}`;
}

const btnSave = $("#btnSave");
const btnLoad = $("#btnLoad");

if (btnSave) {
  btnSave.addEventListener("click", () => {
    const payload = {
      pick: state.pick,
      offsets: normalizeOffsets(state.offsets), // ✅ 永遠存齊
      headBehind: state.headBehind
    };
    localStorage.setItem(presetKey(), JSON.stringify(payload));
    toast("Preset saved");
  });
}

if (btnLoad) {
  btnLoad.addEventListener("click", () => {
    const raw = localStorage.getItem(presetKey());
    if (!raw) return toast("No preset");

    try {
      const data = JSON.parse(raw);

      state.pick = data.pick || state.pick;
      // ✅ 不要直接覆蓋 offsets（會把 s 變 undefined）
      state.offsets = normalizeOffsets(data.offsets || {});
      state.headBehind =
        typeof data.headBehind === "boolean" ? data.headBehind : state.headBehind;

      renderAllThumbs();
      setLayerSources();
      syncTuningUI();
      toast("Preset loaded");
    } catch {
      toast("Preset broken");
    }
  });
}

// =====================================================
// UI events: gender / pose / headBehind / reset / carousel
// =====================================================
document.addEventListener("click", (e) => {
  const seg = e.target.closest(".seg");
  if (!seg) return;

  const g = seg.dataset.gender;
  if (!g) return;

  // UI active
  document.querySelectorAll(".seg").forEach(b => b.classList.remove("is-active"));
  seg.classList.add("is-active");

  setGender(g);
});


document.querySelectorAll(".pose").forEach((btn) => {
  btn.addEventListener("click", () => {
    document.querySelectorAll(".pose").forEach((b) => b.classList.remove("is-active"));
    btn.classList.add("is-active");

    state.pose = btn.dataset.pose;

    state.pick = { head: 0, body: 0, legs: 0, acc: 0 };
    state.car = { head: 0, body: 0, legs: 0, acc: 0 };

    // ✅ 切 pose 一定套用 pose override tuning
    applyTuningFromManifest();

    renderAllThumbs();
    renderCharacterAnimated();
    syncTuningUI();
  });
});

const btnHeadBehind = $("#btnHeadBehind");
if (btnHeadBehind) {
  btnHeadBehind.addEventListener("click", () => {
    state.headBehind = !state.headBehind;
    setLayerSources();
  });
}

const btnReset = $("#btnReset");
if (btnReset) {
  btnReset.addEventListener("click", () => {
    state.pick = { head: 0, body: 0, legs: 0, acc: 0 };
    state.car = { head: 0, body: 0, legs: 0, acc: 0 };

    // ✅ reset 後回到 manifest tuning（不是硬寫一份）
    applyTuningFromManifest();

    renderAllThumbs();
    setLayerSources();
    syncTuningUI();
    toast("Reset");
  });
}

document.querySelectorAll(".car-btn").forEach(btn => {
  btn.addEventListener("click", () => {
    const part = btn.dataset.part;
    const dir = Number(btn.dataset.dir);

    const list = currentList(part);
    const len = list.length;

    // acc 可能是空的：直接忽略
    if (!len) return;

    state.pick[part] = wrapIndex(state.pick[part] + dir, len);

    // 你要「瞬間切換」：不要動畫、不要閃
    setLayerSources();
    renderAllThumbs(); // 更新右側顯示（active / preview）
    syncTuningUI();    // 若你希望切換 part 時顯示正確值
  });
});


// =====================================================
// Export: canvas 合成
// =====================================================
const BASE_Y = 620;
const btnExport = $("#btnExport");
if (btnExport) {
  $("#btnExport").addEventListener("click", async () => {
  const canvas = document.createElement("canvas");
  canvas.width = 1024;
  canvas.height = 1024;
  const ctx = canvas.getContext("2d");
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 以目前畫面上的 character 當「匯出參考框」
  const charRect = character.getBoundingClientRect();

  // 讓匯出有邊界，不要貼滿（避免你某些 pose 超出就被裁）
  const MARGIN = 48; // 你想更鬆就加大
  const maxW = canvas.width - MARGIN * 2;
  const maxH = canvas.height - MARGIN * 2;

  // 依照目前 #character 在畫面上的大小，決定縮放倍率
  const scale = Math.min(maxW / charRect.width, maxH / charRect.height);

  // 把 #character 放在 1024x1024 的正中間
  const originX = (canvas.width - charRect.width * scale) / 2;
  const originY = (canvas.height - charRect.height * scale) / 2;

  // ✅ 強制頭永遠在最上層（匯出層級）
  const order = ["legs", "body", "acc", "head"];
  const map = { head: layerHead, body: layerBody, legs: layerLegs, acc: layerAcc };

  // 確保圖片都載入完成再畫（避免 naturalWidth=0）
  const ensureLoaded = (img) =>
    new Promise((res) => {
      if (!img || !img.src) return res(false);
      if (img.complete && img.naturalWidth > 0) return res(true);
      img.onload = () => res(true);
      img.onerror = () => res(false);
    });

  for (const part of order) {
    const img = map[part];

    // acc 可能 display none / 沒 src
    if (!img || img.style.display === "none" || !img.src) continue;

    const ok = await ensureLoaded(img);
    if (!ok) continue;

    // 讀取「畫面上這張圖實際的位置與大小」
    const r = img.getBoundingClientRect();

    // 轉成相對於 character 的座標，再等比例映射到 canvas
    const x = originX + (r.left - charRect.left) * scale;
    const y = originY + (r.top - charRect.top) * scale;
    const w = r.width * scale;
    const h = r.height * scale;

    ctx.drawImage(img, x, y, w, h);
  }

  const url = canvas.toDataURL("image/png");
  const a = document.createElement("a");
  a.href = url;
  a.download = `character_${state.gender}_${state.pose}.png`;
  a.click();
});


}

// =====================================================
// Init
// =====================================================
(async function init() {
  try {
    await loadManifest();

    // ✅ init 一定先套 tuning（不然你改 JSON 會覺得沒用）
    applyTuningFromManifest();

    renderAllThumbs();
    setLayerSources();
    syncTuningUI();
  } catch (err) {
    console.error(err);
    toast("Init failed: check manifest/assets paths");
  }
})();
