/*
 * test_voice_frame — serialize/parse round-trips for the 20-byte UDP media header.
 */
#include <cassert>
#include <cstdio>
#include <cstring>
#include <vector>

#include "net/voice_frame.h"

using namespace voicecat::net;

static int g_failures = 0;

#define CHECK(cond) \
    do { if (!(cond)) { \
        std::printf("FAIL [%s:%d]: %s\n", __FILE__, __LINE__, #cond); \
        ++g_failures; \
    }} while (0)

static void test_header_round_trip() {
    VoiceFrame f;
    f.type      = kFrameVoice;
    f.flags     = kFlagMarker | kFlagFecPresent;
    f.codec     = kCodecOpus;
    f.ssrc      = 0xDEADBEEF;
    f.seq       = 0x0123456789ABCDEFULL;  // full 64-bit range (protocol v2)
    f.timestamp = 0x12345678;

    uint8_t buf[kVoiceHeaderSize];
    serialize_header(f, buf);

    VoiceFrame out{};
    CHECK(parse_header(buf, kVoiceHeaderSize, out));
    CHECK(out.type      == f.type);
    CHECK(out.flags     == f.flags);
    CHECK(out.codec     == f.codec);
    CHECK(out.ssrc      == f.ssrc);
    CHECK(out.seq       == f.seq);
    CHECK(out.timestamp == f.timestamp);
}

static void test_empty_payload_packet() {
    VoiceFrame f;
    f.type  = kFrameKeepalive;
    f.ssrc  = 42;

    uint8_t buf[kVoiceHeaderSize];
    serialize_header(f, buf);

    VoiceFrame out{};
    CHECK(parse_header(buf, kVoiceHeaderSize, out));
    CHECK(out.type == kFrameKeepalive);
    CHECK(out.ssrc == 42);
}

static void test_payload_packet() {
    std::vector<uint8_t> payload(60, 0xAB);
    VoiceFrame f;
    f.ssrc    = 0x00000001;
    f.seq     = 0x0001;
    f.timestamp = 960;
    f.payload = payload;

    // Serialize full wire packet
    std::vector<uint8_t> wire(kVoiceHeaderSize + payload.size());
    serialize_header(f, wire.data());
    std::memcpy(wire.data() + kVoiceHeaderSize, payload.data(), payload.size());

    VoiceFrame out{};
    CHECK(parse_header(wire.data(), wire.size(), out));
    CHECK(out.ssrc      == f.ssrc);
    CHECK(out.seq       == f.seq);
    CHECK(out.timestamp == f.timestamp);

    // Payload starts at kVoiceHeaderSize
    CHECK(wire.size() - kVoiceHeaderSize == 60);
    CHECK(wire[kVoiceHeaderSize] == 0xAB);
}

static void test_udp_binding_packet() {
    uint8_t token[16];
    for (int i = 0; i < 16; ++i) token[i] = static_cast<uint8_t>(i);

    auto pkt = make_udp_binding_packet(token, 16);
    CHECK(pkt.size() == kVoiceHeaderSize + 16);
    CHECK(pkt[0] == kFrameUdpBinding);
    CHECK(std::memcmp(pkt.data() + kVoiceHeaderSize, token, 16) == 0);
}

static void test_parse_too_short() {
    uint8_t buf[10] = {};
    VoiceFrame out{};
    CHECK(!parse_header(buf, 10, out));
}

static void test_big_endian_layout() {
    VoiceFrame f;
    f.ssrc      = 0x01020304;
    f.seq       = 0x05060708090A0B0CULL;
    f.timestamp = 0x0D0E0F10;

    uint8_t buf[kVoiceHeaderSize];
    serialize_header(f, buf);

    // ssrc at [4..7]
    CHECK(buf[4] == 0x01 && buf[5] == 0x02 && buf[6] == 0x03 && buf[7] == 0x04);
    // seq (u64) at [8..15]
    CHECK(buf[8] == 0x05 && buf[9] == 0x06 && buf[10] == 0x07 && buf[11] == 0x08 &&
          buf[12] == 0x09 && buf[13] == 0x0A && buf[14] == 0x0B && buf[15] == 0x0C);
    // timestamp at [16..19]
    CHECK(buf[16] == 0x0D && buf[17] == 0x0E && buf[18] == 0x0F && buf[19] == 0x10);
}

int main() {
    test_header_round_trip();
    test_empty_payload_packet();
    test_payload_packet();
    test_udp_binding_packet();
    test_parse_too_short();
    test_big_endian_layout();

    if (g_failures == 0) {
        std::printf("voice_frame: all tests passed\n");
        return 0;
    }
    std::printf("voice_frame: %d test(s) FAILED\n", g_failures);
    return 1;
}
