// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

/**
 * @file h3_frame.h
 * @brief HTTP/3 framing (RFC 9114 sec 7) over QUIC varints.
 *
 * An HTTP/3 frame is `Type (varint) | Length (varint) | Frame Payload`. This module parses that
 * header and builds the frames a server uses (DATA, HEADERS carrying a QPACK field section,
 * SETTINGS, GOAWAY), reads a SETTINGS payload, and flags the reserved HTTP/2 frame types that
 * must be treated as a connection error. Pure and host-tested.
 *
 * @author  Douglas Quigg (dstroy0)
 * @date    2026
 */

#ifndef DETERMINISTICESPASYNCWEBSERVER_H3_FRAME_H
#define DETERMINISTICESPASYNCWEBSERVER_H3_FRAME_H

#include "ServerConfig.h"

#if DETWS_ENABLE_HTTP3

#include <stddef.h>
#include <stdint.h>

/** @brief HTTP/3 frame types (RFC 9114 sec 7.2 / 11.2.1). */
enum
{
    H3_DATA = 0x00,
    H3_HEADERS = 0x01,
    H3_CANCEL_PUSH = 0x03,
    H3_SETTINGS = 0x04,
    H3_PUSH_PROMISE = 0x05,
    H3_GOAWAY = 0x07,
    H3_MAX_PUSH_ID = 0x0d,
};

/** @brief SETTINGS parameter identifiers (RFC 9114 sec 7.2.4.1 + RFC 9204). */
enum
{
    H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY = 0x01,
    H3_SETTINGS_MAX_FIELD_SECTION_SIZE = 0x06,
    H3_SETTINGS_QPACK_BLOCKED_STREAMS = 0x07,
};

/** @brief A parsed frame header (payload begins at buf + header_len). */
struct H3Frame
{
    uint64_t type;     ///< frame type
    uint64_t length;   ///< payload length
    size_t header_len; ///< bytes of the type + length varints
};

/** @brief The settings we track, with defaults after h3_settings_defaults(). */
struct H3Settings
{
    uint64_t qpack_max_table_capacity; ///< default 0
    uint64_t max_field_section_size;   ///< default "unlimited"
    uint64_t qpack_blocked_streams;    ///< default 0
};

/** @brief Parse a frame header (type + length varints) at @p buf. @return false if truncated. */
bool h3_frame_parse(const uint8_t *buf, size_t len, H3Frame *out);

/** @brief Write a frame header (type + length varints). @return bytes written, or 0 on overflow. */
size_t h3_frame_write_header(uint8_t *out, size_t cap, uint64_t type, uint64_t length);

/** @brief True if @p type is a reserved HTTP/2 frame type (0x02/0x06/0x08/0x09) - a connection error. */
bool h3_frame_type_reserved(uint64_t type);

/** @brief Fill @p s with the RFC default settings. */
void h3_settings_defaults(H3Settings *s);
/** @brief Apply a SETTINGS payload (id, value varint pairs) to @p s. @return false if malformed. */
bool h3_parse_settings(const uint8_t *payload, size_t len, H3Settings *s);

// --- Frame builders (write a complete frame including its header) -----------------------------

/** @brief DATA frame wrapping @p data. */
size_t h3_build_data(uint8_t *out, size_t cap, const uint8_t *data, size_t len);
/** @brief HEADERS frame wrapping a QPACK-encoded field section @p block. */
size_t h3_build_headers(uint8_t *out, size_t cap, const uint8_t *block, size_t len);
/** @brief SETTINGS frame from @p n (id, value) pairs. */
size_t h3_build_settings(uint8_t *out, size_t cap, const uint64_t *ids, const uint64_t *vals, size_t n);
/** @brief GOAWAY frame carrying @p stream_id (RFC 9114 sec 7.2.6). */
size_t h3_build_goaway(uint8_t *out, size_t cap, uint64_t stream_id);

#endif // DETWS_ENABLE_HTTP3
#endif // DETERMINISTICESPASYNCWEBSERVER_H3_FRAME_H
