export function silhouetteFromCanvas(
  draw: (ctx: CanvasRenderingContext2D, w: number, h: number) => void,
  w: number,
  h: number,
): boolean[][] {
  const canvas = document.createElement("canvas");
  canvas.width = w;
  canvas.height = h;
  const ctx = canvas.getContext("2d");
  if (!ctx) return Array.from({ length: h }, () => Array.from({ length: w }, () => false));
  ctx.fillStyle = "#ffffff";
  ctx.fillRect(0, 0, w, h);
  draw(ctx, w, h);
  const data = ctx.getImageData(0, 0, w, h).data;
  const grid: boolean[][] = [];
  for (let y = 0; y < h; y++) {
    const row: boolean[] = [];
    for (let x = 0; x < w; x++) {
      const i = (y * w + x) * 4;
      row.push(data[i] < 140);
    }
    grid.push(row);
  }
  return dilate(grid);
}

function dilate(grid: boolean[][]): boolean[][] {
  const h = grid.length;
  const w = grid[0]?.length ?? 0;
  const out = grid.map((row) => row.slice());
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      if (!grid[y][x]) continue;
      for (let dy = -1; dy <= 1; dy++) {
        for (let dx = -1; dx <= 1; dx++) {
          const ny = y + dy;
          const nx = x + dx;
          if (ny >= 0 && ny < h && nx >= 0 && nx < w) out[ny][nx] = true;
        }
      }
    }
  }
  return out;
}

export function strataWord(w = 80, h = 22): boolean[][] {
  return silhouetteFromCanvas((ctx, width, height) => {
    ctx.fillStyle = "#000000";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.font = `900 ${Math.floor(height * 0.82)}px "Times New Roman", "Georgia", serif`;
    ctx.fillText("STRATA", width / 2, height / 2 + 1);
  }, w, h);
}

export function watcherFace(w = 36, h = 44): boolean[][] {
  return silhouetteFromCanvas((ctx, width, height) => {
    ctx.fillStyle = "#000000";
    ctx.beginPath();
    ctx.ellipse(width * 0.5, height * 0.4, width * 0.28, height * 0.3, 0, 0, Math.PI * 2);
    ctx.fill();
    ctx.fillRect(width * 0.42, height * 0.66, width * 0.16, height * 0.14);
    ctx.beginPath();
    ctx.ellipse(width * 0.5, height * 0.9, width * 0.4, height * 0.12, 0, 0, Math.PI * 2);
    ctx.fill();
    ctx.fillStyle = "#ffffff";
    ctx.beginPath();
    ctx.ellipse(width * 0.4, height * 0.36, width * 0.055, height * 0.035, 0, 0, Math.PI * 2);
    ctx.ellipse(width * 0.6, height * 0.36, width * 0.055, height * 0.035, 0, 0, Math.PI * 2);
    ctx.fill();
  }, w, h);
}

export function ternBird(w = 48, h = 28): boolean[][] {
  return silhouetteFromCanvas((ctx, width, height) => {
    ctx.fillStyle = "#000000";
    ctx.beginPath();
    ctx.ellipse(width * 0.48, height * 0.58, width * 0.28, height * 0.18, -0.2, 0, Math.PI * 2);
    ctx.fill();
    ctx.beginPath();
    ctx.ellipse(width * 0.7, height * 0.48, width * 0.12, height * 0.12, 0, 0, Math.PI * 2);
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo(width * 0.8, height * 0.48);
    ctx.lineTo(width * 0.96, height * 0.44);
    ctx.lineTo(width * 0.8, height * 0.54);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo(width * 0.2, height * 0.62);
    ctx.quadraticCurveTo(width * 0.05, height * 0.2, width * 0.55, height * 0.42);
    ctx.quadraticCurveTo(width * 0.3, height * 0.5, width * 0.22, height * 0.64);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo(width * 0.22, height * 0.6);
    ctx.lineTo(width * 0.02, height * 0.72);
    ctx.lineTo(width * 0.28, height * 0.66);
    ctx.closePath();
    ctx.fill();
  }, w, h);
}

export type AnamorphicRock = {
  position: [number, number, number];
  scale: [number, number, number];
};

export function rocksFromSilhouette(
  grid: boolean[][],
  viewpoint: [number, number, number],
  target: [number, number, number],
  planeW: number,
  planeH: number,
  depth: number,
  seed: number,
  stride = 1,
): AnamorphicRock[] {
  const h = grid.length;
  const w = grid[0]?.length ?? 0;
  if (!w || !h) return [];

  const [vx, vy, vz] = viewpoint;
  const [tx, ty, tz] = target;
  const fx = tx - vx;
  const fy = ty - vy;
  const fz = tz - vz;
  const fl = Math.hypot(fx, fy, fz) || 1;
  const fxx = fx / fl;
  const fyy = fy / fl;
  const fzz = fz / fl;
  let rx = fzz;
  let ry = 0;
  let rz = -fxx;
  const rl = Math.hypot(rx, ry, rz) || 1;
  rx /= rl;
  ry /= rl;
  rz /= rl;
  const ux = ry * fzz - rz * fyy;
  const uy = rz * fxx - rx * fzz;
  const uz = rx * fyy - ry * fxx;

  const rocks: AnamorphicRock[] = [];
  let n = seed;
  const rand = () => {
    n = (n * 16807) % 2147483647;
    return (n & 0xffff) / 0xffff;
  };

  for (let y = 0; y < h; y += stride) {
    for (let x = 0; x < w; x += stride) {
      if (!grid[y][x]) continue;
      const u = (x / (w - 1) - 0.5) * planeW;
      const v = (0.5 - y / (h - 1)) * planeH;
      const px = tx + rx * u + ux * v;
      const py = ty + ry * u + uy * v;
      const pz = tz + rz * u + uz * v;
      const dx = px - vx;
      const dy = py - vy;
      const dz = pz - vz;
      const dl = Math.hypot(dx, dy, dz) || 1;
      const nx = dx / dl;
      const ny = dy / dl;
      const nz = dz / dl;
      const t = dl + (rand() - 0.5) * depth;
      const s = 0.14 + rand() * 0.12 + t * 0.006;
      rocks.push({
        position: [vx + nx * t, Math.max(0.12, vy + ny * t), vz + nz * t],
        scale: [s, s * (0.85 + rand() * 0.45), s],
      });
    }
  }
  return rocks;
}
