// svg-geometry.jsx
// Geometry SVG components for The Living Mark
// These components expect React to be available globally (React 18 via CDN).
// Requires Babel for JSX transformation.

// ---------------------------------------------------------------------------
// Design tokens (CSS custom properties defined on the host page)
// --void:     #08080d
// --parchment: #eae4da
// --gold:     #c9a84c
// --gold-glow: rgba(201, 168, 76, 0.25)
// --ember:    #8b4513
// --honey:    #d4a017
// --bone:     #f5f0e8
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Shared geometry constants
// ---------------------------------------------------------------------------

const FOL_R = 60; // circle radius — also the center-to-center distance

/**
 * Compute all 19 Flower of Life circle centers.
 *
 * The Flower of Life is a hexagonal packing where every adjacent pair of
 * circles is tangent. With radius r, centers are distance r apart.
 *
 *   Center (1):  (0, 0)
 *   Ring 1 (6):  distance r from center, every 60°
 *   Ring 2a (6): distance 2r from center, every 60° (same phase as ring 1)
 *   Ring 2b (6): distance r√3 from center, every 60° offset by 30°
 *
 * This gives the 19 circles of the traditional Flower of Life.
 */
function computeFoLCircles(r) {
  const circles = [{ cx: 0, cy: 0 }];

  // Ring 1: 6 circles at distance r
  for (let i = 0; i < 6; i++) {
    const angle = (i * 60 * Math.PI) / 180;
    circles.push({
      cx: r * Math.cos(angle),
      cy: r * Math.sin(angle),
    });
  }

  // Ring 2a: 6 circles at distance 2r (same angles as ring 1)
  for (let i = 0; i < 6; i++) {
    const angle = (i * 60 * Math.PI) / 180;
    circles.push({
      cx: 2 * r * Math.cos(angle),
      cy: 2 * r * Math.sin(angle),
    });
  }

  // Ring 2b: 6 circles at distance r√3, offset by 30°
  for (let i = 0; i < 6; i++) {
    const angle = ((i * 60 + 30) * Math.PI) / 180;
    const dist = r * Math.sqrt(3);
    circles.push({
      cx: dist * Math.cos(angle),
      cy: dist * Math.sin(angle),
    });
  }

  return circles; // length: 19
}

/**
 * Compute the SVG polygon points string for a regular hexagon
 * in flat-top orientation (first vertex at 30° → matches the Voronoi dual
 * of the hexagonal circle packing above).
 *
 * The Voronoi cell of a circle center in this packing is a regular hexagon
 * whose circumradius equals r / √3, which is the distance from the center
 * to the midpoint between two adjacent ring-1 circle centers.
 *
 * Flat-top means the top and bottom edges are horizontal (vertices at 30°,
 * 90°, 150°, 210°, 270°, 330°). This orientation tiles perfectly with the
 * hexagonal packing whose ring-1 angles start at 0°.
 */
function hexPoints(cx, cy, circumradius) {
  return Array.from({ length: 6 }, (_, i) => {
    const angle = ((i * 60 + 30) * Math.PI) / 180; // flat-top
    return `${(cx + circumradius * Math.cos(angle)).toFixed(3)},${(cy + circumradius * Math.sin(angle)).toFixed(3)}`;
  }).join(' ');
}

// Precompute the 19 circle positions once (shared across components).
const FOL_CIRCLES = computeFoLCircles(FOL_R);

// The 7 inner circles used in VoronoiMorph: center + ring 1.
const INNER_CIRCLES = FOL_CIRCLES.slice(0, 7);

// Voronoi hexagon circumradius for the inner packing.
// In a packing where adjacent centers are distance r apart,
// the Voronoi cells are regular hexagons with circumradius r / √3.
const HEX_CIRCUM = FOL_R / Math.sqrt(3);

// ---------------------------------------------------------------------------
// Reduced-motion detection hook
// ---------------------------------------------------------------------------

function usePrefersReducedMotion() {
  const [reduced, setReduced] = React.useState(() => {
    if (typeof window === 'undefined') return false;
    return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  });

  React.useEffect(() => {
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    const handler = (e) => setReduced(e.matches);
    mq.addEventListener('change', handler);
    return () => mq.removeEventListener('change', handler);
  }, []);

  return reduced;
}

// ---------------------------------------------------------------------------
// Component 1: FlowerOfLifeBg
// ---------------------------------------------------------------------------
/**
 * A scroll-linked Flower of Life that constructs circle by circle as the
 * viewer scrolls through a chamber.
 *
 * Props:
 *   progress  {number} 0–1  Scroll progress within the chamber.
 *   className {string}      Positioning class (e.g. "absolute inset-0").
 */
function FlowerOfLifeBg({ progress = 0, className = '' }) {
  const reduced = usePrefersReducedMotion();
  const circumference = 2 * Math.PI * FOL_R;

  // How many circles are fully drawn at this progress level.
  // The Nth circle (0-indexed) starts drawing when progress passes N/19
  // and is fully drawn when progress passes (N+1)/19.
  const totalCircles = FOL_CIRCLES.length; // 19

  // Unique id for this instance (avoids SVG id collisions when used multiple times).
  const uid = React.useId ? React.useId() : 'fol';

  const circles = FOL_CIRCLES.map((pos, idx) => {
    let dashOffset;

    if (reduced) {
      // Reduced motion: show all circles fully drawn.
      dashOffset = 0;
    } else {
      // How far into this particular circle's draw phase are we?
      // Each circle occupies 1/totalCircles of the total progress range.
      const circleStart = idx / totalCircles;
      const circleEnd = (idx + 1) / totalCircles;

      if (progress >= circleEnd) {
        // Fully drawn.
        dashOffset = 0;
      } else if (progress <= circleStart) {
        // Not yet started.
        dashOffset = circumference;
      } else {
        // Partially drawn — interpolate within this circle's range.
        const localT = (progress - circleStart) / (1 / totalCircles);
        dashOffset = circumference * (1 - localT);
      }
    }

    return { ...pos, dashOffset };
  });

  // viewBox: the outermost ring-2b circles have centers at r√3 ≈ 103.9px
  // from the origin, so adding r (60px) for the stroke gives ≈ 164px radius.
  // We pad to 180 for breathing room.
  const vbSize = 180;
  const vbStr = `${-vbSize} ${-vbSize} ${vbSize * 2} ${vbSize * 2}`;

  return (
    <svg
      className={className}
      viewBox={vbStr}
      width="100%"
      height="100%"
      aria-hidden="true"
      style={{ display: 'block' }}
    >
      <style>{`
        .fol-circle {
          fill: none;
          stroke: #c9a84c;
          stroke-opacity: 0.08;
          stroke-width: 1;
          stroke-dasharray: ${circumference.toFixed(3)};
        }
        ${reduced ? '' : `.fol-circle {
          transition: stroke-dashoffset 0.3s ease;
        }`}
      `}</style>
      {circles.map((c, idx) => (
        <circle
          key={idx}
          className="fol-circle"
          cx={c.cx.toFixed(3)}
          cy={c.cy.toFixed(3)}
          r={FOL_R}
          style={{ strokeDashoffset: c.dashOffset.toFixed(3) }}
        />
      ))}
    </svg>
  );
}

// ---------------------------------------------------------------------------
// Component 2: VoronoiMorph
// ---------------------------------------------------------------------------
/**
 * Crossfades between the 7 inner Flower of Life circles and their Voronoi
 * dual — a honeycomb of 7 hexagonal cells.
 *
 * At progress = 0: pure Flower of Life circles (opacity 1), hexagons hidden.
 * At progress = 1: circles hidden, pure honeycomb (opacity 1).
 *
 * Props:
 *   progress  {number} 0–1
 *   className {string}
 */
function VoronoiMorph({ progress = 0, className = '' }) {
  const reduced = usePrefersReducedMotion();

  // In reduced-motion mode we jump directly to the hexagon state.
  const circleOpacity = reduced ? 0 : Math.max(0, 1 - progress);
  const hexOpacity    = reduced ? 1 : Math.min(1, progress);

  // Vibration: tiny jitter on each hex vertex when fully visible.
  // We animate each hexagon with a staggered translate.
  // Implementation: CSS keyframes via a <style> block; JS toggles a class.
  const vibrating = !reduced && progress > 0.8;

  // Each hexagon gets a unique stagger index for visual richness.
  const uid = React.useId ? React.useId() : 'vm';

  const vbSize = Math.ceil(FOL_R / Math.sqrt(3) + FOL_R * 2 + 10); // ≈ 165
  const vbStr = `${-vbSize} ${-vbSize} ${vbSize * 2} ${vbSize * 2}`;

  return (
    <svg
      className={className}
      viewBox={vbStr}
      width="100%"
      height="100%"
      aria-hidden="true"
      style={{ display: 'block' }}
    >
      <style>{`
        @keyframes hex-vibrate-0 { 0%,100%{transform:translate(0,0)} 25%{transform:translate(0.6px,-0.8px)} 75%{transform:translate(-0.7px,0.5px)} }
        @keyframes hex-vibrate-1 { 0%,100%{transform:translate(0,0)} 25%{transform:translate(-0.8px,0.6px)} 75%{transform:translate(0.5px,-0.7px)} }
        @keyframes hex-vibrate-2 { 0%,100%{transform:translate(0,0)} 25%{transform:translate(0.7px,0.7px)} 75%{transform:translate(-0.6px,-0.8px)} }
        @keyframes hex-vibrate-3 { 0%,100%{transform:translate(0,0)} 25%{transform:translate(-0.5px,-0.9px)} 75%{transform:translate(0.8px,0.4px)} }
        @keyframes hex-vibrate-4 { 0%,100%{transform:translate(0,0)} 25%{transform:translate(0.9px,-0.4px)} 75%{transform:translate(-0.4px,0.9px)} }
        @keyframes hex-vibrate-5 { 0%,100%{transform:translate(0,0)} 25%{transform:translate(-0.6px,0.8px)} 75%{transform:translate(0.9px,-0.5px)} }
        @keyframes hex-vibrate-6 { 0%,100%{transform:translate(0,0)} 25%{transform:translate(0.4px,0.9px)} 75%{transform:translate(-0.9px,-0.4px)} }

        .vm-circle {
          fill: none;
          stroke: #c9a84c;
          stroke-opacity: 0.15;
          stroke-width: 1;
          transition: opacity 0.4s ease;
        }
        .vm-hex {
          fill: rgba(201,168,76,0.03);
          stroke: #c9a84c;
          stroke-opacity: 0.25;
          stroke-width: 1;
          transition: opacity 0.4s ease;
        }
        ${INNER_CIRCLES.map((_, i) => `
          .vm-hex-${i}.vibrating {
            animation: hex-vibrate-${i} ${(0.45 + i * 0.03).toFixed(2)}s ease-in-out infinite alternate;
          }
        `).join('')}
      `}</style>

      {/* Group A: Flower of Life inner circles */}
      <g style={{ opacity: circleOpacity }}>
        {INNER_CIRCLES.map((c, idx) => (
          <circle
            key={idx}
            className="vm-circle"
            cx={c.cx.toFixed(3)}
            cy={c.cy.toFixed(3)}
            r={FOL_R}
          />
        ))}
      </g>

      {/* Group B: Voronoi hexagonal cells — true dual of the circle packing */}
      <g style={{ opacity: hexOpacity }}>
        {INNER_CIRCLES.map((c, idx) => (
          <polygon
            key={idx}
            className={`vm-hex vm-hex-${idx}${vibrating ? ' vibrating' : ''}`}
            points={hexPoints(c.cx, c.cy, HEX_CIRCUM)}
          />
        ))}
      </g>
    </svg>
  );
}

// ---------------------------------------------------------------------------
// Component 3: BreathingCircle
// ---------------------------------------------------------------------------
/**
 * A single gold circle that breathes — used as the opening and closing mark.
 *
 * Props:
 *   size      {number}  Diameter in pixels. Default: 120.
 *   className {string}
 */
function BreathingCircle({ size = 120, className = '' }) {
  const reduced = usePrefersReducedMotion();
  const uid = React.useId ? React.useId() : 'bc';
  const animId = `${uid}-breathe`;

  const cx = size / 2;
  const cy = size / 2;
  const r = (size / 2) - 2; // small inset so stroke isn't clipped at edge

  return (
    <svg
      className={className}
      width={size}
      height={size}
      viewBox={`0 0 ${size} ${size}`}
      role="img"
      aria-label="A single gold circle — the mark before language"
      style={{ display: 'block', overflow: 'visible' }}
    >
      <style>{`
        @keyframes ${animId}-scale {
          from { transform: scale(0.98); }
          to   { transform: scale(1.02); }
        }
        @keyframes ${animId}-opacity {
          from { stroke-opacity: 0.6; }
          to   { stroke-opacity: 1.0; }
        }
        .${animId}-circle {
          fill: none;
          stroke: #c9a84c;
          stroke-width: 1.5;
          transform-origin: ${cx}px ${cy}px;
          transform-box: fill-box;
          ${reduced
            ? 'stroke-opacity: 0.8;'
            : `
              animation:
                ${animId}-scale   4s ease-in-out infinite alternate,
                ${animId}-opacity 4s ease-in-out infinite alternate;
            `
          }
          will-change: transform, stroke-opacity;
        }
      `}</style>
      <circle
        className={`${animId}-circle`}
        cx={cx}
        cy={cy}
        r={r}
      />
    </svg>
  );
}
