/* global React */
// ============================================================
// Waveform & spectrum SVG helpers
// All draw into an SVG viewBox sized by props (default 600x140)
// ============================================================

const Wave = ({
  width = 600, height = 140,
  fn,                 // (x in [0,1]) => y in [-1, 1]
  color = 'var(--cyan)',
  glow = true,
  strokeWidth = 1.8,
  samples = 600,
  className = '',
  fill = false,
}) => {
  const pad = 8;
  const w = width, h = height;
  const cx = h / 2;
  const amp = (h / 2) - pad;
  let d = '';
  for (let i = 0; i <= samples; i++) {
    const t = i / samples;
    const x = t * w;
    const y = cx - fn(t) * amp;
    d += (i === 0 ? 'M' : 'L') + x.toFixed(2) + ',' + y.toFixed(2) + ' ';
  }
  const fillD = fill ? d + ` L${w},${cx} L0,${cx} Z` : null;
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none"
      width="100%" height={h} className={className}
      style={{ display: 'block' }}>
      {/* center axis */}
      <line x1="0" y1={cx} x2={w} y2={cx} stroke="rgba(148,184,220,0.15)" strokeDasharray="2 4" strokeWidth="1" />
      {fill && <path d={fillD} fill={color} opacity="0.08" />}
      <path d={d} fill="none" stroke={color} strokeWidth={strokeWidth}
        strokeLinejoin="round" strokeLinecap="round"
        style={glow ? { filter: `drop-shadow(0 0 4px ${color})` } : undefined} />
    </svg>
  );
};

// ----- generators -----
const audioFn = (t) => {
  // a slow, voice-like wobble
  return 0.5 * Math.sin(2 * Math.PI * 2 * t) + 0.25 * Math.sin(2 * Math.PI * 3 * t + 1);
};
const carrierFn = (freq = 24) => (t) => Math.sin(2 * Math.PI * freq * t);
const amFn = (m = 0.5, fc = 24, fm = 2) => (t) => {
  const info = Math.sin(2 * Math.PI * fm * t);
  return (1 + m * info) * Math.cos(2 * Math.PI * fc * t) / (1 + m);
};
// raw AM that can clip when m > 1 (for visualizing overmodulation)
const amRawFn = (m = 0.5, fc = 24, fm = 2) => (t) => {
  const info = Math.sin(2 * Math.PI * fm * t);
  const env = (1 + m * info);
  return env * Math.cos(2 * Math.PI * fc * t) / 2;
};
const fmFn = (kf = 6, fc = 18, fm = 2) => (t) => {
  // φ(t) = 2π fc t + kf · ∫sin(2π fm t) dt
  const integ = -Math.cos(2 * Math.PI * fm * t) / (2 * Math.PI * fm);
  return Math.sin(2 * Math.PI * fc * t + 2 * Math.PI * kf * integ);
};

// envelope of AM (for highlighting it)
const amEnvelope = (m, fm = 2) => (t) => {
  return (1 + m * Math.sin(2 * Math.PI * fm * t)) / (1 + m);
};

// ============================================================
// Spectrum (vertical bars)
// ============================================================
const Spectrum = ({ width = 600, height = 180, lines, fmin = 0, fmax = 100, labelsBelow = true }) => {
  const padX = 36, padY = 28;
  const w = width, h = height;
  const baseY = h - padY;
  const topY = padY;
  const fToX = (f) => padX + ((f - fmin) / (fmax - fmin)) * (w - 2 * padX);
  return (
    <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} style={{ display: 'block' }}>
      {/* baseline */}
      <line x1={padX - 8} y1={baseY} x2={w - padX + 8} y2={baseY} stroke="rgba(148,184,220,0.3)" strokeWidth="1.2"/>
      {/* arrow */}
      <polygon points={`${w - padX + 8},${baseY} ${w - padX + 2},${baseY - 4} ${w - padX + 2},${baseY + 4}`} fill="rgba(148,184,220,0.5)" />
      <text x={w - padX + 14} y={baseY + 4} fontSize="10" fontFamily="var(--font-mono)" fill="var(--text-muted)">f →</text>
      {/* y axis */}
      <line x1={padX} y1={baseY} x2={padX} y2={topY - 4} stroke="rgba(148,184,220,0.3)" strokeWidth="1.2"/>
      <text x={padX - 6} y={topY - 8} fontSize="10" fontFamily="var(--font-mono)" fill="var(--text-muted)" textAnchor="end">|S(f)|</text>
      {lines.map((ln, i) => {
        const x = fToX(ln.f);
        const ampPx = (baseY - topY) * (ln.a ?? 1);
        const lineColor = ln.color || 'var(--cyan)';
        return (
          <g key={i}>
            <line x1={x} y1={baseY} x2={x} y2={baseY - ampPx}
              stroke={lineColor} strokeWidth="2.5" style={{filter: `drop-shadow(0 0 4px ${lineColor})`}} />
            <polygon points={`${x},${baseY - ampPx - 6} ${x - 4},${baseY - ampPx} ${x + 4},${baseY - ampPx}`}
              fill={lineColor} style={{filter: `drop-shadow(0 0 4px ${lineColor})`}} />
            <text x={x} y={baseY + 16} fontSize="10" fontFamily="var(--font-mono)"
              fill="var(--text-secondary)" textAnchor="middle">{ln.label}</text>
            {ln.sub && (
              <text x={x} y={baseY + 28} fontSize="9" fontFamily="var(--font-mono)"
                fill="var(--text-muted)" textAnchor="middle">{ln.sub}</text>
            )}
          </g>
        );
      })}
    </svg>
  );
};

// ============================================================
// Connector arrow (for block diagrams)
// ============================================================
const Arrow = ({ direction = 'right', length = 36, color = 'var(--cyan)' }) => {
  const horizontal = direction === 'right' || direction === 'left';
  const w = horizontal ? length : 14;
  const h = horizontal ? 14 : length;
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ flexShrink: 0 }}>
      {horizontal ? (
        <>
          <line x1="0" y1={h/2} x2={w-6} y2={h/2} stroke={color} strokeWidth="1.5" />
          <polygon points={`${w-6},${h/2-4} ${w},${h/2} ${w-6},${h/2+4}`} fill={color} />
        </>
      ) : (
        <>
          <line x1={w/2} y1="0" x2={w/2} y2={h-6} stroke={color} strokeWidth="1.5" />
          <polygon points={`${w/2-4},${h-6} ${w/2},${h} ${w/2+4},${h-6}`} fill={color} />
        </>
      )}
    </svg>
  );
};

Object.assign(window, {
  Wave, Spectrum, Arrow,
  audioFn, carrierFn, amFn, amRawFn, fmFn, amEnvelope,
});
