/**
 * @file protocol.h
 * @brief SkyLink 多平台实时视频传输 - 公共协议定义
 * 
 * 这个文件定义了机载端（ROS 2）、PC 端（Qt）和移动端（Android）之间的通信协议。
 * 必须保持三端的字节对齐和字段顺序一致。
 * 
 * @author flowzl
 * @date 2026-01-19
 */

#pragma once

#include <cstdint>
#include <cstring>

/**
 * @brief 强制1字节对齐，避免编译器补齐导致的大小差异
 */
#pragma pack(push, 1)

/**
 * @struct PacketHeader
 * @brief UDP 数据包头部结构
 * 
 * 总大小: 50 字节
 * - 报头识别: 2 字节 (Magic)
 * - 帧标识: 4 字节 (FrameID)
 * - 分片信息: 4 字节 (TotalChunks + ChunkIndex)
 * - 数据长度: 2 字节 (DataLen)
 * - 时间戳: 8 字节 (Timestamp)
 * - GPS 位置: 24 字节 (Lat, Lon, Alt)
 * - 预留: 6 字节 (Reserve 用于将来扩展)
 */
struct PacketHeader {
    // ============ 包头识别 ============
    uint16_t magic;              ///< 魔数 0xAA55 用于识别合法数据包
    
    // ============ 帧标识 ============
    uint32_t frame_id;           ///< 唯一的帧编号，从 0 开始递增，用于帧同步
    
    // ============ 分片信息 ============
    uint16_t total_chunks;       ///< 该帧总共被分成多少个 UDP 包
    uint16_t chunk_index;        ///< 当前数据包是第几个分片 (0-based)
    
    // ============ 载荷信息 ============
    uint16_t data_len;           ///< 当前包的有效载荷长度（单位：字节）
    
    // ============ 时间戳 ============
    double timestamp;            ///< GPS 时间戳 (秒数，Unix 时间 或 ROS 时间)
    
    // ============ GPS 位置数据 ============
    double lat;                  ///< 纬度 (WGS84 坐标系)
    double lon;                  ///< 经度 (WGS84 坐标系)
    double alt;                  ///< 海拔高度 (米)
    
    // ============ 校验与预留 ============
    uint16_t crc16;              ///< CRC16 校验码 (可选，当前未使用)
    uint32_t reserve;            ///< 保留字段，用于将来的扩展
    
    /**
     * @brief 获取包头大小（字节数）
     * @return 包头固定大小：50 字节
     */
    static constexpr uint16_t size() {
        return sizeof(PacketHeader);
    }
};

/**
 * @brief 设备信息包头结构（UDP 10000）
 *
 * 用于传输设备参数、帧率、像素格式等信息。
 */
struct DeviceInfoPacket {
    uint16_t magic;        ///< 魔数 0xAA56
    uint32_t sequence;     ///< 序列号
    uint16_t width;        ///< 图像宽度
    uint16_t height;       ///< 图像高度
    float framerate;       ///< 帧率
    uint32_t pixel_format; ///< 像素格式（V4L2 FourCC）
    char device_path[32];  ///< 设备路径（/dev/videoX）
    char device_name[64];  ///< 设备名称
    float current_fps;     ///< 当前实际 FPS
    uint32_t total_frames; ///< 总帧数
    uint64_t timestamp;    ///< 时间戳（毫秒）
    uint16_t crc16;        ///< CRC 校验（可选）

    static constexpr uint16_t size() {
        return sizeof(DeviceInfoPacket);
    }
};

/**
 * @brief 恢复默认的对齐方式
 */
#pragma pack(pop)

// ============================================================================
// 协议常量定义
// ============================================================================

/** @brief 数据包魔数，用于识别合法的 SkyLink 数据包 */
constexpr uint16_t SKYLINK_MAGIC = 0xAA55;

/** @brief 设备信息魔数，用于识别设备信息数据包 */
constexpr uint16_t DEVICE_INFO_MAGIC = 0xAA56;

/** @brief 设备信息请求魔数，用于请求立即发送设备信息 */
constexpr uint16_t DEVICE_INFO_REQUEST = 0xAA57;

/** @brief 最大的单个 UDP 包大小 (包含报头) - MTU 1500 字节 */
constexpr uint16_t MAX_UDP_PACKET_SIZE = 1472;  // 1500 - 20 (IP) - 8 (UDP)

/** @brief 最大的载荷大小 */
constexpr uint16_t MAX_PAYLOAD_SIZE = MAX_UDP_PACKET_SIZE - PacketHeader::size();

/** @brief UDP 接收缓冲区大小 */
constexpr uint32_t RX_BUFFER_SIZE = 65536;

/** @brief 最大支持的分片数 */
constexpr uint16_t MAX_CHUNKS = 1000;

/** @brief 帧超时时间（毫秒），超过此时间的分片将被丢弃 */
constexpr uint32_t FRAME_TIMEOUT_MS = 5000;

// ============================================================================
// 图像编码相关常量
// ============================================================================

/** @brief JPEG 图像格式 ID */
constexpr uint8_t IMAGE_FORMAT_JPEG = 0x01;

/** @brief JPEG 最大压缩质量 */
constexpr uint8_t JPEG_MAX_QUALITY = 100;

/** @brief JPEG 最小压缩质量 */
constexpr uint8_t JPEG_MIN_QUALITY = 10;

// ============================================================================
// 辅助函数
// ============================================================================

/**
 * @brief 验证包头魔数是否合法
 * @param header 待验证的包头结构
 * @return 如果魔数为 0xAA55 返回 true，否则返回 false
 */
inline bool isValidPacket(const PacketHeader& header) {
    return header.magic == SKYLINK_MAGIC;
}

/**
 * @brief 计算 CRC16 校验码 (可选)
 * @param data 数据指针
 * @param len 数据长度
 * @return 16 位 CRC 校验结果
 */
inline uint16_t crc16_ccitt(const uint8_t* data, uint16_t len) {
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < len; i++) {
        crc ^= (uint16_t)data[i] << 8;
        for (int j = 0; j < 8; j++) {
            if (crc & 0x8000) {
                crc = (crc << 1) ^ 0x1021;
            } else {
                crc = crc << 1;
            }
            crc &= 0xFFFF;
        }
    }
    return crc;
}

