/* ─────────────────────────────────────────────────────────────────────────────
   WivexxAccessories — Canvas2D renderer for the mascot items (control room).
   Reads window.MASCOT_ITEMS (ui/mascot_items.js), the single source of truth that
   the tk floating orb also draws from (via wivexx_mascot_items.py). Draw each item
   over the orb with WivexxAccessories.draw(ctx, name, cx, cy, R, t, lookX, lookY).
   ───────────────────────────────────────────────────────────────────────────── */
(function () {
  "use strict";
  const ITEMS = window.MASCOT_ITEMS || {};
  const PAL = ITEMS._palette || {};

  // resolve a color token ("volt", "volt@0.55", "#rrggbb", "#rrggbb@0.5") → css rgba
  function col(tok) {
    if (!tok || tok === "none") return null;
    let a = 1, hex = tok;
    const at = tok.indexOf("@");
    if (at >= 0) { a = parseFloat(tok.slice(at + 1)); hex = tok.slice(0, at); }
    hex = PAL[hex] || hex;
    if (hex[0] !== "#") hex = "#000000";
    const r = parseInt(hex.slice(1, 3), 16), g = parseInt(hex.slice(3, 5), 16), b = parseInt(hex.slice(5, 7), 16);
    return "rgba(" + r + "," + g + "," + b + "," + a + ")";
  }

  const TAU = Math.PI * 2;

  // per-prim animation → { alpha, dx, dy } offsets (in R units for dx/dy)
  function anim(an, t) {
    const o = { alpha: 1, dx: 0, dy: 0 };
    if (!an) return o;
    const dur = an.dur || 1, ph = (an.ph || 0);
    if (an.k === "blink") {
      const lo = (an.lo != null) ? an.lo : 0.3;
      o.alpha = lo + (1 - lo) * (0.5 + 0.5 * Math.sin(TAU * (t / dur)));
    } else if (an.k === "flash") {
      o.alpha = (Math.sin(TAU * (t / dur)) > (an.thr || 0)) ? 1 : 0;
    } else if (an.k === "bob") {
      const s = (an.amp || 0.05) * Math.sin(TAU * (t / dur + ph));
      if (an.ax === "x") o.dx = s; else o.dy = s;
    }
    return o;
  }

  function draw(ctx, name, cx, cy, R, t, lookX, lookY) {
    const item = ITEMS[name];
    if (!item) return;
    lookX = lookX || 0; lookY = lookY || 0;
    ctx.save();

    // group transform: rotate about a pivot and/or translate with the gaze
    const g = item.grp;
    if (g) {
      if (g.tr) {
        let tx = (g.tr.gx || 0) * lookX, ty = (g.tr.gy || 0) * lookY;
        const cl = g.tr.clamp;
        if (cl) { const m = Math.hypot(tx, ty); if (m > cl) { tx = tx * cl / m; ty = ty * cl / m; } }
        ctx.translate(tx * R, ty * R);
      }
      if (g.rot) {
        const ang = ((g.rot.deg || 0) * Math.sin(TAU * (t / (g.rot.dur || 1))) + (g.rot.gaze || 0) * lookX) * Math.PI / 180;
        const px = cx + (g.rot.piv[0]) * R, py = cy + (g.rot.piv[1]) * R;
        ctx.translate(px, py); ctx.rotate(ang); ctx.translate(-px, -py);
      }
    }

    const X = (x) => cx + x * R, Y = (y) => cy + y * R;
    for (const p of item.prims) {
      const a = anim(p.an, t);
      if (a.alpha <= 0.001) continue;
      ctx.globalAlpha = a.alpha;
      const ox = a.dx, oy = a.dy;
      const fill = col(p.f), stroke = col(p.s), lw = (p.sw || 0) * R;
      ctx.lineJoin = "round"; ctx.lineCap = (p.cap === "round") ? "round" : "butt";

      if (p.t === "poly" || p.t === "line") {
        ctx.beginPath();
        p.p.forEach((pt, i) => { const xx = X(pt[0] + ox), yy = Y(pt[1] + oy); i ? ctx.lineTo(xx, yy) : ctx.moveTo(xx, yy); });
        if (p.t === "poly") ctx.closePath();
        if (fill && p.t === "poly") { ctx.fillStyle = fill; ctx.fill(); }
        if (stroke) { ctx.strokeStyle = stroke; ctx.lineWidth = Math.max(0.6, lw); ctx.stroke(); }
      } else if (p.t === "circ") {
        ctx.beginPath(); ctx.arc(X(p.x + ox), Y(p.y + oy), p.r * R, 0, TAU);
        if (fill) { ctx.fillStyle = fill; ctx.fill(); }
        if (stroke) { ctx.strokeStyle = stroke; ctx.lineWidth = Math.max(0.6, lw); ctx.stroke(); }
      } else if (p.t === "ell") {
        ctx.beginPath(); ctx.ellipse(X(p.x + ox), Y(p.y + oy), p.rx * R, p.ry * R, 0, 0, TAU);
        if (fill) { ctx.fillStyle = fill; ctx.fill(); }
        if (stroke) { ctx.strokeStyle = stroke; ctx.lineWidth = Math.max(0.6, lw); ctx.stroke(); }
      } else if (p.t === "arc") {
        ctx.beginPath(); ctx.ellipse(X(p.x + ox), Y(p.y + oy), p.rx * R, p.ry * R, 0, p.a0 * Math.PI / 180, p.a1 * Math.PI / 180);
        if (stroke) { ctx.strokeStyle = stroke; ctx.lineWidth = Math.max(0.6, lw); ctx.stroke(); }
      } else if (p.t === "rect" || p.t === "rr") {
        const x0 = X(p.x + ox) - p.w * R / 2, y0 = Y(p.y + oy) - p.h * R / 2, w = p.w * R, h = p.h * R;
        const rad = (p.t === "rr") ? Math.min(p.rad * R, w / 2, h / 2) : 0;
        ctx.beginPath();
        if (rad > 0 && ctx.roundRect) ctx.roundRect(x0, y0, w, h, rad);
        else roundRectPath(ctx, x0, y0, w, h, rad);
        if (fill) { ctx.fillStyle = fill; ctx.fill(); }
        if (stroke) { ctx.strokeStyle = stroke; ctx.lineWidth = Math.max(0.6, lw); ctx.stroke(); }
      }
    }
    ctx.restore();
  }

  function roundRectPath(ctx, x, y, w, h, r) {
    if (r <= 0) { ctx.rect(x, y, w, h); return; }
    ctx.moveTo(x + r, y);
    ctx.arcTo(x + w, y, x + w, y + h, r);
    ctx.arcTo(x + w, y + h, x, y + h, r);
    ctx.arcTo(x, y + h, x, y, r);
    ctx.arcTo(x, y, x + w, y, r);
    ctx.closePath();
  }

  window.WivexxAccessories = { draw, items: ITEMS };
})();
