/*
 * net/voice_frame.h — UDP media frame wire format (header-only).
 *
 * Design: docs/voice.md §2. The 14-byte fixed header is also used as AEAD associated data.
 * Multi-byte fields are big-endian. The payload (Opus packet) is AEAD-encrypted.
 */
#ifndef VOICECAT_NET_VOICE_FRAME_H
#define VOICECAT_NET_VOICE_FRAME_H

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>

namespace voicecat::net {

// Frame types.
inline constexpr uint8_t kFrameVoice      = 1;
inline constexpr uint8_t kFrameKeepalive  = 2;
inline constexpr uint8_t kFrameUdpBinding = 3;

// Flag bits in VoiceFrame::flags.
inline constexpr uint8_t kFlagMarker     = 0x01;  // start of talkspurt
inline constexpr uint8_t kFlagFecPresent = 0x02;  // this frame carries previous-frame FEC
inline constexpr uint8_t kFlagDtx        = 0x04;  // comfort-noise / DTX silence frame
inline constexpr uint8_t kFlagLast       = 0x08;  // last frame before stream stop

// Codec IDs.
inline constexpr uint16_t kCodecOpus = 0;

// Size of the serialized header (bytes before the payload).
inline constexpr size_t kVoiceHeaderSize = 20;

/*
 * Wire layout (big-endian):
 *   [0]      type      u8
 *   [1]      flags     u8
 *   [2..3]   codec     u16
 *   [4..7]   ssrc      u32
 *   [8..15]  seq       u64   (full monotonic send counter — the AEAD nonce counter)
 *   [16..19] timestamp u32   (sample clock @48 kHz)
 *   [20+]    payload         (AEAD-encrypted Opus packet)
 *
 * The 20-byte header is the AEAD AAD (authenticated, not encrypted).
 * The payload region is the AEAD ciphertext + 16-byte Poly1305 MAC.
 *
 * Protocol v2 widened `seq` from u16 to u64: the receiver reconstructs the AEAD
 * nonce counter directly from this field, so the full 64-bit counter must be on the
 * wire (a 16-bit field wrapped after 65,536 frames and desynced the nonce).
 */
struct VoiceFrame {
    uint8_t  type      = kFrameVoice;
    uint8_t  flags     = 0;
    uint16_t codec     = kCodecOpus;
    uint32_t ssrc      = 0;
    uint64_t seq       = 0;
    uint32_t timestamp = 0;
    std::vector<uint8_t> payload;   // Opus bytes (pre-AEAD on send; post-AEAD on recv)
};

// Serialize the 20-byte header into buf[0..19]. buf must be at least kVoiceHeaderSize bytes.
inline void serialize_header(const VoiceFrame& f, uint8_t* buf) {
    buf[0]  = f.type;
    buf[1]  = f.flags;
    buf[2]  = static_cast<uint8_t>(f.codec >> 8);
    buf[3]  = static_cast<uint8_t>(f.codec & 0xFF);
    buf[4]  = static_cast<uint8_t>(f.ssrc >> 24);
    buf[5]  = static_cast<uint8_t>(f.ssrc >> 16);
    buf[6]  = static_cast<uint8_t>(f.ssrc >> 8);
    buf[7]  = static_cast<uint8_t>(f.ssrc & 0xFF);
    buf[8]  = static_cast<uint8_t>(f.seq >> 56);
    buf[9]  = static_cast<uint8_t>(f.seq >> 48);
    buf[10] = static_cast<uint8_t>(f.seq >> 40);
    buf[11] = static_cast<uint8_t>(f.seq >> 32);
    buf[12] = static_cast<uint8_t>(f.seq >> 24);
    buf[13] = static_cast<uint8_t>(f.seq >> 16);
    buf[14] = static_cast<uint8_t>(f.seq >> 8);
    buf[15] = static_cast<uint8_t>(f.seq & 0xFF);
    buf[16] = static_cast<uint8_t>(f.timestamp >> 24);
    buf[17] = static_cast<uint8_t>(f.timestamp >> 16);
    buf[18] = static_cast<uint8_t>(f.timestamp >> 8);
    buf[19] = static_cast<uint8_t>(f.timestamp & 0xFF);
}

// Parse the 20-byte header from buf. Returns false if len < kVoiceHeaderSize.
inline bool parse_header(const uint8_t* buf, size_t len, VoiceFrame& out) {
    if (len < kVoiceHeaderSize) return false;
    out.type      = buf[0];
    out.flags     = buf[1];
    out.codec     = static_cast<uint16_t>((buf[2] << 8) | buf[3]);
    out.ssrc      = (static_cast<uint32_t>(buf[4]) << 24) |
                    (static_cast<uint32_t>(buf[5]) << 16) |
                    (static_cast<uint32_t>(buf[6]) << 8)  |
                     static_cast<uint32_t>(buf[7]);
    out.seq       = (static_cast<uint64_t>(buf[8])  << 56) |
                    (static_cast<uint64_t>(buf[9])  << 48) |
                    (static_cast<uint64_t>(buf[10]) << 40) |
                    (static_cast<uint64_t>(buf[11]) << 32) |
                    (static_cast<uint64_t>(buf[12]) << 24) |
                    (static_cast<uint64_t>(buf[13]) << 16) |
                    (static_cast<uint64_t>(buf[14]) << 8)  |
                     static_cast<uint64_t>(buf[15]);
    out.timestamp = (static_cast<uint32_t>(buf[16]) << 24) |
                    (static_cast<uint32_t>(buf[17]) << 16) |
                    (static_cast<uint32_t>(buf[18]) << 8)  |
                     static_cast<uint32_t>(buf[19]);
    return true;
}

// Serialize a full UDP_BINDING packet (type=3, token in payload, no AEAD).
inline std::vector<uint8_t> make_udp_binding_packet(const uint8_t* token, size_t token_len) {
    std::vector<uint8_t> pkt(kVoiceHeaderSize + token_len, 0);
    pkt[0] = kFrameUdpBinding;
    std::memcpy(pkt.data() + kVoiceHeaderSize, token, token_len);
    return pkt;
}

}  // namespace voicecat::net

#endif  // VOICECAT_NET_VOICE_FRAME_H
