// ====== ACOUSTICS EXPERIMENTS ======

// ---------- 1. Sound Propagation ----------
function SoundPropagationExp({ category, name, catColor }) {
  const [medium, setMedium] = useState('air'); // air, water, solid, vacuum
  const [isRinging, setIsRinging] = useState(false);
  const [time, setTime] = useState(0);
  const rafRef = useRef();

  const mediumData = {
    air:    { name: '空气',  speed: 340,  color: '#87CEEB', transmit: 0.7 },
    water:  { name: '水',    speed: 1500, color: '#4DB8FF', transmit: 0.9 },
    solid:  { name: '木头',  speed: 4500, color: '#8B5A2B', transmit: 1.0 },
    vacuum: { name: '真空',  speed: 0,    color: '#141416', transmit: 0 },
  };
  const m = mediumData[medium];

  useEffect(() => {
    if (!isRinging) return;
    let last = performance.now();
    const tick = (now) => {
      const dt = (now - last) / 1000;
      last = now;
      setTime(t => t + dt);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [isRinging]);

  // draw bell + sound waves
  const canvasW = 700, canvasH = 420;
  const cx = canvasW / 2, cy = canvasH / 2;

  const waves = [];
  if (isRinging && m.speed > 0) {
    const waveCount = 6;
    const interval = 0.15; // seconds between waves
    for (let i = 0; i < waveCount; i++) {
      const t = time - i * interval;
      if (t > 0) {
        const r = t * m.speed * 0.3; // scale for visual
        if (r < 400) waves.push({ r, opacity: Math.max(0, 1 - r / 400) });
      }
    }
  }

  const bellAngle = isRinging ? Math.sin(time * 15) * 0.12 : 0;

  const panelTabs = [
    { id: 'controls', label: '操作' },
    { id: 'data', label: '数据/公式' },
    { id: 'principle', label: '原理' },
  ];

  const panelContent = (tab) => {
    if (tab === 'controls') return (
      <>
        <div className="panel-section">
          <div className="panel-section-title">介质选择</div>
          <div className="btn-row" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            {Object.entries(mediumData).map(([k, v]) => (
              <button key={k} className={`btn ${medium === k ? 'primary' : ''}`}
                style={medium === k ? {} : {}}
                onClick={() => setMedium(k)}>
                {v.name}
              </button>
            ))}
          </div>
        </div>
        <div className="panel-section">
          <div className="panel-section-title">振源控制</div>
          <button className={`btn ${isRinging ? 'primary' : ''}`} style={{ width: '100%', justifyContent: 'center' }}
            onClick={() => { setIsRinging(!isRinging); if (!isRinging) setTime(0); }}>
            <Icon name={isRinging ? 'Bell' : 'BellOff'} size={14} />
            <span>{isRinging ? '正在发声（点击停止）' : '点击发声'}</span>
          </button>
        </div>
      </>
    );
    if (tab === 'data') return (
      <>
        <div className="meter-row">
          <MeterCard label="介质" value={m.name} unit="" sub={`声速 ${m.speed} m/s`} />
          <MeterCard label="传播状态" value={m.transmit > 0 ? '传播中' : '不能传声'} unit="" />
        </div>
        <FormulaBox
          expr="v = s / t"
          subs={[
            `声速 <span class="val">v</span> = <span class="val">${m.speed} m/s</span>`,
            `距离 <span class="val">s</span> = v · t，声音随时间向四周扩散`,
            m.transmit === 0
              ? '<strong style="color:#111114">真空不能传声！声音需要介质</strong>'
              : `介质密度越大，分子间作用力越强，声速越快`,
          ]}
        />
      </>
    );
    if (tab === 'principle') return (
      <ExplainBox title="声音的产生与传播">
        <p><strong>产生：</strong>声音由物体振动产生，振动停止，发声也停止。</p>
        <p style={{marginTop:8}}><strong>传播：</strong>声音的传播需要介质，真空不能传声。</p>
        <p style={{marginTop:8}}><strong>声速：</strong>v<sub>固体</sub> {'>'} v<sub>液体</sub> {'>'} v<sub>气体</sub></p>
        <p style={{marginTop:8}}>15℃空气中声速约 340 m/s。</p>
        <p style={{marginTop:8}}>声音以波的形式传播，叫声波。</p>
      </ExplainBox>
    );
  };

  return (
    <ExperimentShell category={category} name={name} catColor={catColor}
      panelTabs={panelTabs} panelContent={panelContent}>
      <div className="sim-canvas-wrap">
        <svg width={canvasW} height={canvasH} className="sim-canvas" style={{ background: m.color + '33' }}>
          {/* medium bg label */}
          <text x={canvasW - 16} y={24} textAnchor="end" fill="#5E3A18" fontSize="14" fontFamily="serif">
            介质：{m.name}
          </text>

          {/* sound waves */}
          {waves.map((w, i) => (
            <circle key={i} cx={cx} cy={cy} r={w.r} fill="none"
              stroke={catColor} strokeWidth="2" opacity={w.opacity * m.transmit} />
          ))}

          {/* bell */}
          <g transform={`translate(${cx}, ${cy}) rotate(${bellAngle * 180 / Math.PI})`}>
            {/* bell string */}
            <line x1="0" y1="-60" x2="0" y2="-20" stroke="#5E3A18" strokeWidth="2" />
            {/* bell body */}
            <path d={`M -22 -20 L -18 20 Q 0 28 18 20 L 22 -20 Q 0 -28 -22 -20 Z`}
              fill="#D4A853" stroke="#8B6914" strokeWidth="1.5" />
            <ellipse cx="0" cy="-20" rx="22" ry="5" fill="#E8C770" stroke="#8B6914" strokeWidth="1.5" />
            {/* bell clapper */}
            <line x1="0" y1="0" x2="0" y2="16" stroke="#5E3A18" strokeWidth="2" />
            <circle cx="0" cy="18" r="4" fill="#8B6914" />
          </g>

          {/* ear icon on right if transmitting */}
          {m.transmit > 0 && (
            <g transform={`translate(${canvasW - 80}, ${cy})`} opacity={isRinging ? 1 : 0.4}>
              <path d="M 0 -20 Q 25 -15 25 0 Q 25 15 0 20 Q -5 10 -5 0 Q -5 -10 0 -20 Z"
                fill="#f5d0a0" stroke="#8B6914" strokeWidth="1.5" />
            </g>
          )}

          {m.transmit === 0 && (
            <g transform={`translate(${cx}, ${cy + 80})`}>
              <text x="0" y="0" textAnchor="middle" fill="#FF5C5C" fontSize="16" fontWeight="bold">
                真空不能传声！
              </text>
              <text x="0" y="24" textAnchor="middle" fill="#5E3A18" fontSize="12">
                没有介质，振动无法传播
              </text>
            </g>
          )}
        </svg>
      </div>
    </ExperimentShell>
  );
}

// ---------- 2. Pitch & Frequency ----------
function PitchFrequencyExp({ category, name, catColor }) {
  const [freq, setFreq] = useState(440); // Hz
  const [stringLen, setStringLen] = useState(50); // cm
  const [isPlaying, setIsPlaying] = useState(false);
  const [time, setTime] = useState(0);
  const rafRef = useRef();

  // Relationship: f ∝ 1/L (for string), adjust freq based on length
  // base freq at L = 50cm
  const actualFreq = Math.round(freq * 50 / stringLen);

  useEffect(() => {
    if (!isPlaying) return;
    let last = performance.now();
    const tick = (now) => {
      const dt = (now - last) / 1000;
      last = now;
      setTime(t => t + dt);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [isPlaying]);

  const canvasW = 700, canvasH = 420;

  // waveform
  const wavePoints = [];
  const amplitude = 40;
  for (let x = 0; x <= canvasW - 60; x += 2) {
    const t = x / (canvasW - 60) * (2 * Math.PI * 3) + time * actualFreq * 2 * Math.PI * 0.1;
    const y = Math.sin(t) * amplitude;
    wavePoints.push(`${x + 30},${canvasH / 2 + y}`);
  }

  // string vibration visualization
  const stringY = 120;
  const stringVib = isPlaying ? Math.sin(time * actualFreq * 2 * Math.PI) * 8 : 0;

  const panelTabs = [
    { id: 'controls', label: '操作' },
    { id: 'data', label: '数据/公式' },
    { id: 'principle', label: '原理' },
  ];

  const getPitchDesc = (f) => {
    if (f < 80) return '低沉';
    if (f < 200) return '低音';
    if (f < 500) return '中音';
    if (f < 1500) return '高音';
    return '尖锐';
  };

  const panelContent = (tab) => {
    if (tab === 'controls') return (
      <>
        <div className="panel-section">
          <div className="panel-section-title">频率调节</div>
          <ControlSlider label="振源频率" value={freq} onChange={setFreq} min={60} max={2000} step={10} unit=" Hz" />
          <ControlSlider label="琴弦长度" value={stringLen} onChange={setStringLen} min={20} max={80} step={1} unit=" cm" />
        </div>
        <div className="panel-section">
          <div className="panel-section-title">振动控制</div>
          <button className={`btn ${isPlaying ? 'primary' : ''}`} style={{ width: '100%', justifyContent: 'center' }}
            onClick={() => setIsPlaying(!isPlaying)}>
            {isPlaying ? '⏸ 停止振动' : '▶ 开始振动'}
          </button>
        </div>
      </>
    );
    if (tab === 'data') return (
      <>
        <div className="meter-row">
          <MeterCard label="实际频率" value={actualFreq} unit=" Hz" sub={getPitchDesc(actualFreq)} />
          <MeterCard label="周期" value={(1000 / actualFreq).toFixed(2)} unit=" ms" />
        </div>
        <FormulaBox
          expr="f = 1 / T"
          subs={[
            `频率 <span class="val">f = ${actualFreq} Hz</span>`,
            `周期 <span class="val">T = ${(1/actualFreq*1000).toFixed(3)} ms</span>`,
            `琴弦越短，振动越快，频率越高，音调越高`,
          ]}
        />
        <div className="chart-box">
          <MiniChart data={Array.from({length: 80}, (_, i) => Math.sin(i / 80 * 2 * Math.PI * (actualFreq / 200)) * 25)}
            yLabel="波形" color={catColor} yMin={-60} yMax={60} />
        </div>
      </>
    );
    if (tab === 'principle') return (
      <ExplainBox title="音调与频率">
        <p><strong>音调：</strong>声音的高低叫音调。</p>
        <p style={{marginTop:6}}><strong>频率：</strong>物体每秒振动的次数，单位 Hz。</p>
        <p style={{marginTop:6}}>频率越高，音调越高；频率越低，音调越低。</p>
        <p style={{marginTop:6}}><strong>弦乐器：</strong>弦越短、越细、越紧，音调越高。</p>
        <p style={{marginTop:6}}>人耳听觉范围：20 Hz ~ 20000 Hz</p>
      </ExplainBox>
    );
  };

  return (
    <ExperimentShell category={category} name={name} catColor={catColor}
      panelTabs={panelTabs} panelContent={panelContent}>
      <div className="sim-canvas-wrap">
        <svg width={canvasW} height={canvasH} className="sim-canvas">
          {/* string instrument top */}
          <g>
            {/* wooden frame */}
            <rect x="60" y={stringY - 30} width={canvasW - 120} height="60" rx="4"
              fill="#A97442" stroke="#5E3A18" strokeWidth="2" />
            {/* string */}
            {isPlaying ? (
              <path d={`M 80 ${stringY} ${Array.from({length: 41}, (_, i) => {
                const x = 80 + i * (canvasW - 160) / 40;
                const phase = i / 40 * Math.PI * 4;
                const y = stringY + Math.sin(phase + time * actualFreq * 2 * Math.PI) * 6;
                return `L ${x} ${y}`;
              }).join(' ')} Z`}
                fill="none" stroke="#D4A853" strokeWidth="2" />
            ) : (
              <line x1="80" y1={stringY} x2={canvasW - 80} y2={stringY}
                stroke="#D4A853" strokeWidth="2" />
            )}
            {/* left post */}
            <rect x="76" y={stringY - 25} width="8" height="50" fill="#5E3A18" />
            {/* right post - movable */}
            <rect x={80 + stringLen / 80 * (canvasW - 160) - 4} y={stringY - 25} width="8" height="50" fill="#5E3A18" />
          </g>

          {/* waveform display */}
          <g>
            <rect x="30" y="200" width={canvasW - 60} height="180" rx="6"
              fill="#111114" stroke="#2e2e33" strokeWidth="1.5" />
            <text x="44" y="222" fill="#8FA3BD" fontSize="11">波形显示</text>

            {/* center line */}
            <line x1="30" y1={290} x2={canvasW - 30} y2={290}
              stroke="#2e2e33" strokeWidth="1" strokeDasharray="4,4" />

            {/* waveform */}
            <polyline points={wavePoints.join(' ')} fill="none" stroke={catColor} strokeWidth="2" />

            {/* freq text */}
            <text x={canvasW - 44} y="222" textAnchor="end" fill={catColor} fontSize="13" fontWeight="bold"
              fontFamily="JetBrains Mono, monospace">
              {actualFreq} Hz
            </text>
          </g>
        </svg>
      </div>
    </ExperimentShell>
  );
}

// ---------- 3. Loudness & Amplitude ----------
function LoudnessAmplitudeExp({ category, name, catColor }) {
  const [amplitude, setAmplitude] = useState(30);
  const [freq, setFreq] = useState(440);
  const [isPlaying, setIsPlaying] = useState(false);
  const [time, setTime] = useState(0);
  const rafRef = useRef();

  useEffect(() => {
    if (!isPlaying) return;
    let last = performance.now();
    const tick = (now) => {
      const dt = (now - last) / 1000;
      last = now;
      setTime(t => t + dt);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [isPlaying]);

  const canvasW = 700, canvasH = 420;
  const speakerX = 120, speakerY = canvasH / 2;

  const wavePoints = [];
  for (let x = 0; x <= canvasW - 200; x += 2) {
    const t = x / (canvasW - 200) * Math.PI * 4 + time * freq * 2 * Math.PI * 0.1;
    const y = Math.sin(t) * amplitude;
    wavePoints.push(`${x + 170},${speakerY + y}`);
  }

  const loudness = Math.round(amplitude / 60 * 100);
  const loudnessLevel = loudness > 70 ? '响亮' : loudness > 40 ? '中等' : loudness > 15 ? '较轻' : '微弱';

  const panelTabs = [
    { id: 'controls', label: '操作' },
    { id: 'data', label: '数据/公式' },
    { id: 'principle', label: '原理' },
  ];

  const panelContent = (tab) => {
    if (tab === 'controls') return (
      <>
        <div className="panel-section">
          <div className="panel-section-title">参数调节</div>
          <ControlSlider label="振幅" value={amplitude} onChange={setAmplitude} min={5} max={60} step={1} unit=" mm" />
          <ControlSlider label="频率" value={freq} onChange={setFreq} min={100} max={1000} step={10} unit=" Hz" />
        </div>
        <div className="panel-section">
          <div className="panel-section-title">控制</div>
          <button className={`btn ${isPlaying ? 'primary' : ''}`} style={{ width: '100%', justifyContent: 'center' }}
            onClick={() => setIsPlaying(!isPlaying)}>
            {isPlaying ? '⏸ 停止' : '▶ 播放声音'}
          </button>
        </div>
      </>
    );
    if (tab === 'data') return (
      <>
        <div className="meter-row">
          <MeterCard label="振幅" value={amplitude} unit=" mm" sub="振动幅度" />
          <MeterCard label="响度等级" value={loudnessLevel} unit="" sub={`相对响度 ${loudness}%`} />
        </div>
        <FormulaBox
          expr="响度与振幅"
          subs={[
            `振幅 <span class="val">A = ${amplitude} mm</span>`,
            `频率 <span class="val">f = ${freq} Hz</span>`,
            `振幅越大，响度越大；振幅越小，响度越小`,
            `响度还与距离发声体的远近、声音分散程度有关`,
            `注意：响度是主观感觉，振幅是客观物理量`,
          ]}
        />
        <div className="chart-box">
          <MiniChart
            data={Array.from({length: 100}, (_, i) => Math.sin(i / 100 * 2 * Math.PI * 4) * amplitude)}
            yLabel="波形" color={catColor} yMin={-70} yMax={70} />
        </div>
      </>
    );
    if (tab === 'principle') return (
      <ExplainBox title="响度与振幅">
        <p><strong>响度：</strong>声音的大小（强弱）叫响度。</p>
        <p style={{marginTop:6}}><strong>振幅：</strong>物体振动时偏离原位置的最大距离。</p>
        <p style={{marginTop:6}}>振幅越大，响度越大；振幅越小，响度越小。</p>
        <p style={{marginTop:6}}><strong>注意：</strong>音调和响度是两个不同的特性。</p>
        <p style={{marginTop:6}}>音调由频率决定，响度由振幅决定。</p>
      </ExplainBox>
    );
  };

  // speaker cone vibration
  const coneOffset = isPlaying ? Math.sin(time * freq * 2 * Math.PI) * amplitude * 0.3 : 0;

  return (
    <ExperimentShell category={category} name={name} catColor={catColor}
      panelTabs={panelTabs} panelContent={panelContent}>
      <div className="sim-canvas-wrap">
        <svg width={canvasW} height={canvasH} className="sim-canvas">
          {/* speaker */}
          <g transform={`translate(${speakerX}, ${speakerY})`}>
            {/* speaker cabinet */}
            <rect x="-50" y="-80" width="60" height="160" rx="6"
              fill="#2a2a2a" stroke="#111" strokeWidth="2" />
            {/* speaker cone outer */}
            <ellipse cx="10" cy="0" rx="35" ry="55" fill="#3a3a3a" stroke="#111" strokeWidth="2" />
            {/* cone inner (vibrating) */}
            <ellipse cx={10 + coneOffset} cy="0" rx="20" ry="32"
              fill="#555" stroke="#222" strokeWidth="1.5" />
            <circle cx={10 + coneOffset} cy="0" r="8" fill="#1a1a1a" />
            {/* cone dust cap */}
            <circle cx={10 + coneOffset} cy="0" r="5" fill="#333" />
          </g>

          {/* sound waves from speaker */}
          {isPlaying && Array.from({length: 5}, (_, i) => {
            const t = (time * 0.5 + i * 0.2) % 1;
            const x = speakerX + 20 + t * (canvasW - 180);
            const opacity = Math.max(0, 1 - t) * 0.6;
            const h = amplitude * 2;
            return (
              <g key={i}>
                <ellipse cx={x} cy={speakerY} rx="3" ry={h / 2} fill="none"
                  stroke={catColor} strokeWidth="2" opacity={opacity} />
              </g>
            );
          })}

          {/* waveform display area */}
          <rect x="170" y={speakerY - 80} width={canvasW - 200} height="160" rx="4"
            fill="#111114" stroke="#2e2e33" strokeWidth="1" opacity="0.6" />
          <polyline points={wavePoints.join(' ')} fill="none" stroke={catColor} strokeWidth="2" />
          <line x1="170" y1={speakerY} x2={canvasW - 30} y2={speakerY}
            stroke="#2e2e33" strokeWidth="1" strokeDasharray="4,4" />

          {/* labels */}
          <text x="180" y={speakerY - 65} fill="#8FA3BD" fontSize="11">波形：振幅 {amplitude} mm · 频率 {freq} Hz</text>
          <text x={canvasW - 40} y={speakerY - 65} textAnchor="end" fill={catColor} fontSize="11" fontWeight="bold">
            响度 {loudness}
          </text>
        </svg>
      </div>
    </ExperimentShell>
  );
}

// Export
Object.assign(window, {
  SoundPropagationExp,
  PitchFrequencyExp,
  LoudnessAmplitudeExp,
});
