#ifndef W3CS_QUALITY_H
#define W3CS_QUALITY_H

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

#ifdef __cplusplus
extern "C" {
#endif

/* Level 10 uses these two values in the accepted capture. Keep them stable. */
#define W3CS_QUALITY_CURRENT_POSITION_STEP 0.015625f
#define W3CS_QUALITY_WORLD_MIN_SPAN 16.0f
/* Cache/encoder sentinel: retain the accepted level-10 vertex codec for a
 * lower-quality draw that is not safe for profile-specific loss. This is
 * distinct from -1 (the byte-exact classic path) and 0 (profile loss is
 * permitted, although its position step remains 1/64). */
#define W3CS_QUALITY_POSITION_PROTECTED 15

enum w3cs_quality_geometry_class {
    W3CS_QUALITY_WORLD_XYZ = 0,
    W3CS_QUALITY_SCREEN_XYZRHW = 1,
    W3CS_QUALITY_PROTECTED_UI = 2,
    W3CS_QUALITY_UNKNOWN_GEOMETRY = 3
};

/* The encoder does not change any item marked here. */
enum w3cs_quality_preserve_flag {
    W3CS_QUALITY_PRESERVE_XYZRHW = 1u << 0,
    W3CS_QUALITY_PRESERVE_UI = 1u << 1,
    W3CS_QUALITY_PRESERVE_TEXTURES = 1u << 2,
    W3CS_QUALITY_PRESERVE_TOPOLOGY = 1u << 3,
    W3CS_QUALITY_PRESERVE_DRAW_ORDER = 1u << 4
};

struct w3cs_quality_profile {
    uint8_t level;
    /* A transmitted position residual stays in 1/64-unit decoder units.
     * Lower levels only force its low N bits to zero. This remains compatible
     * with the current opcode-35 decoder and makes the stream easier to pack. */
    uint8_t max_position_drop_bits;
    uint8_t octa_normal_bits;
    /* Clear this many low IEEE binary16 mantissa bits after the established
     * float16-delta acceptance gate. Zero preserves the accepted codec. */
    uint8_t half_mantissa_drop_bits;
    uint8_t anchor_interval_kept_frames;
    uint16_t preserve_flags;
    /* Zero means the accepted fixed-step level-10 behavior. Other levels
     * choose the coarsest step whose projected corner bound stays below this
     * value. */
    float maximum_position_error_px;
    float normal_hold_degrees;
    float maximum_normal_error_degrees;
    float normal_hold_cos_squared;
    float normal_minimum_dot;
};

const struct w3cs_quality_profile *w3cs_quality_profile(unsigned level);

/* Apply the profile's canonical half-delta precision. The decoder receives
 * the returned binary16 value directly; the encoder must feed its float32
 * reconstruction back as the next base. Level 10 is byte-identical. */
uint16_t w3cs_quality_coarsen_half_delta(
    const struct w3cs_quality_profile *profile,
    uint16_t half_delta);

/* True only when PIXELS is a non-empty, tightly packed array whose texels
 * are byte-identical. The proxy uses this content test for uncompressed
 * fixed-function textures. Unknown and block-compressed layouts are not
 * passed here. */
bool w3cs_quality_texels_are_uniform(
    const void *pixels,
    size_t size_bytes,
    size_t bytes_per_texel);

/* D3D row-vector convention. The array is row-major and projection is
 * [x y z 1] * object_to_clip. */
struct w3cs_quality_projection {
    float object_to_clip[16];
    float viewport_x;
    float viewport_y;
    float viewport_width;
    float viewport_height;
};

/* Compose D3D's row-vector World * View * Projection transform and attach
 * the active viewport. False means the caller must keep the classic path. */
bool w3cs_quality_compose_projection(
    const float world[16],
    const float view[16],
    const float projection[16],
    float viewport_x,
    float viewport_y,
    float viewport_width,
    float viewport_height,
    struct w3cs_quality_projection *result);

struct w3cs_quality_position_batch {
    const void *vertices;
    uint32_t vertex_count;
    uint32_t stride_bytes;
    uint32_t position_offset_bytes;
    enum w3cs_quality_geometry_class geometry_class;
    bool protected_content;
    const struct w3cs_quality_projection *projection;
};

/* Return -1 to use the exact classic path. Return 0..9 to quantize residuals
 * in multiples of (1/64 * 2^return_value). The level-10 result is always 0
 * for eligible world geometry, which preserves the accepted encoder output. */
int w3cs_quality_choose_position_drop_bits(
    const struct w3cs_quality_profile *profile,
    const struct w3cs_quality_position_batch *batch,
    float *chosen_step,
    float *worst_projected_error_px);

/* A depth-writing draw can be coplanar with a later alpha pass, and an alpha
 * draw can compare against an earlier coplanar depth value. A small position
 * change can then expose a complete triangle even when projected x/y error is
 * sub-pixel. Keep the accepted 1/64 position step for these draw classes.
 * Other lower-profile attributes remain eligible. */
int w3cs_quality_clamp_depth_sensitive_position_drop(
    int selected_drop_bits,
    bool depth_test_enabled,
    bool depth_write_enabled,
    bool alpha_test_enabled,
    bool alpha_blend_enabled);

/* -1 denotes the byte-exact classic path. Values 0..14 permit the lower
 * profile and name its position precision. W3CS_QUALITY_POSITION_PROTECTED
 * denotes the accepted level-10 vertex codec with all lower-profile loss
 * disabled. A cached reconstruction is safe for a later draw only when its
 * position precision and profile-loss class satisfy that draw. */
bool w3cs_quality_position_precision_satisfies(
    int cached_drop_bits,
    int required_drop_bits);

/* Measure a conservative projected displacement for a quantization cell. It
 * combines a perspective denominator bound with all eight cell corners. */
bool w3cs_quality_measure_position_error_px(
    const struct w3cs_quality_position_batch *batch,
    unsigned drop_bits,
    float *worst_projected_error_px);

/* Quantize one scalar against the decoder's reconstructed base. wire_quantum
 * is still expressed in 1/64 units. The caller must intern reconstructed,
 * not current, as the next base. A zero quantum is a valid held value. */
bool w3cs_quality_quantize_position(float current, float base_reconstructed,
                                    unsigned drop_bits,
                                    int16_t *wire_quantum,
                                    float *reconstructed);

enum w3cs_quality_normal_action {
    W3CS_QUALITY_NORMAL_EXACT = 0,
    W3CS_QUALITY_NORMAL_HOLD = 1,
    W3CS_QUALITY_NORMAL_EMIT = 2
};

struct w3cs_quality_normal_result {
    enum w3cs_quality_normal_action action;
    int8_t octa_u;
    int8_t octa_v;
    float reconstructed[3];
    float angular_error_degrees;
};

/* The returned int8 pair uses the current /127 decoder. Fewer logical bits
 * snap that pair to a smaller codebook. No protocol revision is necessary.
 * Pass the prior decoder reconstruction for deterministic deadband feedback. */
bool w3cs_quality_encode_normal(
    const struct w3cs_quality_profile *profile,
    const float current[3],
    const float previous_reconstructed[3],
    bool have_previous,
    struct w3cs_quality_normal_result *result);

enum w3cs_quality_frame_flag {
    W3CS_QUALITY_FRAME_RECOVERY = 1u << 0,
    W3CS_QUALITY_FRAME_GEOMETRY_ANCHOR = 1u << 1,
    W3CS_QUALITY_FRAME_FORCED = 1u << 2
};

enum w3cs_quality_frame_reason {
    W3CS_QUALITY_FRAME_DROPPED = 0,
    W3CS_QUALITY_FRAME_DUE = 1,
    W3CS_QUALITY_FRAME_PASSTHROUGH = 2,
    W3CS_QUALITY_FRAME_FORCED_KEEP = 3,
    W3CS_QUALITY_FRAME_CLOCK_RESET = 4
};

/* Standalone cadence experiment support. The production proxy does not call
 * these helpers and preserves every source Present. */

struct w3cs_quality_cadence {
    uint32_t source_fps;
    uint32_t target_fps;
    uint64_t next_due_ns;
    uint32_t period_remainder;
    uint64_t last_timestamp_ns;
    bool initialized;
};

struct w3cs_quality_frame_decision {
    bool keep;
    /* At reduced cadence, a kept frame must be re-encoded against a reliable
     * recovery or the current geometry anchor. It must never depend on a
     * frame that the cadence filter could drop. */
    bool require_anchor_base;
    enum w3cs_quality_frame_reason reason;
};

void w3cs_quality_cadence_init(
    struct w3cs_quality_cadence *cadence,
    uint32_t source_fps,
    uint32_t target_fps);

struct w3cs_quality_frame_decision w3cs_quality_cadence_decide(
    struct w3cs_quality_cadence *cadence,
    uint64_t timestamp_ns,
    uint32_t frame_flags);

/* Call this before encoding a kept frame. Reset kept_since_anchor after an
 * anchor is emitted. The first kept frame is always an anchor when no valid
 * anchor exists. */
bool w3cs_quality_should_make_anchor(
    const struct w3cs_quality_profile *profile,
    uint32_t kept_since_anchor,
    bool have_valid_anchor);

struct w3cs_quality_bandwidth_estimate {
    uint8_t level;
    uint8_t position_drop_bits;
    uint64_t projected_total_bytes;
    double projected_ratio;
    double projected_saving_percent;
    double modeled_position_value_saving;
    bool position_model_extrapolated;
};

/* A spatial-only planning model for the accepted 9.973567-second sample.
 * Every quality level preserves all input frames. Position savings use the
 * measured 14.119-bit entropy of current residuals and credit no normal
 * deadband gain. It assumes eligible draws reach the profile maximum. It is
 * not a replacement for post-zstd A/B. */
bool w3cs_quality_estimate_accepted_capture(
    unsigned level,
    struct w3cs_quality_bandwidth_estimate *estimate);

#ifdef __cplusplus
}
#endif

#endif
