// ============================================================
// One Roof Labs — point-cloud / scan-line hero canvas
// A city seen from 400ft: perturbed grid of LiDAR returns + a
// scan line that sweeps across, lighting up points AND drawing
// in roof outlines (varied shapes) as it passes over them.
// ============================================================

// Roof footprint shapes in unit space (x:0..1, y:0..~0.8). Each is a
// list of polylines [ [ [x,y], ... ], ... ] — outer footprint + ridge/hip lines.
const ROOF_SHAPES = [
  // gable: rectangle + center ridge
  [[[0,0],[1,0],[1,0.66],[0,0.66],[0,0]], [[0,0.33],[1,0.33]]],
  // hip: rectangle + short ridge + 4 hips to corners
  [[[0,0],[1,0],[1,0.7],[0,0.7],[0,0]],
   [[0.3,0.35],[0.7,0.35]],
   [[0,0],[0.3,0.35]], [[1,0],[0.7,0.35]], [[0,0.7],[0.3,0.35]], [[1,0.7],[0.7,0.35]]],
  // pyramid hip: square + crossing diagonals
  [[[0,0],[1,0],[1,1],[0,1],[0,0]], [[0,0],[1,1]], [[1,0],[0,1]]],
  // L-shape with ridges
  [[[0,0],[0.58,0],[0.58,0.5],[1,0.5],[1,1],[0,1],[0,0]],
   [[0.29,0],[0.29,1]], [[0.58,0.75],[1,0.75]]],
  // cross / cross-gable
  [[[0.34,0],[0.66,0],[0.66,0.34],[1,0.34],[1,0.66],[0.66,0.66],[0.66,1],
    [0.34,1],[0.34,0.66],[0,0.66],[0,0.34],[0.34,0.34],[0.34,0]],
   [[0.5,0],[0.5,1]], [[0,0.5],[1,0.5]]],
  // T-shape
  [[[0,0],[1,0],[1,0.4],[0.66,0.4],[0.66,1],[0.34,1],[0.34,0.4],[0,0.4],[0,0]],
   [[0,0.2],[1,0.2]], [[0.5,0.4],[0.5,1]]],
  // long rectangle, offset ridge (carport/garage feel)
  [[[0,0],[1,0],[1,0.42],[0,0.42],[0,0]], [[0,0.21],[1,0.21]], [[0.7,0],[0.7,0.42]]],
];

function PointCloudCanvas({ density = 1, style = {} }) {
  const canvasRef = React.useRef(null);

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    let raf, w, h, dpr, points = [], houses = [], scanX = 0, running = true;
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    function rot(pt, deg) {
      const r = deg * Math.PI / 180, c = Math.cos(r), s = Math.sin(r);
      return [pt[0] * c - pt[1] * s, pt[0] * s + pt[1] * c];
    }

    function buildHouses() {
      houses = [];
      // place roof outlines mostly on the right (clear of the headline)
      const n = Math.max(5, Math.round(8 * Math.min(1.4, w / 1100)));
      const cells = [];
      const cols = 3, rows = Math.max(3, Math.round(n / 1.5));
      for (let i = 0; i < n; i++) {
        const s = 64 + Math.random() * 78;
        const cx = w * (0.5 + Math.random() * 0.42);
        const cy = h * (0.12 + Math.random() * 0.74);
        const shape = ROOF_SHAPES[Math.floor(Math.random() * ROOF_SHAPES.length)];
        const deg = [0, 90, 180, 270][Math.floor(Math.random() * 4)];
        // bake absolute paths
        const paths = shape.map((path) => path.map(([ux, uy]) => {
          const [rx, ry] = rot([ux - 0.5, uy - 0.4], deg);
          return [cx + rx * s, cy + ry * s];
        }));
        // skip if it strays into the far-left text column
        const minX = Math.min(...paths.flat().map((p) => p[0]));
        if (minX < w * 0.46) { i--; continue; }
        houses.push({ cx, paths, lit: 0, drawn: 0 });
      }
    }

    function build() {
      const rect = canvas.parentElement.getBoundingClientRect();
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = rect.width; h = rect.height;
      canvas.width = w * dpr; canvas.height = h * dpr;
      canvas.style.width = w + 'px'; canvas.style.height = h + 'px';
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);

      points = [];
      const cell = 26 / density;
      const cols = Math.ceil(w / cell) + 2;
      const rows = Math.ceil(h / cell) + 2;
      for (let i = 0; i < cols; i++) {
        for (let j = 0; j < rows; j++) {
          const street = (i % 6 === 0) || (j % 6 === 0);
          if (street && Math.random() > 0.12) continue;
          if (Math.random() > 0.82) continue;
          const jitter = cell * 0.34;
          const x = i * cell - cell + (Math.random() - 0.5) * jitter;
          const y = j * cell - cell + (Math.random() - 0.5) * jitter;
          points.push({
            x, y,
            base: 0.05 + Math.random() * 0.14,
            r: Math.random() > 0.93 ? 1.6 : 0.9,
            lit: 0,
            phase: Math.random() * Math.PI * 2,
            flag: Math.random() > 0.985,
          });
        }
      }
      buildHouses();
      scanX = -w * 0.2;
    }

    let t0 = performance.now();
    function frame(now) {
      if (!running) return;
      const dt = Math.min(40, now - t0); t0 = now;
      ctx.clearRect(0, 0, w, h);

      if (!reduce) scanX += (w + 240) * (dt / 9000);
      if (scanX > w + 120) scanX = -120;
      const scanWidth = 150;

      // points
      for (const p of points) {
        const d = Math.abs(p.x - scanX);
        if (d < scanWidth) p.lit = Math.max(p.lit, 1 - d / scanWidth);
        p.lit *= 0.962;
        const tw = reduce ? 0 : Math.sin(now * 0.0011 + p.phase) * 0.03;
        let alpha = p.base + tw + p.lit * 0.7;
        let col = '241,230,207';
        if (p.flag) { alpha = p.base + tw + p.lit * 0.85 + 0.12; col = '224,120,90'; }
        alpha = Math.max(0, Math.min(0.95, alpha));
        ctx.beginPath();
        ctx.fillStyle = `rgba(${col},${alpha})`;
        ctx.arc(p.x, p.y, p.r + p.lit * 1.1, 0, Math.PI * 2);
        ctx.fill();
      }

      // roof outlines — light up as the scan passes, then linger faintly
      ctx.lineJoin = 'round';
      ctx.lineCap = 'round';
      for (const ho of houses) {
        const d = Math.abs(ho.cx - scanX);
        if (d < scanWidth + 30) ho.lit = Math.max(ho.lit, 1 - d / (scanWidth + 30));
        ho.lit *= 0.975;
        ho.drawn = Math.max(ho.drawn, ho.lit > 0.05 ? 1 : ho.drawn); // remembers it was seen
        if (reduce) ho.lit = 0.5;
        const ghost = 0.05 * ho.drawn;
        const a = Math.max(0, Math.min(0.85, ghost + ho.lit * 0.75));
        if (a < 0.015) continue;
        ctx.strokeStyle = `rgba(241,230,207,${a})`;
        ctx.lineWidth = 1 + ho.lit * 0.5;
        for (const path of ho.paths) {
          ctx.beginPath();
          path.forEach((pt, k) => k === 0 ? ctx.moveTo(pt[0], pt[1]) : ctx.lineTo(pt[0], pt[1]));
          ctx.stroke();
        }
        // tiny corner node when freshly scanned
        if (ho.lit > 0.4) {
          const c = ho.paths[0][0];
          ctx.fillStyle = `rgba(241,230,207,${ho.lit * 0.8})`;
          ctx.fillRect(c[0] - 1.5, c[1] - 1.5, 3, 3);
        }
      }

      // scan line
      if (!reduce) {
        const g = ctx.createLinearGradient(scanX - 60, 0, scanX + 4, 0);
        g.addColorStop(0, 'rgba(241,230,207,0)');
        g.addColorStop(1, 'rgba(241,230,207,0.22)');
        ctx.fillStyle = g;
        ctx.fillRect(scanX - 60, 0, 64, h);
        ctx.fillStyle = 'rgba(241,230,207,0.5)';
        ctx.fillRect(scanX, 0, 1, h);
      }
      raf = requestAnimationFrame(frame);
    }

    build();
    raf = requestAnimationFrame(frame);
    const onResize = () => build();
    window.addEventListener('resize', onResize);
    return () => { running = false; cancelAnimationFrame(raf); window.removeEventListener('resize', onResize); };
  }, [density]);

  return <canvas ref={canvasRef} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', ...style }} />;
}

Object.assign(window, { PointCloudCanvas });
