import 'dart:typed_data';
import '../crypto/aes_cipher.dart';
import '../crypto/key_manager.dart';
import '../fec/reed_solomon.dart';
import '../modem/fsk_modulator.dart';
import '../../utils/audio_math.dart';
import '../../utils/constants.dart';

/// Transmit pipeline: voice bytes → packet → encrypt → RS FEC → FSK audio.
///
/// Call [frameAndModulate] with each LPC super-frame (9 bytes) to get back
/// a Float64List of audio samples ready for AudioTrack playback.
class Framer {
  final KeyManager   _keys;
  final Uint8List    _sessionId;     // 4-byte session identifier
  final FskModulator _modem = FskModulator();

  int _seq = 0; // rolling 16-bit sequence counter

  Framer(this._keys, this._sessionId);

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

  /// Full transmit pipeline for one 9-byte LPC payload.
  ///
  /// Steps:
  ///   1. Pad payload to 11 bytes (add seq-high + type tag).
  ///   2. Encrypt with AES-256-CTR.
  ///   3. Build raw packet (SYNC + SEQ + TYPE + LEN + encrypted payload + CRC).
  ///   4. Apply RS(15,11) FEC to the 11-byte encrypted payload → 15 bytes.
  ///   5. Re-assemble the on-wire frame with FEC-encoded payload.
  ///   6. Modulate through 4-FSK → audio samples.
  Float64List frameAndModulate(Uint8List lpcPayload) {
    assert(lpcPayload.length == 9, 'Framer: LPC payload must be 9 bytes');

    final seq = _seq & 0xFFFF;
    _seq = (_seq + 1) & 0xFFFF;

    // ── Step 1: Pad payload → 11 bytes ─────────────────────────────
    // Layout: [lpc(9)] [seq_low(1)] [type(1)]
    final plaintext = Uint8List(C.pktPayloadLen); // 11 bytes
    plaintext.setRange(0, 9, lpcPayload);
    plaintext[9]  = seq & 0xFF;
    plaintext[10] = C.pkTypeVoice;

    // ── Step 2: AES-256-CTR encryption ────────────────────────────
    final nonce     = _keys.buildNonce(_sessionId, seq);
    final encrypted = AesCipher.encrypt(plaintext, _keys.key, nonce);

    // ── Step 4: RS FEC — encode the 11-byte encrypted payload only ──
    final rsEncoded = ReedSolomon.encodeBlock(encrypted); // 11 B → 15 B

    // ── Step 5: Assemble on-wire frame (23 bytes) ──────────────────
    // [SYNC:2][SEQ:2][TYPE:1][RS_PAYLOAD:15][CRC:2]  = 22 bytes
    // Recompute CRC over the RS-encoded block for the wire frame.
    final wire = Uint8List(22);
    wire[0] = C.syncWord[0];
    wire[1] = C.syncWord[1];
    wire[2] = (seq >> 8) & 0xFF;
    wire[3] =  seq       & 0xFF;
    wire[4] = C.pkTypeVoice;
    wire.setRange(5, 20, rsEncoded);        // 15 bytes RS-encoded payload
    final wireCrc = AudioMath.crc16(wire.sublist(2, 20)); // CRC over [2..19]
    wire[20] = (wireCrc >> 8) & 0xFF;
    wire[21] =  wireCrc       & 0xFF;

    // ── Step 6: 4-FSK modulation ────────────────────────────────────
    return _modem.modulatePacket(wire);
  }

  /// Modulate a raw control packet (handshake, ping, etc.).
  Float64List frameControl(int type, Uint8List payload) {
    final seq = _seq & 0xFFFF;
    _seq = (_seq + 1) & 0xFFFF;

    final padded = Uint8List(C.pktPayloadLen);
    padded.setRange(0, payload.length.clamp(0, C.pktPayloadLen), payload);
    final nonce     = _keys.buildNonce(_sessionId, seq);
    final encrypted = AesCipher.encrypt(padded, _keys.key, nonce);
    final rsEncoded = ReedSolomon.encodeBlock(encrypted);

    final wire = Uint8List(22);
    wire[0] = C.syncWord[0];
    wire[1] = C.syncWord[1];
    wire[2] = (seq >> 8) & 0xFF;
    wire[3] =  seq       & 0xFF;
    wire[4] = type;
    wire.setRange(5, 20, rsEncoded);
    final wireCrc = AudioMath.crc16(wire.sublist(2, 20));
    wire[20] = (wireCrc >> 8) & 0xFF;
    wire[21] =  wireCrc       & 0xFF;

    return _modem.modulatePacket(wire);
  }

  void reset() {
    _seq = 0;
    _modem.reset();
  }

}
