/**
 * LANE-RENDER — the break room. A GLYTCH arcade cabinet near the start zone.
 *
 * Clicking it opens `match.html` — the original match-the-glitch prototype, which deploy
 * copies next to the game (see deploy.sh). The two games finally meet. The cabinet is a
 * real object in the world with its own raycast (index.ts owns the pick + the cursor
 * affordance), not a UI button, because it should feel like a thing you find on the
 * floor, not a menu item.
 *
 * The marquee spells GLYTCH in glowing blocks — a placeholder until a MODELBEAST cabinet
 * lands under the `arcade-cabinet` asset key (the registry would hot-swap it, but the
 * break room isn't a MachineDef, so this is hand-built and self-contained).
 */
import * as THREE from 'three';

// 5x5 bitmap font for G L Y T C H — one column of glowing pixels per letter row.
const GLYPHS: Record<string, number[]> = {
  G: [0b01110, 0b10000, 0b10011, 0b10001, 0b01110],
  L: [0b10000, 0b10000, 0b10000, 0b10000, 0b11111],
  Y: [0b10001, 0b01010, 0b00100, 0b00100, 0b00100],
  T: [0b11111, 0b00100, 0b00100, 0b00100, 0b00100],
  C: [0b01110, 0b10001, 0b10000, 0b10001, 0b01110],
  H: [0b10001, 0b10001, 0b11111, 0b10001, 0b10001],
};

export class BreakRoom {
  readonly group = new THREE.Group();
  /** The subtree the raycast tests against — click anywhere on the cabinet counts. */
  readonly clickable: THREE.Object3D[] = [];
  private marqueeMat: THREE.MeshStandardMaterial;

  constructor(x: number, z: number) {
    this.group.name = 'breakroom';
    this.group.position.set(x, 0, z);
    // The iso rig looks from (+x,+y,+z), so a face whose normal is +z points 45° away
    // from the viewer and the marquee reads edge-on. Turn the whole cabinet to face the
    // camera — it's a prop, not a machine, so it doesn't owe the grid an orientation.
    this.group.rotation.y = Math.PI / 4;

    const body = new THREE.MeshStandardMaterial({ color: 0x1a1830, roughness: 0.7, metalness: 0.2 });
    // Wide enough to carry its own marquee: GLYTCH at a legible pixel size spans ~1.8
    // units, and a sign wider than its cabinet reads as a floating decal.
    const cabinet = new THREE.Mesh(new THREE.BoxGeometry(2.1, 2.6, 1.1), body);
    cabinet.position.y = 1.3;
    cabinet.castShadow = cabinet.receiveShadow = true;
    this.group.add(cabinet);
    this.clickable.push(cabinet);

    // Screen: a dark panel with a cyan glow, angled toward the player.
    const screen = new THREE.Mesh(
      new THREE.PlaneGeometry(1.5, 1.0),
      new THREE.MeshStandardMaterial({
        color: 0x0a1420, emissive: 0x1b6cff, emissiveIntensity: 0.8, roughness: 0.4,
      }),
    );
    screen.position.set(0, 1.45, 0.56);
    screen.rotation.x = -0.18;
    this.group.add(screen);
    this.clickable.push(screen);

    // Marquee: GLYTCH in emissive pixels across the top.
    this.marqueeMat = new THREE.MeshStandardMaterial({
      color: 0x3fd4ff, emissive: 0x3fd4ff, emissiveIntensity: 1.6,
    });
    const marquee = this.buildMarquee();
    marquee.position.set(0, 2.28, 0.57);
    this.group.add(marquee);

    // Control deck.
    const deck = new THREE.Mesh(new THREE.BoxGeometry(2.1, 0.15, 0.5), body);
    deck.position.set(0, 0.78, 0.55);
    deck.rotation.x = -0.3;
    this.group.add(deck);
    this.clickable.push(deck);
  }

  private buildMarquee(): THREE.Object3D {
    const g = new THREE.Group();
    const word = 'GLYTCH';
    // Sized so the word spans most of the cabinet's width — at the rig's closest zoom
    // (8) a 0.03 pixel was sub-pixel on screen and GLYTCH was an illegible smudge.
    const px = 0.05;
    const letterW = 5 * px + px; // 5 cols + gap
    const totalW = word.length * letterW;
    const pixel = new THREE.BoxGeometry(px * 0.9, px * 0.9, px * 0.6);
    word.split('').forEach((ch, li) => {
      const rows = GLYPHS[ch];
      if (!rows) return;
      rows.forEach((bits, ry) => {
        for (let cx = 0; cx < 5; cx++) {
          if (!(bits & (1 << (4 - cx)))) continue;
          const m = new THREE.Mesh(pixel, this.marqueeMat);
          m.position.set(
            -totalW / 2 + li * letterW + cx * px,
            (2 - ry) * px,
            0,
          );
          g.add(m);
        }
      });
    });
    return g;
  }

  /** Marquee attract-mode shimmer. */
  frame(timeSec: number): void {
    this.marqueeMat.emissiveIntensity = 1.3 + Math.sin(timeSec * 4) * 0.4;
  }
}
