import 'dart:math' as math;
import 'dart:typed_data';
import '../../utils/constants.dart';
import '../../utils/audio_math.dart';

/// 4-FSK audio modulator.
///
/// Maps pairs of bits (dibits) to one of 4 sinusoidal tones:
///   dibit 00 → f0 = 1000 Hz
///   dibit 01 → f1 = 1400 Hz
///   dibit 10 → f2 = 1800 Hz
///   dibit 11 → f3 = 2200 Hz
///
/// Symbol rate: 600 baud → gross bitrate 1200 bps.
/// Each symbol is ~13.33 samples at 8000 Hz.  A phase accumulator
/// advances continuously so tone transitions are phase-coherent.
///
/// Wire frame produced for each call to [modulatePacket]:
///   [preamble: 24 alternating 00/11 symbols] [data symbols…]
///
/// The output is a Float64List suitable for conversion to Int16 PCM
/// via [AudioMath.floatToInt16Bytes].
class FskModulator {
  // ── State ─────────────────────────────────────────────────────────
  double _phase = 0.0; // radians, maintained across calls for continuity

  // ── Public API ────────────────────────────────────────────────────

  /// Modulate an arbitrary byte array [data] into an audio Float64List.
  ///
  /// Prepends [C.preambleSymbols] alternating symbols and a 2-byte sync word.
  Float64List modulatePacket(List<int> data) {
    // Build dibit stream: preamble + sync word + data bits.
    final dibits = <int>[];

    // Preamble: alternating 00, 11 for timing acquisition.
    for (int i = 0; i < C.preambleSymbols; i++) {
      dibits.add(i.isEven ? 0 : 3);
    }

    // Sync word (2 bytes) as dibits.
    for (final byte in C.syncWord) {
      _byteToDibits(byte, dibits);
    }

    // Data bytes as dibits.
    for (final byte in data) {
      _byteToDibits(byte, dibits);
    }

    return _synthesise(dibits);
  }

  // ── Private helpers ───────────────────────────────────────────────

  /// Append 4 dibits for one byte (MSB first).
  static void _byteToDibits(int byte, List<int> out) {
    out.add((byte >> 6) & 0x3);
    out.add((byte >> 4) & 0x3);
    out.add((byte >> 2) & 0x3);
    out.add( byte       & 0x3);
  }

  /// Generate the audio waveform for the given dibit sequence.
  ///
  /// Each symbol spans exactly [C.samplesPerSymbol] (≈13.33) audio samples.
  /// A phase accumulator advances at the appropriate angular rate for the
  /// selected tone, giving smooth inter-symbol transitions.
  ///
  /// A raised-cosine cross-fade of 2 samples is applied at every tone change
  /// to reduce spectral splatter without distorting the majority of each symbol.
  Float64List _synthesise(List<int> dibits) {
    // Total samples = ceil(dibits.length * samplesPerSymbol).
    final totalSamples =
        (dibits.length * C.samplesPerSymbol).ceil() + 8; // +8 guard
    final out = Float64List(totalSamples);

    double symbolAcc = 0.0; // fractional sample accumulator
    int    sampleIdx = 0;
    int    prevSymbol = -1;

    for (final dibit in dibits) {
      final freqHz = C.fskToneHz[dibit];
      final omega  = 2.0 * math.pi * freqHz / C.sampleRate;

      // Number of samples for this symbol (~13 or 14 via accumulator).
      symbolAcc += C.samplesPerSymbol;
      final symbolSamples = symbolAcc.floor();
      symbolAcc -= symbolSamples;

      for (int i = 0; i < symbolSamples && sampleIdx < out.length; i++) {
        double s = C.txAmplitude * math.sin(_phase);

        // Apply 2-sample cross-fade at start of tone change to reduce clicks.
        if (prevSymbol != dibit && i < 2) {
          s *= i / 2.0;
        }

        out[sampleIdx++] = s;
        _phase = (_phase + omega) % (2.0 * math.pi);
      }

      prevSymbol = dibit;
    }

    return Float64List.sublistView(out, 0, sampleIdx);
  }

  /// Reset the phase accumulator (call at session start).
  void reset() {
    _phase = 0.0;
  }
}
