/* Pond — Waikanae River, looking across the water toward the far bank.
 * Stones skip TOWARD the camera (across the river in the depth direction),
 * not laterally. Each successive skip grows visibly larger and the ripple
 * gets bigger as the stone approaches the near bank — cheap perspective
 * trick on a 320×140 pixel grid.
 *
 * Click sets the lateral throw line. Hold to charge — release for power.
 * Power maps to skip count + total z-distance travelled before sinking.
 * Nothing launches on its own.
 */

function Pond({ onSkip, fps = 22 }) {
  const canvasRef = React.useRef(null);
  const wrapRef = React.useRef(null);

  // Internal pixel resolution
  const W = 320, H = 140;
  const WATER_TOP    = 84;      // far bank water line (the "z=0" depth)
  const WATER_BOTTOM = H - 6;   // near bank — stone leaves the canvas past here

  // Colours (CC-29 v2)
  const COL = {
    bone:    "#F2F0E5",
    boneDim: "#E6E2D1",
    mint:    "#A2DCC7",
    mintDim: "#7AC2A9",
    pink:    "#EDC8C4",
    navy:    "#45444F",
    plum:    "#352B42",
    plumLight:"#4A3D5C",
    pineDark: "#2A2235",
  };

  const stones      = React.useRef([]);
  const ripples     = React.useRef([]);
  const stars       = React.useRef([]);
  const fish        = React.useRef([]);
  const trees       = React.useRef([]);
  const tRef        = React.useRef(0);

  // Charge state
  const chargingRef     = React.useRef(false);
  const chargeStartRef  = React.useRef(0);
  const chargeOriginRef = React.useRef({ x: W / 2 });
  const [chargeFrac, setChargeFrac] = React.useState(0);

  const CHARGE_MAX_MS = 1500;

  // one-time scene props
  React.useEffect(() => {
    const s = [];
    for (let i = 0; i < 18; i++) {
      s.push({
        x: Math.floor(Math.random() * W),
        y: Math.floor(Math.random() * 50) + 4,
        twinkle: Math.random() * Math.PI * 2,
        color: Math.random() < 0.18 ? COL.pink : COL.bone,
      });
    }
    stars.current = s;

    const t = [];
    let x = 2;
    while (x < W - 4) {
      const h = 14 + Math.floor(Math.random() * 18);
      const w = Math.max(6, Math.floor(h * 0.55));
      t.push({ x, baseY: WATER_TOP - 1, h, w });
      x += Math.max(3, Math.floor(w * 0.55 + Math.random() * 4 - 1));
    }
    trees.current = t;

    const f = [];
    for (let i = 0; i < 2; i++) {
      f.push({
        x: Math.random() * W,
        depth: 0.4 + Math.random() * 0.5,
        vx: 0.28 + Math.random() * 0.18,
        wig: Math.random() * Math.PI * 2,
      });
    }
    fish.current = f;
  }, []);

  // Helpers — map normalised depth z (0..1, far→near) to canvas Y
  const zToY = (z) => Math.floor(WATER_TOP + z * (WATER_BOTTOM - WATER_TOP));
  // Stone visual size grows with depth (perspective)
  const zToSize = (z) => 1 + Math.round(z * 2); // 1..3
  // Ripple radius scales with depth (closer = larger)
  const zToRipple = (z) => 4 + Math.round(z * 10);

  // Launch a stone from the far bank, travelling toward the camera.
  const launchStone = React.useCallback((opts) => {
    const { lateralX, power } = opts;
    // power 0..1 → skipsLeft 2..8, plus total z reach
    const skipsLeft = 2 + Math.floor(power * 6);
    // z-distance covered per skip diminishes; first skip is biggest.
    const firstSkipZ = 0.14 + power * 0.10;     // ~0.14–0.24 of pond depth
    // arc seconds per skip — small for snappy feel
    const firstArcMs = 380 - Math.round(power * 100); // 380–280ms

    const colors = [COL.bone, COL.mint, COL.pink];
    stones.current.push({
      x: lateralX + (Math.random() * 6 - 3), // slight lateral wobble
      z: 0,                                   // far bank
      arcT: 0,                                // 0..1 within current arc
      arcStartZ: 0,
      arcEndZ: firstSkipZ,
      arcMs: firstArcMs,
      arcStartTime: performance.now(),
      arcHeight: 14,                          // visual height above water in px
      skipsLeft,
      skipsDone: 0,
      color: opts.color || colors[Math.floor(Math.random() * colors.length)],
      lateralDrift: (Math.random() - 0.5) * 0.4, // tiny side-drift per arc
    });
  }, []);

  // main draw loop
  React.useEffect(() => {
    const cv = canvasRef.current;
    if (!cv) return;
    cv.width = W; cv.height = H;
    const ctx = cv.getContext("2d");
    ctx.imageSmoothingEnabled = false;

    let raf;
    let last = 0;
    const frameInterval = 1000 / fps;

    const draw = (now) => {
      raf = requestAnimationFrame(draw);
      if (now - last < frameInterval) return;
      last = now;
      tRef.current += 1;
      const t = tRef.current;

      // BG: sky
      ctx.fillStyle = COL.plum;
      ctx.fillRect(0, 0, W, WATER_TOP);
      // horizon glow just above water
      ctx.fillStyle = COL.plumLight;
      ctx.fillRect(0, WATER_TOP - 4, W, 4);

      // stars
      for (const s of stars.current) {
        const blink = Math.sin(t * 0.06 + s.twinkle);
        if (blink > -0.3) {
          ctx.fillStyle = s.color;
          ctx.fillRect(s.x, s.y, 1, 1);
        }
      }

      // moon
      const moonX = 270, moonY = 22;
      ctx.fillStyle = COL.bone;
      ctx.fillRect(moonX + 2, moonY, 8, 1);
      ctx.fillRect(moonX, moonY + 1, 12, 2);
      ctx.fillRect(moonX, moonY + 3, 12, 4);
      ctx.fillRect(moonX, moonY + 7, 12, 2);
      ctx.fillRect(moonX + 2, moonY + 9, 8, 1);
      ctx.fillStyle = COL.boneDim;
      ctx.fillRect(moonX + 7, moonY + 1, 5, 8);

      // pine treeline along the far bank
      for (const tr of trees.current) {
        drawPine(ctx, tr.x, tr.baseY, tr.h, tr.w, COL.pineDark);
      }

      // far bank line
      ctx.fillStyle = COL.pineDark;
      ctx.fillRect(0, WATER_TOP, W, 1);

      // water
      ctx.fillStyle = COL.navy;
      ctx.fillRect(0, WATER_TOP + 1, W, H - WATER_TOP - 1);
      // deep band
      ctx.fillStyle = COL.plum;
      ctx.fillRect(0, 120, W, H - 120);

      // river current — horizontal dashes, mint near the far bank, pink up
      // close (subtle hint of foreground reflection)
      ctx.fillStyle = COL.mint;
      for (let i = 0; i < 18; i++) {
        const gx = Math.floor((i * 31 + t * 0.45) % W);
        const gy = 90 + ((i * 5) % 26);
        const blink = (t + i * 7) % 20;
        if (blink < 10) ctx.fillRect(gx, gy, 3, 1);
      }
      ctx.fillStyle = COL.pink;
      for (let i = 0; i < 5; i++) {
        const gx = Math.floor((i * 53 + t * 0.32 + 30) % W);
        const gy = 110 + ((i * 13) % 16);
        const blink = (t + i * 9) % 32;
        if (blink < 4) ctx.fillRect(gx, gy, 1, 1);
      }

      // fish with perspective — they sit at a depth, drift left→right
      for (const fi of fish.current) {
        fi.x += fi.vx;
        fi.wig += 0.04;
        if (fi.x > W + 6) { fi.x = -6; fi.depth = 0.4 + Math.random() * 0.5; }
        const wy = zToY(fi.depth) + Math.sin(fi.wig) * 1.2;
        ctx.fillStyle = COL.plumLight;
        ctx.fillRect(Math.floor(fi.x), Math.floor(wy), 2, 1);
        ctx.fillRect(Math.floor(fi.x - 2), Math.floor(wy - 1), 1, 1);
        ctx.fillRect(Math.floor(fi.x - 2), Math.floor(wy + 1), 1, 1);
      }

      // ripples
      ripples.current = ripples.current.filter(r => r.life < r.max);
      for (const r of ripples.current) {
        r.life += 1;
        const rad = (r.life / r.max) * r.peakRad;
        const alpha = 1 - r.life / r.max;
        ctx.fillStyle = alpha > 0.5 ? COL.mint : COL.mintDim;
        const steps = 22;
        for (let i = 0; i < steps; i++) {
          const ang = (i / steps) * Math.PI * 2;
          const ex = Math.floor(r.x + Math.cos(ang) * rad);
          // squish vertically for perspective ellipse
          const ey = Math.floor(r.y + Math.sin(ang) * rad * 0.38);
          if (ey > WATER_TOP && ey < H - 4) ctx.fillRect(ex, ey, 1, 1);
        }
      }

      // stones — perspective skipping toward camera
      stones.current = stones.current.filter(s => !s.dead);
      for (const s of stones.current) {
        const elapsed = now - s.arcStartTime;
        s.arcT = Math.min(1, elapsed / s.arcMs);
        // current z position interpolates from arcStart → arcEnd within this skip
        s.z = s.arcStartZ + (s.arcEndZ - s.arcStartZ) * s.arcT;

        // visual y is water line at this z minus arc height (parabola)
        const waterY = zToY(s.z);
        const arcLift = Math.sin(s.arcT * Math.PI) * s.arcHeight;
        const drawY = waterY - arcLift;

        // tiny lateral drift per arc
        s.x += s.lateralDrift;

        // landed? trigger ripple + advance skip
        if (s.arcT >= 1) {
          ripples.current.push({
            x: Math.floor(s.x),
            y: waterY,
            life: 0,
            max: 18 + Math.round(s.z * 14),
            peakRad: zToRipple(s.z),
          });
          if (onSkip) onSkip();

          s.skipsLeft -= 1;
          s.skipsDone += 1;
          if (s.skipsLeft <= 0 || s.z >= 0.96) {
            // final sink — bigger ripple + die
            ripples.current.push({
              x: Math.floor(s.x),
              y: waterY,
              life: 0,
              max: 28 + Math.round(s.z * 16),
              peakRad: zToRipple(s.z) * 1.4,
            });
            s.dead = true;
            continue;
          }
          // queue next skip: shorter z-distance, shorter arc, lower lift
          const remaining = 1 - s.z;
          const shrink = 0.78;
          const nextZSpan = Math.min(remaining * 0.7,
                                     (s.arcEndZ - s.arcStartZ) * shrink);
          s.arcStartZ = s.z;
          s.arcEndZ   = Math.min(1, s.z + nextZSpan);
          s.arcMs     = Math.max(180, s.arcMs * shrink);
          s.arcHeight = Math.max(3, s.arcHeight * 0.7);
          s.arcStartTime = now;
          s.arcT = 0;
          continue;
        }

        // draw stone — size scales with depth
        const size = zToSize(s.z);
        const sx = Math.floor(s.x);
        const sy = Math.floor(drawY);
        ctx.fillStyle = s.color;
        if (size === 1) {
          ctx.fillRect(sx, sy, 2, 1);
        } else if (size === 2) {
          ctx.fillRect(sx - 1, sy, 4, 1);
          ctx.fillRect(sx, sy - 1, 2, 1);
        } else {
          // size 3 — closer to camera, fatter pebble
          ctx.fillRect(sx - 2, sy,     5, 2);
          ctx.fillRect(sx - 1, sy - 1, 3, 1);
        }
        // soft shadow on water at the stone's would-be landing point
        ctx.fillStyle = COL.plum;
        ctx.fillRect(sx, waterY + 1, Math.max(1, size - 1), 1);
      }
    };

    raf = requestAnimationFrame(draw);
    return () => cancelAnimationFrame(raf);
  }, [fps, onSkip]);

  // Drive the charge UI fraction while held
  React.useEffect(() => {
    if (!chargingRef.current) return undefined;
    let raf;
    const tick = () => {
      if (!chargingRef.current) return;
      const elapsed = Math.min(performance.now() - chargeStartRef.current, CHARGE_MAX_MS);
      setChargeFrac(elapsed / CHARGE_MAX_MS);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  });

  const toCanvas = (clientX) => {
    const rect = wrapRef.current.getBoundingClientRect();
    return { x: ((clientX - rect.left) / rect.width) * W };
  };

  const handleDown = (e) => {
    if (e.button !== undefined && e.button !== 0) return;
    e.preventDefault();
    const p = toCanvas(e.clientX);
    chargeOriginRef.current = { x: p.x };
    chargeStartRef.current = performance.now();
    chargingRef.current = true;
    setChargeFrac(0);
    if (wrapRef.current && wrapRef.current.setPointerCapture && e.pointerId !== undefined) {
      try { wrapRef.current.setPointerCapture(e.pointerId); } catch {}
    }
  };

  const releaseCharge = () => {
    if (!chargingRef.current) return;
    const elapsed = Math.min(performance.now() - chargeStartRef.current, CHARGE_MAX_MS);
    const power = elapsed / CHARGE_MAX_MS;
    launchStone({ lateralX: chargeOriginRef.current.x, power });
    chargingRef.current = false;
    setChargeFrac(0);
  };

  const handleUp     = () => releaseCharge();
  const handleCancel = () => releaseCharge();

  return (
    <div ref={wrapRef}
         className="pond-frame"
         onPointerDown={handleDown}
         onPointerUp={handleUp}
         onPointerCancel={handleCancel}
         style={{ cursor: "crosshair", touchAction: "none" }}
         role="button"
         aria-label="hold to charge a stone throw">
      <canvas ref={canvasRef}
              style={{
                width: "100%",
                height: "100%",
                display: "block",
                imageRendering: "pixelated",
              }}/>

      {chargeFrac > 0 && (
        <div style={{
          position: "absolute",
          left: "50%",
          bottom: 26,
          transform: "translateX(-50%)",
          width: 160,
          height: 10,
          border: "1.5px solid var(--ss-bone)",
          background: "rgba(53, 43, 66, 0.7)",
          padding: 1,
          pointerEvents: "none",
        }}>
          <div style={{
            width: `${Math.round(chargeFrac * 100)}%`,
            height: "100%",
            background: chargeFrac > 0.85 ? "var(--ss-pink)" : "var(--ss-mint)",
            transition: "background 80ms ease",
          }}/>
        </div>
      )}

      <div className="corner-mark" style={{ top: 8, left: 10 }}>waikanae river · stones.exe</div>
      <div className="corner-mark" style={{ bottom: 8, left: 10 }}>
        click and hold to throw across
      </div>
      <div className="corner-mark" style={{ bottom: 8, right: 10 }}>
        24fps · 320×140
      </div>
    </div>
  );
}

function drawPine(ctx, x, baseY, h, w, color) {
  ctx.fillStyle = color;
  const trunkH = Math.max(2, Math.floor(h * 0.15));
  const trunkX = x + Math.floor(w / 2) - 1;
  ctx.fillRect(trunkX, baseY - trunkH, 2, trunkH);
  const crownH = h - trunkH;
  for (let y = 0; y < crownH; y++) {
    const t = (crownH - y) / crownH;
    const halfW = Math.ceil(t * (w / 2));
    ctx.fillRect(x + Math.floor(w / 2) - halfW, baseY - trunkH - y, halfW * 2 + 1, 1);
  }
}

window.Pond = Pond;
