// Synthesizes the game's SFX as 16-bit PCM WAVs so no licensed audio is
// needed. Run after changing any envelope:
//   dart run tool/gen_sfx.dart
import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';

const sampleRate = 44100;

void main() {
  final outDir = Directory('assets/audio')..createSync(recursive: true);

  _write(outDir, 'place.wav', _tone([(180, 220)], 0.06, gain: 0.5));
  _write(outDir, 'clear.wav', _tone([(440, 880)], 0.16, gain: 0.6));
  _write(
      outDir,
      'combo.wav',
      _concat([
        _tone([(660, 660)], 0.07, gain: 0.55),
        _tone([(880, 880)], 0.07, gain: 0.55),
        _tone([(1175, 1175)], 0.1, gain: 0.6),
      ]));
  _write(
      outDir,
      'win.wav',
      _concat([
        _tone([(523, 523)], 0.12, gain: 0.55),
        _tone([(659, 659)], 0.12, gain: 0.55),
        _tone([(784, 784)], 0.12, gain: 0.55),
        _tone([(1047, 1047)], 0.3, gain: 0.6),
      ]));
  _write(outDir, 'lose.wav', _tone([(330, 150)], 0.45, gain: 0.5));

  stdout.writeln('SFX written to ${outDir.path}');
}

/// Sine sweep segments (startHz, endHz) with a soft attack/decay envelope.
Float64List _tone(List<(double, double)> sweeps, double seconds,
    {double gain = 0.5}) {
  final n = (seconds * sampleRate).round();
  final out = Float64List(n);
  var phase = 0.0;
  for (var i = 0; i < n; i++) {
    final t = i / n;
    final seg = sweeps[(t * sweeps.length).floor().clamp(0, sweeps.length - 1)];
    final freq = seg.$1 + (seg.$2 - seg.$1) * t;
    phase += 2 * math.pi * freq / sampleRate;
    final attack = math.min(1.0, i / (0.005 * sampleRate));
    final decay = math.min(1.0, (n - i) / (0.05 * sampleRate));
    out[i] = math.sin(phase) * gain * attack * decay;
  }
  return out;
}

Float64List _concat(List<Float64List> parts) {
  final total = parts.fold<int>(0, (sum, p) => sum + p.length);
  final out = Float64List(total);
  var offset = 0;
  for (final p in parts) {
    out.setAll(offset, p);
    offset += p.length;
  }
  return out;
}

void _write(Directory dir, String name, Float64List samples) {
  final pcm = Int16List(samples.length);
  for (var i = 0; i < samples.length; i++) {
    pcm[i] = (samples[i].clamp(-1.0, 1.0) * 32767).round();
  }
  final dataSize = pcm.length * 2;
  final header = ByteData(44)
    ..setUint32(0, 0x52494646, Endian.big) // RIFF
    ..setUint32(4, 36 + dataSize, Endian.little)
    ..setUint32(8, 0x57415645, Endian.big) // WAVE
    ..setUint32(12, 0x666d7420, Endian.big) // fmt
    ..setUint32(16, 16, Endian.little)
    ..setUint16(20, 1, Endian.little) // PCM
    ..setUint16(22, 1, Endian.little) // mono
    ..setUint32(24, sampleRate, Endian.little)
    ..setUint32(28, sampleRate * 2, Endian.little)
    ..setUint16(32, 2, Endian.little)
    ..setUint16(34, 16, Endian.little)
    ..setUint32(36, 0x64617461, Endian.big) // data
    ..setUint32(40, dataSize, Endian.little);
  final file = File('${dir.path}/$name');
  final bytes = BytesBuilder()
    ..add(header.buffer.asUint8List())
    ..add(pcm.buffer.asUint8List());
  file.writeAsBytesSync(bytes.toBytes());
  stdout.writeln('  $name (${(dataSize / 1024).toStringAsFixed(1)} KB)');
}
