// Full blob-tracking visualization — covers every effect from blob-tracker/:
// filled rects, wireframes (cross/outline/bracket), connection lines (straight/curved/dotted),
// text labels (id/coordinates/both), dot indicators, trails with scale+blur feedback,
// grid overlay, metrics, per-layer colors.
const { useEffect, useRef } = React;

const DEFAULT_LAYERS = {
  wireframes: { enabled: true, style: 'cross', color: '#ffffff', opacity: 0.85, lineWidth: 1, bracketLength: 0.2 },
  filled:     { enabled: false, color: '#ffffff', opacity: 0.15 },
  lines:      { enabled: true, color: '#ffffff', opacity: 0.45, lineWidth: 1, maxDistance: 0.32, minDistance: 0.04, maxPer: 3, curved: false, dotted: false },
  labels:     { enabled: true, color: '#ffffff', opacity: 0.8, fontSize: 11, displayMode: 'both', precision: 2 },
  dots:       { enabled: false, color: '#ff3b30', radius: 3, offsetX: 0, offsetY: 0 },
  trails:     { enabled: false, persistence: 0.97, feedbackScale: 1.005, feedbackBlur: 2 },
  grid:       { enabled: false, color: 'rgba(255,255,255,0.15)', spacing: 50, lineWidth: 0.5 },
  metrics:    { enabled: false, position: 'top-left' },
};

function Demo({
  width = 1200, height = 720, blobCount = 14,
  backdrop = 'video', speed = 1, seed = 1, highlightId = null,
  accent = '#ffffff',
  layers = {},
  backgroundImage = null,
  backgroundVideo = null,
  style = {}, className = '',
}) {
  const canvasRef = useRef(null);
  const trailRef = useRef(null);
  const videoBgRef = useRef(null);
  const blobsRef = useRef([]);
  const rafRef = useRef(0);
  const startRef = useRef(performance.now());

  // Merge layers with defaults; if accent provided and a layer has no explicit color, use accent
  const L = {};
  for (const k in DEFAULT_LAYERS) {
    L[k] = { ...DEFAULT_LAYERS[k], ...(layers[k] || {}) };
    if ('color' in L[k] && !(layers[k] && layers[k].color)) {
      if (k !== 'grid' && k !== 'dots') L[k].color = accent;
    }
  }

  useEffect(() => {
    let s = seed * 9999;
    const rand = () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };
    blobsRef.current = Array.from({ length: blobCount }, (_, i) => ({
      id: i + 101,
      w: 0.04 + rand() * 0.08, h: 0.04 + rand() * 0.08,
      ax: 0.15 + rand() * 0.2, ay: 0.12 + rand() * 0.2,
      fx: 0.08 + rand() * 0.12, fy: 0.09 + rand() * 0.12,
      phx: rand() * Math.PI * 2, phy: rand() * Math.PI * 2,
      cx: 0.2 + rand() * 0.6, cy: 0.2 + rand() * 0.6,
      wobble: rand(),
    }));
  }, [blobCount, seed]);

  useEffect(() => {
    const canvas = canvasRef.current; if (!canvas) return;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    canvas.width = width * dpr; canvas.height = height * dpr;
    const ctx = canvas.getContext('2d');
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);

    const trail = document.createElement('canvas');
    trail.width = width; trail.height = height;
    trailRef.current = trail;
    const tctx = trail.getContext('2d');

    const vbg = document.createElement('canvas');
    vbg.width = Math.floor(width / 2); vbg.height = Math.floor(height / 2);
    videoBgRef.current = vbg;

    let fps = 60, lastT = performance.now(), frames = 0, fpsT = lastT;

    const loop = () => {
      const now = performance.now();
      frames++;
      if (now - fpsT >= 500) { fps = Math.round(frames / ((now - fpsT) / 1000)); frames = 0; fpsT = now; }
      const t = ((now - startRef.current) / 1000) * speed;

      drawVideoBg(vbg, t);
      const blobs = blobsRef.current;
      for (const b of blobs) {
        b.u = Math.max(0.05, Math.min(0.95, b.cx + Math.cos(t * b.fx + b.phx) * b.ax * 0.8));
        b.v = Math.max(0.05, Math.min(0.95, b.cy + Math.sin(t * b.fy + b.phy) * b.ay * 0.8));
        b.curW = b.w * (0.85 + 0.15 * Math.sin(t * 0.8 + b.wobble * 6));
        b.curH = b.h * (0.85 + 0.15 * Math.cos(t * 0.7 + b.wobble * 6));
      }

      // ===== TRAILS (feedback with scale + blur) =====
      if (L.trails.enabled) {
        // decay
        tctx.globalCompositeOperation = 'source-over';
        tctx.fillStyle = `rgba(0,0,0,${1 - L.trails.persistence})`;
        tctx.fillRect(0, 0, width, height);
        // scale feedback
        if (L.trails.feedbackScale !== 1.0) {
          tctx.save();
          const s = L.trails.feedbackScale;
          tctx.translate(width / 2, height / 2);
          tctx.scale(s, s);
          tctx.translate(-width / 2, -height / 2);
          if (L.trails.feedbackBlur > 0) tctx.filter = `blur(${L.trails.feedbackBlur}px)`;
          tctx.globalAlpha = 0.85;
          tctx.drawImage(trail, 0, 0);
          tctx.restore();
          tctx.filter = 'none';
          tctx.globalAlpha = 1;
        }
        // draw fresh blob strokes into trail
        tctx.strokeStyle = L.wireframes.color;
        tctx.lineWidth = L.wireframes.lineWidth;
        tctx.globalAlpha = 0.9;
        for (const b of blobs) {
          const x = b.u * width, y = b.v * height;
          const w = b.curW * width, h = b.curH * height;
          tctx.strokeRect(x - w / 2, y - h / 2, w, h);
        }
        tctx.globalAlpha = 1;
      } else {
        tctx.clearRect(0, 0, width, height);
      }

      // ===== BACKDROP =====
      ctx.clearRect(0, 0, width, height);
      if (backdrop === 'dark') {
        ctx.fillStyle = '#0a0a0a';
        ctx.fillRect(0, 0, width, height);
      } else if (backdrop === 'grid-bg') {
        ctx.fillStyle = '#0a0a0a';
        ctx.fillRect(0, 0, width, height);
      } else if (backgroundVideo && backgroundVideo.readyState >= 2 && backgroundVideo.videoWidth) {
        // Draw live video (webcam / screen / file-video) with "cover" fit
        const iw = backgroundVideo.videoWidth, ih = backgroundVideo.videoHeight;
        const cr = width / height, ir = iw / ih;
        let sx = 0, sy = 0, sw = iw, sh = ih;
        if (ir > cr) { sw = ih * cr; sx = (iw - sw) / 2; }
        else { sh = iw / cr; sy = (ih - sh) / 2; }
        ctx.fillStyle = '#0a0a0a';
        ctx.fillRect(0, 0, width, height);
        ctx.drawImage(backgroundVideo, sx, sy, sw, sh, 0, 0, width, height);
        ctx.fillStyle = 'rgba(0,0,0,0.25)';
        ctx.fillRect(0, 0, width, height);
      } else if (backgroundImage && backgroundImage.complete && backgroundImage.naturalWidth) {
        // Draw user-provided image with "cover" fit
        const iw = backgroundImage.naturalWidth, ih = backgroundImage.naturalHeight;
        const cr = width / height, ir = iw / ih;
        let sx = 0, sy = 0, sw = iw, sh = ih;
        if (ir > cr) { sw = ih * cr; sx = (iw - sw) / 2; }
        else { sh = iw / cr; sy = (ih - sh) / 2; }
        ctx.fillStyle = '#0a0a0a';
        ctx.fillRect(0, 0, width, height);
        ctx.drawImage(backgroundImage, sx, sy, sw, sh, 0, 0, width, height);
        // Slight darken so overlays remain legible
        ctx.fillStyle = 'rgba(0,0,0,0.25)';
        ctx.fillRect(0, 0, width, height);
      } else {
        ctx.drawImage(vbg, 0, 0, width, height);
      }

      // Grid overlay (on top of backdrop, below layers)
      if (L.grid.enabled) {
        ctx.strokeStyle = L.grid.color;
        ctx.lineWidth = L.grid.lineWidth;
        for (let x = 0; x < width; x += L.grid.spacing) {
          ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke();
        }
        for (let y = 0; y < height; y += L.grid.spacing) {
          ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke();
        }
      }

      // Trail composite
      if (L.trails.enabled) {
        ctx.globalCompositeOperation = 'lighter';
        ctx.drawImage(trail, 0, 0);
        ctx.globalCompositeOperation = 'source-over';
      }

      // ===== FILLED RECTANGLES =====
      if (L.filled.enabled) {
        ctx.fillStyle = L.filled.color;
        ctx.globalAlpha = L.filled.opacity;
        for (const b of blobs) {
          const x = b.u * width, y = b.v * height;
          const w = b.curW * width, h = b.curH * height;
          ctx.fillRect(x - w / 2, y - h / 2, w, h);
        }
        ctx.globalAlpha = 1;
      }

      // ===== WIREFRAMES =====
      if (L.wireframes.enabled) {
        ctx.strokeStyle = L.wireframes.color;
        ctx.lineWidth = L.wireframes.lineWidth;
        ctx.globalAlpha = L.wireframes.opacity;
        for (const b of blobs) {
          const x = b.u * width, y = b.v * height;
          const w = b.curW * width, h = b.curH * height;
          const rx = x - w / 2, ry = y - h / 2;
          if (L.wireframes.style === 'bracket') {
            const bl = L.wireframes.bracketLength;
            const aw = w * bl, ah = h * bl;
            ctx.beginPath();
            ctx.moveTo(rx, ry + ah); ctx.lineTo(rx, ry); ctx.lineTo(rx + aw, ry);
            ctx.moveTo(rx + w - aw, ry); ctx.lineTo(rx + w, ry); ctx.lineTo(rx + w, ry + ah);
            ctx.moveTo(rx, ry + h - ah); ctx.lineTo(rx, ry + h); ctx.lineTo(rx + aw, ry + h);
            ctx.moveTo(rx + w - aw, ry + h); ctx.lineTo(rx + w, ry + h); ctx.lineTo(rx + w, ry + h - ah);
            ctx.stroke();
          } else if (L.wireframes.style === 'outline') {
            ctx.strokeRect(rx, ry, w, h);
          } else {
            ctx.strokeRect(rx, ry, w, h);
            ctx.beginPath();
            ctx.moveTo(x, ry); ctx.lineTo(x, ry + h);
            ctx.moveTo(rx, y); ctx.lineTo(rx + w, y);
            ctx.stroke();
          }
          if (highlightId != null && b.id === highlightId) {
            ctx.save();
            ctx.globalAlpha = 1; ctx.lineWidth = 2;
            ctx.strokeRect(rx - 6, ry - 6, w + 12, h + 12);
            ctx.restore();
          }
        }
        ctx.globalAlpha = 1;
      }

      // ===== CONNECTION LINES =====
      if (L.lines.enabled) {
        ctx.strokeStyle = L.lines.color;
        ctx.lineWidth = L.lines.lineWidth;
        ctx.globalAlpha = L.lines.opacity;
        ctx.lineCap = 'round';
        if (L.lines.dotted) ctx.setLineDash([3, 6]);
        const counts = new Map();
        for (let i = 0; i < blobs.length; i++) {
          for (let j = i + 1; j < blobs.length; j++) {
            const a = blobs[i], c = blobs[j];
            const d = Math.hypot(a.u - c.u, a.v - c.v);
            if (d < L.lines.minDistance || d > L.lines.maxDistance) continue;
            const ci = counts.get(a.id) || 0, cj = counts.get(c.id) || 0;
            if (ci >= L.lines.maxPer || cj >= L.lines.maxPer) continue;
            counts.set(a.id, ci + 1); counts.set(c.id, cj + 1);
            const x1 = a.u * width, y1 = a.v * height;
            const x2 = c.u * width, y2 = c.v * height;
            ctx.beginPath();
            if (L.lines.curved) {
              const mx = (x1 + x2) / 2, my = (y1 + y2) / 2;
              const dx = x2 - x1, dy = y2 - y1;
              ctx.moveTo(x1, y1);
              ctx.quadraticCurveTo(mx + -dy * 0.2, my + dx * 0.2, x2, y2);
            } else {
              ctx.moveTo(x1, y1); ctx.lineTo(x2, y2);
            }
            ctx.stroke();
          }
        }
        ctx.setLineDash([]);
        ctx.globalAlpha = 1;
      }

      // ===== DOT INDICATORS =====
      if (L.dots.enabled) {
        ctx.fillStyle = L.dots.color;
        for (const b of blobs) {
          const x = b.u * width + L.dots.offsetX;
          const y = b.v * height + L.dots.offsetY;
          ctx.beginPath();
          ctx.arc(x, y, L.dots.radius, 0, Math.PI * 2);
          ctx.fill();
        }
      }

      // ===== TEXT LABELS =====
      if (L.labels.enabled) {
        ctx.fillStyle = L.labels.color;
        ctx.globalAlpha = L.labels.opacity;
        ctx.font = `${L.labels.fontSize}px "JetBrains Mono", "SF Mono", monospace`;
        ctx.textBaseline = 'middle';
        for (const b of blobs) {
          const x = b.u * width, y = b.v * height;
          const w = b.curW * width, h = b.curH * height;
          const tx = x + w / 2 + 8, ty = y - h / 2 - 4;
          let txt = '';
          if (L.labels.displayMode === 'id') txt = String(b.id);
          else if (L.labels.displayMode === 'coordinates') txt = `${b.u.toFixed(L.labels.precision)}, ${b.v.toFixed(L.labels.precision)}`;
          else txt = `${b.id}  ${b.u.toFixed(L.labels.precision)}, ${b.v.toFixed(L.labels.precision)}`;
          ctx.fillText(txt, tx, ty);
        }
        ctx.globalAlpha = 1;
      }

      // ===== METRICS =====
      if (L.metrics.enabled) {
        const pos = L.metrics.position || 'top-left';
        const pad = 14;
        const lines = [`blobs  ${blobs.length}`, `fps    ${fps}`, `res    ${width}x${height}`];
        ctx.font = '11px "JetBrains Mono", monospace';
        const bw = 160, bh = lines.length * 16 + 14;
        const bx = pos.includes('right') ? width - bw - pad : pad;
        const by = pos.includes('bottom') ? height - bh - pad : pad;
        ctx.fillStyle = 'rgba(0,0,0,0.5)';
        ctx.fillRect(bx, by, bw, bh);
        ctx.strokeStyle = 'rgba(255,255,255,0.2)';
        ctx.strokeRect(bx + 0.5, by + 0.5, bw - 1, bh - 1);
        ctx.fillStyle = 'rgba(255,255,255,0.9)';
        ctx.textBaseline = 'top';
        for (let i = 0; i < lines.length; i++) ctx.fillText(lines[i], bx + 10, by + 10 + i * 16);
      }

      rafRef.current = requestAnimationFrame(loop);
    };
    rafRef.current = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(rafRef.current);
  }, [width, height, backdrop, speed, highlightId, accent, backgroundImage, backgroundVideo, blobCount, JSON.stringify(L)]);

  return <canvas ref={canvasRef} className={className} style={{ width: '100%', height: '100%', display: 'block', ...style }} />;
}

function drawVideoBg(canvas, t) {
  const ctx = canvas.getContext('2d');
  const w = canvas.width, h = canvas.height;
  ctx.fillStyle = '#0a0a0a'; ctx.fillRect(0, 0, w, h);
  const blobs = [
    { x: 0.3 + Math.sin(t * 0.2) * 0.15, y: 0.4 + Math.cos(t * 0.15) * 0.2, r: 0.45, c: '#2a2a2a' },
    { x: 0.7 + Math.cos(t * 0.18) * 0.2,  y: 0.6 + Math.sin(t * 0.22) * 0.15, r: 0.5, c: '#1e1e1e' },
    { x: 0.5 + Math.sin(t * 0.1) * 0.3,   y: 0.3 + Math.cos(t * 0.12) * 0.25, r: 0.35, c: '#242424' },
  ];
  for (const b of blobs) {
    const cx = b.x * w, cy = b.y * h, cr = b.r * Math.max(w, h);
    const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, cr);
    g.addColorStop(0, b.c); g.addColorStop(1, 'rgba(10,10,10,0)');
    ctx.fillStyle = g; ctx.fillRect(0, 0, w, h);
  }
  ctx.globalAlpha = 0.04; ctx.fillStyle = '#fff';
  for (let y = 0; y < h; y += 3) ctx.fillRect(0, y, w, 1);
  ctx.globalAlpha = 1;
}

// Helper: build a `layers` object from a simple preset name (used by Landing/Onboarding).
Demo.presets = {
  classic: {
    wireframes: { enabled: true, style: 'cross' },
    filled: { enabled: false }, lines: { enabled: true }, labels: { enabled: true },
    trails: { enabled: false }, dots: { enabled: false }, grid: { enabled: false },
  },
  inked: {
    wireframes: { enabled: true, style: 'cross' },
    trails: { enabled: true, persistence: 0.96, feedbackScale: 1.008, feedbackBlur: 1 },
    lines: { enabled: false }, labels: { enabled: false },
    filled: { enabled: false }, dots: { enabled: false }, grid: { enabled: false },
  },
  woven: {
    lines: { enabled: true, curved: true, maxPer: 4, maxDistance: 0.4 },
    wireframes: { enabled: false }, labels: { enabled: false }, trails: { enabled: false },
    filled: { enabled: false }, dots: { enabled: true, radius: 2, color: '#ffffff' }, grid: { enabled: false },
  },
  ghost: {
    labels: { enabled: true, displayMode: 'both' },
    wireframes: { enabled: false }, lines: { enabled: false }, trails: { enabled: false },
    filled: { enabled: false }, dots: { enabled: true, radius: 2, color: '#ffffff' }, grid: { enabled: false },
  },
  full: {
    wireframes: { enabled: true, style: 'cross' }, filled: { enabled: true, opacity: 0.1 },
    lines: { enabled: true }, labels: { enabled: true },
    trails: { enabled: true }, dots: { enabled: true, color: '#ff3b30' },
    grid: { enabled: true }, metrics: { enabled: true },
  },
};

window.Demo = Demo;
