/* global React, window */
// ============================================================
// PIXEL TUXEDO CAT — voxel-style 3D pixel art, drawn to <canvas>
// (depth layer + offset top layer = extruded "3D pixel" volume).
// Canvas keeps the DOM light (1 node vs ~300 rects).
// ============================================================

const PX_TOP = { K: "#22221f", W: "#f4f0e7", P: "#e6929d", G: "#8ab35f", H: "#ffffff", D: "#0c0c0d" };
const PX_DEPTH = { K: "#050505", W: "#c7c0b1", P: "#ad6671", G: "#557e3a", H: "#cfcabd", D: "#000" };

function gridFront(eyesClosed) {
  const W = 16, H = 18;
  const g = Array.from({ length: H }, () => Array(W).fill("."));
  const R = (x0, y0, x1, y1, c) => { for (let y = y0; y <= y1; y++) for (let x = x0; x <= x1; x++) if (g[y] && g[y][x] !== undefined) g[y][x] = c; };
  const S = (x, y, c) => { if (g[y] && x >= 0 && x < W) g[y][x] = c; };
  S(4, 0, "K"); S(11, 0, "K");
  R(3, 1, 5, 1, "K"); R(10, 1, 12, 1, "K");
  R(3, 1, 12, 8, "K");
  S(4, 1, "P"); S(11, 1, "P");
  R(5, 6, 10, 8, "W");
  if (eyesClosed) { R(5, 5, 6, 5, "D"); R(9, 5, 10, 5, "D"); }
  else { R(5, 4, 6, 5, "G"); R(9, 4, 10, 5, "G"); S(5, 4, "H"); S(9, 4, "H"); }
  R(7, 7, 8, 7, "P");
  R(4, 9, 11, 9, "K"); R(3, 10, 12, 16, "K");
  R(6, 9, 9, 9, "W"); R(6, 10, 9, 13, "W"); R(7, 14, 8, 14, "W");
  R(4, 16, 6, 17, "W"); R(9, 16, 11, 17, "W");
  R(12, 11, 14, 12, "K"); R(13, 12, 14, 16, "K");
  return { g, W, H };
}

function gridSide(frame) {
  const W = 22, H = 13;
  const g = Array.from({ length: H }, () => Array(W).fill("."));
  const R = (x0, y0, x1, y1, c) => { for (let y = y0; y <= y1; y++) for (let x = x0; x <= x1; x++) if (g[y] && g[y][x] !== undefined) g[y][x] = c; };
  const S = (x, y, c) => { if (g[y] && x >= 0 && x < W) g[y][x] = c; };
  R(0, 2, 1, 6, "K"); S(0, 1, "K");                 // tail
  R(2, 3, 15, 8, "K");                               // body
  R(14, 1, 20, 7, "K");                              // head
  S(15, 0, "K"); R(17, 0, 19, 0, "K");               // ears
  R(15, 5, 19, 7, "W"); R(4, 7, 14, 8, "W");         // chest + belly
  R(18, 5, 20, 7, "W");                              // muzzle
  R(17, 3, 18, 4, "G"); S(17, 3, "H");               // eye
  S(20, 5, "P");                                     // nose
  // legs — two frames for a trot
  if (frame === 0) { R(4, 9, 5, 11, "K"); R(12, 9, 13, 12, "K"); R(7, 9, 8, 12, "K"); R(15, 9, 16, 11, "K"); }
  else { R(4, 9, 5, 12, "K"); R(12, 9, 13, 11, "K"); R(7, 9, 8, 11, "K"); R(15, 9, 16, 12, "K"); }
  // white socks
  R(4, 11, 5, 11, "W"); R(7, 11, 8, 11, "W"); R(12, 11, 13, 11, "W"); R(15, 11, 16, 11, "W");
  return { g, W, H };
}

function paintGrid(ctx, data, scale, withShadow, palTop, palDepth) {
  palTop = palTop || PX_TOP; palDepth = palDepth || PX_DEPTH;
  const off = Math.max(1, Math.round(scale * 0.22));
  ctx.imageSmoothingEnabled = false;
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  if (withShadow) {
    ctx.save();
    ctx.fillStyle = "rgba(20,12,5,.26)";
    ctx.beginPath();
    ctx.ellipse((data.W / 2) * scale, (data.H + 1.1) * scale, (data.W / 2.3) * scale, 1.1 * scale, 0, 0, Math.PI * 2);
    ctx.fill();
    ctx.restore();
  }
  const draw = (pal, dx, dy) => {
    for (let y = 0; y < data.H; y++) for (let x = 0; x < data.W; x++) {
      const c = data.g[y][x];
      if (c === ".") continue;
      ctx.fillStyle = pal[c] || "#000";
      ctx.fillRect(Math.round(x * scale + dx), Math.round(y * scale + dy), scale, scale);
    }
  };
  draw(palDepth, off, off);  // depth (bottom-right)
  draw(palTop, 0, 0);        // top
}

function PixelCanvas({ data, size, scale, pad = 1, shadowPad = 2, palTop, palDepth }) {
  const ref = React.useRef(null);
  const dpr = (window.devicePixelRatio || 1);
  const cw = (data.W + pad) * scale;
  const ch = (data.H + pad + shadowPad) * scale;
  React.useEffect(() => {
    const cv = ref.current; if (!cv) return;
    const ctx = cv.getContext("2d");
    cv.width = cw * dpr; cv.height = ch * dpr;
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    paintGrid(ctx, data, scale, true, palTop, palDepth);
  });
  return <canvas ref={ref} style={{ width: cw, height: ch, imageRendering: "pixelated", display: "block" }} />;
}

function PixelTuxedoCat({ size = 150, variant = "sit", eyesClosed, blink = true, style = {} }) {
  const closedDefault = variant === "nap";
  const [closed, setClosed] = React.useState(closedDefault);
  React.useEffect(() => {
    if (!blink || closedDefault) return;
    let t; const loop = () => { t = setTimeout(() => { setClosed(true); setTimeout(() => setClosed(false), 150); loop(); }, 2400 + Math.random() * 3600); };
    loop();
    return () => clearTimeout(t);
  }, [blink, closedDefault]);
  const shut = eyesClosed != null ? eyesClosed : (closedDefault || closed);
  const data = gridFront(shut);
  const scale = Math.max(4, Math.round(size / data.W));
  return (
    <div style={{ display: "inline-block", position: "relative", ...style }}>
      <PixelCanvas data={data} size={size} scale={scale} />
      {variant === "nap" && (
        <span style={{ position: "absolute", top: -2, right: -6, fontFamily: "var(--mono)", fontSize: 14, color: "#3a2414", opacity: .6 }}>z<small style={{ fontSize: 9 }}>z</small></span>
      )}
    </div>
  );
}

function PixelTuxedoSide({ size = 150, walking = true, style = {} }) {
  const [frame, setFrame] = React.useState(0);
  const data = gridSide(walking ? frame : 0);
  const scale = Math.max(3, Math.round(size / data.W));
  return (
    <div style={{ display: "inline-block", ...style }}>
      <PixelCanvas data={data} size={size} scale={scale} shadowPad={3} />
      {walking && <WalkFramer onTick={() => setFrame((f) => (f ? 0 : 1))} />}
    </div>
  );
}

// drives the 2-frame walk cycle (paused when not walking via CSS class on ancestor)
function WalkFramer({ onTick }) {
  React.useEffect(() => {
    let id;
    const tick = () => { onTick(); };
    id = setInterval(tick, 180);
    return () => clearInterval(id);
  }, []);
  return null;
}

// brown-curly-hair girl (cover companion)
const GIRL_TOP = { H: "#6e4a2e", h: "#8c5f3a", S: "#ebb78f", E: "#2a1a10", M: "#b85f4d", T: "#C4603D", W: "#f4f0e7" };
const GIRL_DEPTH = { H: "#49301a", h: "#5e3f25", S: "#c9966f", E: "#000", M: "#8a4536", T: "#9a4528", W: "#cfc9bd" };
function gridGirl() {
  const W = 14, H = 18;
  const g = Array.from({ length: H }, () => Array(W).fill("."));
  const R = (x0, y0, x1, y1, c) => { for (let y = y0; y <= y1; y++) for (let x = x0; x <= x1; x++) if (g[y] && g[y][x] !== undefined) g[y][x] = c; };
  const S = (x, y, c) => { if (g[y] && x >= 0 && x < W) g[y][x] = c; };
  // curly hair mass
  R(2, 1, 11, 9, "H");
  // curl bumps on top
  S(3, 0, "H"); S(5, 0, "H"); S(8, 0, "H"); S(10, 0, "H"); S(2, 0, "h"); S(11, 0, "h");
  // side curls
  R(2, 9, 3, 12, "H"); R(10, 9, 11, 12, "H"); S(2, 12, "h"); S(11, 12, "h");
  // face
  R(4, 3, 9, 9, "S");
  // fringe
  R(4, 3, 9, 3, "H"); S(4, 4, "H"); S(9, 4, "H");
  // eyes + mouth
  S(5, 5, "E"); S(8, 5, "E"); R(6, 7, 7, 7, "M");
  // neck
  R(6, 10, 7, 10, "S");
  // shirt
  R(3, 11, 10, 17, "T"); R(5, 11, 8, 11, "W");
  // arms
  R(2, 12, 2, 16, "T"); R(11, 12, 11, 16, "T");
  // hair highlights
  S(3, 2, "h"); S(4, 2, "h"); S(9, 6, "h");
  return { g, W, H };
}
function PixelGirl({ size = 150, style = {} }) {
  const data = gridGirl();
  const scale = Math.max(4, Math.round(size / data.W));
  return (
    <div style={{ display: "inline-block", ...style }}>
      <PixelCanvas data={data} size={size} scale={scale} palTop={GIRL_TOP} palDepth={GIRL_DEPTH} />
    </div>
  );
}

window.PixelTuxedoCat = PixelTuxedoCat;
window.PixelTuxedoSide = PixelTuxedoSide;
window.PixelGirl = PixelGirl;
