#ifndef W3CS_PROTOCOL_H
#define W3CS_PROTOCOL_H

#include <stdint.h>

#define W3CS_VERSION 1u
/* GStreamer's SCTP send path caps one message below 64 KiB including our
 * envelope. A 60 KiB payload leaves headroom and replaces five 12 KiB
 * messages for a typical WC3 frame. */
#define W3CS_MAX_FRAGMENT (60u * 1024u)
#define W3CS_MAX_FRAME (4u * 1024u * 1024u)

enum w3cs_kind {
    W3CS_HELLO = 1,
    W3CS_RESOURCE = 2,
    W3CS_FRAME = 3,
    W3CS_REPAIR = 4,
    W3CS_ACK = 5,
    W3CS_ERROR = 6,
    /* A bundle contains a SHA-256 digest followed by resource records. A
     * reference contains the same digest and the decoded byte count. */
    W3CS_RESOURCE_BUNDLE = 7,
    W3CS_RESOURCE_REFERENCE = 8
};

enum w3cs_flag {
    W3CS_COMPRESSED = 1u,
    W3CS_KEYFRAME = 2u,
    W3CS_LAST = 4u,
    /* A disposable geometry anchor is independently decodable against the
     * reliable recovery epoch. The following short GOP can delta against it. */
    W3CS_GEOMETRY_ANCHOR = 8u
};

enum w3cs_opcode {
    W3CS_OP_FRAME_STATE = 1,
    W3CS_OP_CREATE_BUFFER = 2,
    W3CS_OP_UPDATE_BUFFER = 3,
    W3CS_OP_CREATE_TEXTURE = 4,
    W3CS_OP_UPDATE_TEXTURE = 5,
    W3CS_OP_DESTROY_RESOURCE = 6,
    W3CS_OP_DEFINE_BLOB = 7,
    W3CS_OP_DEFINE_BLOB_DELTA = 8,
    W3CS_OP_DEFINE_BLOB_XOR_MASK = 9,
    W3CS_OP_RESET_BLOB_CACHE = 10,
    W3CS_OP_DEFINE_BLOB_FLOAT16_DELTA = 11,
    /* Texture pixels live in a content-addressed blob. This record binds the
     * stable pixel content to the session-local D3D texture identity. */
    W3CS_OP_UPDATE_TEXTURE_BLOB = 12,
    /* A viewer can attach while the persistent game is between Presents.
     * These markers let the relay discard in-flight updates from the prior
     * viewer and start the new reliable resource epoch at an exact boundary. */
    W3CS_OP_RESOURCE_SNAPSHOT_BEGIN = 13,
    W3CS_OP_RESOURCE_SNAPSHOT_END = 14,
    W3CS_OP_SET_RENDER_STATE = 16,
    W3CS_OP_SET_TRANSFORM = 17,
    W3CS_OP_SET_TEXTURE = 18,
    W3CS_OP_SET_FVF = 19,
    W3CS_OP_SET_STREAM_SOURCE = 20,
    W3CS_OP_SET_INDICES = 21,
    W3CS_OP_SET_MATERIAL = 22,
    W3CS_OP_SET_LIGHT = 23,
    W3CS_OP_LIGHT_ENABLE = 24,
    W3CS_OP_SET_TEXTURE_STAGE_STATE = 25,
    W3CS_OP_SET_SAMPLER_STATE = 26,
    W3CS_OP_SET_VIEWPORT = 27,
    W3CS_OP_SET_SCISSOR = 28,
    W3CS_OP_CLEAR = 29,
    W3CS_OP_SET_CURSOR = 30,
    W3CS_OP_SHOW_CURSOR = 31,
    W3CS_OP_DRAW_PRIMITIVE = 32,
    W3CS_OP_DRAW_INDEXED_PRIMITIVE = 33,
    /* WC3 changes D3DTS_WORLD before most draws. Keep translation as float32
     * and store the other affine components as IEEE binary16. */
    W3CS_OP_SET_WORLD_TRANSFORM_COMPACT = 34,
    /* Vertex delta with quantized positions. World/screen position words
     * carry error-feedback int16 deltas in steps of 1/64 (sub-pixel at any
     * sane camera; the encoder interns its own reconstruction as the next
     * chain base, so quantization error can never accumulate or oscillate).
     * The z and rhw words of transformed vertices keep the exact raw path:
     * their semantic range is tiny, so an absolute step is wrong for them.
     * A mode byte elides whole sections; see w3cs_define_blob_quant_delta. */
    W3CS_OP_DEFINE_BLOB_QUANT_DELTA = 35,
    /* Sparse XOR update of one FULL texture level against the content the
     * browser currently holds (fog-of-war style textures re-upload nearly
     * identical pixels: measured 99.9%+ unchanged between updates, so a
     * ~124 KB compressed full update becomes ~100 bytes). Payload after the
     * header: 4-bit-per-word change mask ((words + 1) / 2 bytes, the
     * DEFINE_BLOB_XOR_MASK layout) followed by the changed bytes. XOR is
     * NOT idempotent, so the record is CRC-gated: the browser verifies
     * base_crc32 against its current level bytes before applying and
     * result_crc32 after; any mismatch skips the record and requests a
     * repair, and the encoder ships a periodic full update as a keyframe,
     * so desync can never corrupt pixels. */
    W3CS_OP_UPDATE_TEXTURE_XOR = 36,
    /* Assert that the browser already holds this exact blob (id, size,
     * and both hashes; payload is w3cs_define_blob alone, 24 bytes).
     * Measured need: looping animations return to identical poses every
     * few frames, and each return re-paid the full delta cost - 118 MiB
     * of one game's defines targeted ids defined moments earlier. The
     * browser retains blobs drawn within its last 16 APPLIED frames (plus
     * the last anchor set); the encoder only references blobs drawn
     * within its last 8 CAPTURED frames of the same recovery epoch, and
     * applied frames are a subset of captured frames, so an eligible
     * reference is always still held - under any relay drop pattern.
     * Keyframes and geometry anchors never use references: they stay
     * self-sufficient so a reference broken by an out-of-window drop is
     * healed at the next anchor, exactly like a broken delta chain. A
     * reference to a missing blob is the existing missing-dependency
     * path (frame dropped, repair requested). */
    W3CS_OP_DEFINE_BLOB_REF = 37
};

/* Section layout after w3cs_define_blob_quant_delta, in this order:
 *   pos_mask    ((pos_words_total + 7) / 8)      absent with DENSE_POS
 *   pos_values  int16 per changed (or all, DENSE_POS) position word
 *   octa_mask   ((size / stride + 7) / 8)        present with OCTA_NORMALS
 *   octa_values 2 bytes (int8 u, int8 v) per octa_mask bit
 *   float_mask  ((words + 7) / 8)                absent with NO_FLOAT
 *   half_values 2 bytes per float_mask bit
 *   raw_mask    4 bits per word                  absent with NO_RAW
 *   raw_values  1 byte per raw_mask bit
 * pos_words_total = (size / stride) * quantized position words, where XYZ
 * layouts quantize 3 words and XYZRHW quantizes only x and y (2 words).
 * Application order: position adds, then octahedral normal writes
 * (absolute, words 3..5 of each masked vertex), then half adds, then
 * raw XOR.
 *
 * OCTA_NORMALS ships a changed near-unit normal as one octahedral point
 * (int8 u, v in [-127, 127]) instead of three float16 component deltas -
 * measured 176.5 MiB of half deltas (~28% of one real game's entire
 * encoded wire), because CPU-skinned meshes rotate ~80% of their normals
 * every frame. Both ends reconstruct the SAME float32 unit vector
 * (shared decode: fold, then normalize; one float32 rounding per step),
 * and the encoder interns that reconstruction as the next chain base, so
 * like positions the error is fed back and can never accumulate. A
 * vertex whose normal is degenerate, non-finite, not near unit length,
 * or outside the angular tolerance keeps the classic float16/raw path -
 * the sections mix freely per vertex. */
enum w3cs_quant_mode {
    W3CS_QUANT_DENSE_POS = 1u,
    W3CS_QUANT_NO_RAW = 2u,
    W3CS_QUANT_NO_FLOAT = 4u,
    W3CS_QUANT_OCTA_NORMALS = 8u
};
#define W3CS_QUANT_POSITION_STEP 0.015625f
/* Encoder-side gate for the quantized record: WC3 draws its ENTIRE UI
 * (menus, minimap square, cursor quad) as XYZ geometry on a 0..0.8
 * coordinate plane, indistinguishable from world content by FVF. Quantize
 * only slices whose peak |position| says world scale; the 1/64 step that
 * is sub-pixel for world coordinates in the thousands would snap the UI
 * plane to a ~2%-of-screen grid (observed live, twice). */
#define W3CS_QUANT_WORLD_MIN_SPAN 16.0f

#pragma pack(push, 1)
struct w3cs_envelope {
    uint8_t magic[4];
    uint8_t version;
    uint8_t kind;
    uint16_t flags;
    uint32_t session;
    uint32_t sequence;
    uint32_t frame;
    uint16_t fragment_index;
    uint16_t fragment_count;
    uint32_t payload_size;
    uint32_t payload_crc32;
};

struct w3cs_record {
    uint8_t opcode;
    uint8_t flags;
    uint16_t reserved;
    uint32_t payload_size;
};

struct w3cs_create_buffer {
    uint32_t id;
    uint32_t generation;
    uint32_t size;
    uint32_t usage;
    uint32_t format_or_fvf;
    uint32_t pool;
    uint8_t kind;
};

struct w3cs_update_buffer {
    uint32_t id;
    uint32_t generation;
    uint32_t offset;
    uint32_t size;
};

struct w3cs_create_texture {
    uint32_t id;
    uint32_t generation;
    uint32_t width;
    uint32_t height;
    uint32_t levels;
    uint32_t usage;
    uint32_t format;
    uint32_t pool;
};

struct w3cs_update_texture {
    uint32_t id;
    uint32_t generation;
    uint32_t level;
    uint32_t x;
    uint32_t y;
    uint32_t width;
    uint32_t height;
    uint32_t pitch;
    uint32_t format;
    uint32_t size;
};

struct w3cs_update_texture_blob {
    struct w3cs_update_texture texture;
    uint32_t blob_id;
};

struct w3cs_update_texture_xor {
    struct w3cs_update_texture texture;  /* always the full level: x = y = 0,
                                          * size = full level byte count */
    uint32_t base_crc32;
    uint32_t result_crc32;
};

struct w3cs_set_cursor {
    uint32_t hot_x;
    uint32_t hot_y;
    uint32_t width;
    uint32_t height;
    uint32_t pitch;
    uint32_t format;
    uint32_t size;
};

struct w3cs_define_blob {
    uint32_t id;
    uint32_t size;
    uint64_t hash_a;
    uint64_t hash_b;
};

struct w3cs_define_blob_delta {
    struct w3cs_define_blob blob;
    uint32_t base_id;
};

struct w3cs_define_blob_float16_delta {
    struct w3cs_define_blob_delta delta;
    uint32_t stride;
    uint32_t fvf;
};

struct w3cs_define_blob_quant_delta {
    struct w3cs_define_blob_float16_delta base;
    uint8_t mode;
    uint8_t reserved[3];
};
#pragma pack(pop)

#endif
