#include "w3cs_quality.h"

#include <float.h>
#include <math.h>
#include <string.h>

#define W3CS_QUALITY_PI 3.14159265358979323846
#define W3CS_QUALITY_NS_PER_SECOND UINT64_C(1000000000)

/* Indexed by quality level. Index zero is intentionally invalid. Level 10
 * matches the accepted capture: 1/64 position residuals, current int8 octa
 * normals, the 0.9999 deadband, and no cadence reduction. */
static const struct w3cs_quality_profile g_profiles[11] = {
    {0},
    {1, 9, 4, 6, 8, 0x1fu, 3.25f, 5.0f, 12.0f,
     0.992403877f, 0.978147601f},
    {2, 8, 4, 0, 8, 0x1fu, 2.25f, 3.8f, 11.0f,
     0.995607770f, 0.981627183f},
    {3, 7, 5, 0, 8, 0x1fu, 1.50f, 2.8f, 6.8f,
     0.997613700f, 0.992965508f},
    {4, 6, 5, 0, 8, 0x1fu, 1.10f, 2.3f, 6.0f,
     0.998389439f, 0.994521895f},
    {5, 5, 6, 0, 8, 0x1fu, 0.80f, 1.8f, 3.8f,
     0.999013364f, 0.997801468f},
    {6, 4, 6, 0, 8, 0x1fu, 0.55f, 1.4f, 3.2f,
     0.999403069f, 0.998440764f},
    {7, 3, 7, 0, 8, 0x1fu, 0.35f, 1.1f, 2.1f,
     0.999631458f, 0.999328394f},
    {8, 2, 7, 0, 8, 0x1fu, 0.20f, 0.9f, 1.8f,
     0.999753280f, 0.999506560f},
    {9, 1, 8, 0, 8, 0x1fu, 0.10f, 0.65f, 1.25f,
     0.999871305f, 0.999762027f},
    {10, 0, 8, 0, 8, 0x1fu, 0.0f, 0.57296f, 1.14593f,
     0.9999f, 0.9998f}
};

const struct w3cs_quality_profile *w3cs_quality_profile(unsigned level)
{
    if (level < 1u || level > 10u)
        return NULL;
    return &g_profiles[level];
}

uint16_t w3cs_quality_coarsen_half_delta(
    const struct w3cs_quality_profile *profile,
    uint16_t half_delta)
{
    unsigned bits;
    uint16_t mask;
    if (!profile)
        return half_delta;
    bits = profile->half_mantissa_drop_bits;
    if (!bits)
        return half_delta;
    if (bits > 10u)
        bits = 10u;
    mask = (uint16_t)((1u << bits) - 1u);
    return (uint16_t)(half_delta & (uint16_t)~mask);
}

bool w3cs_quality_texels_are_uniform(
    const void *pixels,
    size_t size_bytes,
    size_t bytes_per_texel)
{
    const uint8_t *bytes = (const uint8_t *)pixels;
    size_t offset;
    if (!bytes || !size_bytes || !bytes_per_texel
            || size_bytes % bytes_per_texel)
        return false;
    for (offset = bytes_per_texel; offset < size_bytes;
         offset += bytes_per_texel)
        if (memcmp(bytes, bytes + offset, bytes_per_texel) != 0)
            return false;
    return true;
}

static bool finite_projection(const struct w3cs_quality_projection *p)
{
    unsigned i;
    if (!p || !isfinite(p->viewport_x) || !isfinite(p->viewport_y)
            || !isfinite(p->viewport_width)
            || !isfinite(p->viewport_height)
            || !(p->viewport_width > 0.0f)
            || !(p->viewport_height > 0.0f))
        return false;
    for (i = 0; i < 16u; ++i)
        if (!isfinite(p->object_to_clip[i]))
            return false;
    return true;
}

static bool multiply_matrix(const float left[16], const float right[16],
                            float result[16])
{
    unsigned row;
    unsigned column;
    if (!left || !right || !result)
        return false;
    for (row = 0; row < 4u; ++row) {
        for (column = 0; column < 4u; ++column) {
            double value = 0.0;
            unsigned inner;
            for (inner = 0; inner < 4u; ++inner) {
                float a = left[row * 4u + inner];
                float b = right[inner * 4u + column];
                if (!isfinite(a) || !isfinite(b))
                    return false;
                value += (double)a * (double)b;
            }
            if (!isfinite(value) || value > FLT_MAX || value < -FLT_MAX)
                return false;
            result[row * 4u + column] = (float)value;
            if (!isfinite(result[row * 4u + column]))
                return false;
        }
    }
    return true;
}

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)
{
    float world_view[16];
    struct w3cs_quality_projection candidate;
    if (!result || !isfinite(viewport_x) || !isfinite(viewport_y)
            || !isfinite(viewport_width) || !isfinite(viewport_height)
            || !(viewport_width > 0.0f) || !(viewport_height > 0.0f))
        return false;
    if (!multiply_matrix(world, view, world_view)
            || !multiply_matrix(world_view, projection,
                                candidate.object_to_clip))
        return false;
    candidate.viewport_x = viewport_x;
    candidate.viewport_y = viewport_y;
    candidate.viewport_width = viewport_width;
    candidate.viewport_height = viewport_height;
    if (!finite_projection(&candidate))
        return false;
    *result = candidate;
    return true;
}

bool w3cs_quality_position_precision_satisfies(
    int cached_drop_bits,
    int required_drop_bits)
{
    if (cached_drop_bits < -1
            || cached_drop_bits > W3CS_QUALITY_POSITION_PROTECTED
            || required_drop_bits < -1
            || required_drop_bits > W3CS_QUALITY_POSITION_PROTECTED)
        return false;
    if (cached_drop_bits == -1)
        return true;
    if (required_drop_bits == -1)
        return false;
    if (cached_drop_bits == W3CS_QUALITY_POSITION_PROTECTED)
        return true;
    if (required_drop_bits == W3CS_QUALITY_POSITION_PROTECTED)
        return false;
    return cached_drop_bits <= required_drop_bits;
}

static bool read_position(const struct w3cs_quality_position_batch *batch,
                          uint32_t vertex, float out[3])
{
    const uint8_t *bytes;
    uint64_t offset;
    if (!batch || !batch->vertices || vertex >= batch->vertex_count
            || batch->stride_bytes < 12u
            || batch->position_offset_bytes > batch->stride_bytes - 12u)
        return false;
    offset = (uint64_t)vertex * batch->stride_bytes
        + batch->position_offset_bytes;
    if (offset > SIZE_MAX - 12u)
        return false;
    bytes = (const uint8_t *)batch->vertices + (size_t)offset;
    memcpy(out, bytes, 12u);
    return isfinite(out[0]) && isfinite(out[1]) && isfinite(out[2]);
}

static bool project_point(const struct w3cs_quality_projection *p,
                          const float point[3], float screen[2])
{
    const float *m = p->object_to_clip;
    float clip_x = point[0] * m[0] + point[1] * m[4]
        + point[2] * m[8] + m[12];
    float clip_y = point[0] * m[1] + point[1] * m[5]
        + point[2] * m[9] + m[13];
    float clip_z = point[0] * m[2] + point[1] * m[6]
        + point[2] * m[10] + m[14];
    float clip_w = point[0] * m[3] + point[1] * m[7]
        + point[2] * m[11] + m[15];
    float ndc_x;
    float ndc_y;
    /* D3D's homogeneous depth volume is 0 <= z <= w. Requiring both the
     * source point and every error-cell corner to remain inside it prevents
     * lower precision from moving a vertex across the near/far clip plane.
     * X/Y edge clipping can still move by the explicitly bounded pixel
     * error; depth-plane crossing can instead create or remove triangles. */
    if (!isfinite(clip_x) || !isfinite(clip_y) || !isfinite(clip_z)
            || !isfinite(clip_w) || !(clip_w > 1e-6f)
            || clip_z < 0.0f || clip_z > clip_w)
        return false;
    ndc_x = clip_x / clip_w;
    ndc_y = clip_y / clip_w;
    screen[0] = p->viewport_x + (ndc_x + 1.0f)
        * 0.5f * p->viewport_width;
    screen[1] = p->viewport_y + (1.0f - ndc_y)
        * 0.5f * p->viewport_height;
    return isfinite(screen[0]) && isfinite(screen[1]);
}

static bool perspective_error_bound(
    const struct w3cs_quality_projection *p,
    const float point[3],
    const float half_error[3],
    float *error_px)
{
    const float *m = p->object_to_clip;
    double clip_x = (double)point[0] * m[0] + (double)point[1] * m[4]
        + (double)point[2] * m[8] + m[12];
    double clip_y = (double)point[0] * m[1] + (double)point[1] * m[5]
        + (double)point[2] * m[9] + m[13];
    double clip_w = (double)point[0] * m[3] + (double)point[1] * m[7]
        + (double)point[2] * m[11] + m[15];
    double delta_x = (double)half_error[0] * fabs((double)m[0])
        + (double)half_error[1] * fabs((double)m[4])
        + (double)half_error[2] * fabs((double)m[8]);
    double delta_y = (double)half_error[0] * fabs((double)m[1])
        + (double)half_error[1] * fabs((double)m[5])
        + (double)half_error[2] * fabs((double)m[9]);
    double delta_w = (double)half_error[0] * fabs((double)m[3])
        + (double)half_error[1] * fabs((double)m[7])
        + (double)half_error[2] * fabs((double)m[11]);
    double minimum_w = clip_w - delta_w;
    double denominator;
    double ndc_x_bound;
    double ndc_y_bound;
    double pixel_x_bound;
    double pixel_y_bound;
    double combined;
    if (!isfinite(clip_x) || !isfinite(clip_y) || !isfinite(clip_w)
            || !(minimum_w > 1e-6))
        return false;
    denominator = clip_w * minimum_w;
    ndc_x_bound = (delta_x * clip_w + fabs(clip_x) * delta_w)
        / denominator;
    ndc_y_bound = (delta_y * clip_w + fabs(clip_y) * delta_w)
        / denominator;
    pixel_x_bound = 0.5 * p->viewport_width * ndc_x_bound;
    pixel_y_bound = 0.5 * p->viewport_height * ndc_y_bound;
    combined = sqrt(pixel_x_bound * pixel_x_bound
        + pixel_y_bound * pixel_y_bound);
    if (!isfinite(combined) || combined > FLT_MAX)
        return false;
    *error_px = (float)combined;
    return true;
}

bool w3cs_quality_measure_position_error_px(
    const struct w3cs_quality_position_batch *batch,
    unsigned drop_bits,
    float *worst_projected_error_px)
{
    float half_step;
    float worst = 0.0f;
    uint32_t vertex;
    if (worst_projected_error_px)
        *worst_projected_error_px = INFINITY;
    if (!batch || !batch->vertex_count || drop_bits > 14u
            || !finite_projection(batch->projection))
        return false;
    half_step = 0.5f * ldexpf(W3CS_QUALITY_CURRENT_POSITION_STEP,
                             (int)drop_bits);
    if (!isfinite(half_step) || !(half_step > 0.0f))
        return false;
    for (vertex = 0; vertex < batch->vertex_count; ++vertex) {
        float point[3];
        float half_error[3];
        float center[2];
        float analytic_error;
        unsigned corner;
        if (!read_position(batch, vertex, point)
                || !project_point(batch->projection, point, center))
            return false;
        /* The first term is the mathematical rounding cell. The second term
         * covers the float32 residual division and reconstruction add. */
        half_error[0] = half_step + 4.0f * FLT_EPSILON
            * (fabsf(point[0]) + half_step + 1.0f);
        half_error[1] = half_step + 4.0f * FLT_EPSILON
            * (fabsf(point[1]) + half_step + 1.0f);
        half_error[2] = half_step + 4.0f * FLT_EPSILON
            * (fabsf(point[2]) + half_step + 1.0f);
        if (!perspective_error_bound(batch->projection, point, half_error,
                                     &analytic_error))
            return false;
        if (analytic_error > worst)
            worst = analytic_error;
        for (corner = 0; corner < 8u; ++corner) {
            float displaced[3];
            float projected[2];
            float dx;
            float dy;
            float error;
            displaced[0] = point[0]
                + ((corner & 1u) ? half_error[0] : -half_error[0]);
            displaced[1] = point[1]
                + ((corner & 2u) ? half_error[1] : -half_error[1]);
            displaced[2] = point[2]
                + ((corner & 4u) ? half_error[2] : -half_error[2]);
            if (!project_point(batch->projection, displaced, projected))
                return false;
            dx = projected[0] - center[0];
            dy = projected[1] - center[1];
            error = sqrtf(dx * dx + dy * dy);
            if (!isfinite(error))
                return false;
            if (error > worst)
                worst = error;
        }
    }
    if (worst_projected_error_px)
        *worst_projected_error_px = worst;
    return true;
}

static bool eligible_world_batch(
    const struct w3cs_quality_position_batch *batch)
{
    float peak = 0.0f;
    uint32_t vertex;
    if (!batch || !batch->vertices || !batch->vertex_count
            || batch->protected_content
            || batch->geometry_class != W3CS_QUALITY_WORLD_XYZ)
        return false;
    for (vertex = 0; vertex < batch->vertex_count; ++vertex) {
        float point[3];
        unsigned axis;
        if (!read_position(batch, vertex, point))
            return false;
        for (axis = 0; axis < 3u; ++axis) {
            float magnitude = fabsf(point[axis]);
            if (magnitude > peak)
                peak = magnitude;
        }
    }
    return peak >= W3CS_QUALITY_WORLD_MIN_SPAN;
}

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)
{
    int bits;
    if (chosen_step)
        *chosen_step = 0.0f;
    if (worst_projected_error_px)
        *worst_projected_error_px = INFINITY;
    if (!profile || !eligible_world_batch(batch))
        return -1;
    /* Level 10 deliberately keeps the accepted fixed-step behavior even if
     * projection metadata is not available. */
    if (profile->level == 10u) {
        float measured;
        if (chosen_step)
            *chosen_step = W3CS_QUALITY_CURRENT_POSITION_STEP;
        if (w3cs_quality_measure_position_error_px(batch, 0u, &measured)
                && worst_projected_error_px)
            *worst_projected_error_px = measured;
        return 0;
    }
    for (bits = (int)profile->max_position_drop_bits; bits >= 0; --bits) {
        float measured;
        if (!w3cs_quality_measure_position_error_px(
                batch, (unsigned)bits, &measured))
            return -1;
        if (measured <= profile->maximum_position_error_px + 1e-5f) {
            if (chosen_step)
                *chosen_step = ldexpf(W3CS_QUALITY_CURRENT_POSITION_STEP,
                                      bits);
            if (worst_projected_error_px)
                *worst_projected_error_px = measured;
            return bits;
        }
    }
    return -1;
}

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)
{
    if (selected_drop_bits < 0
            || selected_drop_bits >= W3CS_QUALITY_POSITION_PROTECTED)
        return selected_drop_bits;
    if (depth_test_enabled
            && (depth_write_enabled || alpha_test_enabled
                || alpha_blend_enabled))
        return 0;
    return selected_drop_bits;
}

bool w3cs_quality_quantize_position(float current, float base_reconstructed,
                                    unsigned drop_bits,
                                    int16_t *wire_quantum,
                                    float *reconstructed)
{
    float coarse_step;
    float delta;
    float next;
    long coarse_units;
    long wire;
    long scale;
    float quotient;
    long minimum_coarse_units;
    long maximum_coarse_units;
    float tolerance;
    if (!wire_quantum || !reconstructed || drop_bits > 14u
            || !isfinite(current) || !isfinite(base_reconstructed))
        return false;
    coarse_step = ldexpf(W3CS_QUALITY_CURRENT_POSITION_STEP,
                         (int)drop_bits);
    scale = 1l << drop_bits;
    delta = current - base_reconstructed;
    if (!isfinite(coarse_step) || !(coarse_step > 0.0f)
            || !isfinite(delta))
        return false;
    quotient = delta / coarse_step;
    minimum_coarse_units = -32768l / scale;
    maximum_coarse_units = 32767l / scale;
    /* lroundf has a domain error outside LONG's range. The wire range is
     * much smaller, so reject before rounding. Halfway values round away
     * from zero and therefore also need the open outer bounds below. */
    if (!isfinite(quotient)
            || quotient >= (float)maximum_coarse_units + 0.5f
            || quotient <= (float)minimum_coarse_units - 0.5f)
        return false;
    coarse_units = lroundf(quotient);
    wire = coarse_units * scale;
    if (wire < -32768l || wire > 32767l)
        return false;
    next = base_reconstructed
        + (float)wire * W3CS_QUALITY_CURRENT_POSITION_STEP;
    tolerance = 0.5f * coarse_step + fabsf(current) * 1e-6f;
    if (!isfinite(next) || fabsf(next - current) > tolerance)
        return false;
    *wire_quantum = (int16_t)wire;
    *reconstructed = next;
    return true;
}

static float octa_sign(float value)
{
    return value >= 0.0f ? 1.0f : -1.0f;
}

static void octa_decode(int8_t u, int8_t v, float out[3])
{
    /* Match d3d9_proxy.c and the browser's per-operation Math.fround chain.
     * The volatile stores are required on i686, where x87 otherwise keeps
     * intermediate results at 80-bit precision. */
    volatile float fu = (float)u / 127.0f;
    volatile float fv = (float)v / 127.0f;
    volatile float one_minus_au = 1.0f - fabsf(fu);
    volatile float z = one_minus_au - fabsf(fv);
    volatile float x = fu;
    volatile float y = fv;
    volatile float xx;
    volatile float yy;
    volatile float zz;
    volatile float xy;
    volatile float sum;
    volatile float length;
    volatile float ox;
    volatile float oy;
    volatile float oz;
    if (z < 0.0f) {
        volatile float fold_x = 1.0f - fabsf(fv);
        volatile float fold_y = 1.0f - fabsf(fu);
        x = fold_x * octa_sign(fu);
        y = fold_y * octa_sign(fv);
    }
    xx = x * x;
    yy = y * y;
    zz = z * z;
    xy = xx + yy;
    sum = xy + zz;
    length = sqrtf(sum);
    if (!(length > 0.0f) || !isfinite(length)) {
        out[0] = 0.0f;
        out[1] = 0.0f;
        out[2] = 1.0f;
        return;
    }
    ox = x / length;
    oy = y / length;
    oz = z / length;
    out[0] = ox;
    out[1] = oy;
    out[2] = oz;
}

static int8_t quantize_octa_axis(float projected, unsigned bits)
{
    long logical_max = (1l << (bits - 1u)) - 1l;
    long logical;
    long wire;
    if (bits == 8u) {
        wire = lroundf(projected * 127.0f);
        if (wire < -127l)
            wire = -127l;
        if (wire > 127l)
            wire = 127l;
        return (int8_t)wire;
    }
    logical = lroundf(projected * (float)logical_max);
    if (logical < -logical_max)
        logical = -logical_max;
    if (logical > logical_max)
        logical = logical_max;
    wire = lroundf((float)logical * 127.0f / (float)logical_max);
    if (wire < -127l)
        wire = -127l;
    if (wire > 127l)
        wire = 127l;
    return (int8_t)wire;
}

static bool octa_encode_bits(const float normal[3], unsigned bits,
                             int8_t *u, int8_t *v)
{
    float denominator = fabsf(normal[0]) + fabsf(normal[1])
        + fabsf(normal[2]);
    float px;
    float py;
    if (!u || !v || bits < 2u || bits > 8u
            || !(denominator > 0.0f) || !isfinite(denominator))
        return false;
    px = normal[0] / denominator;
    py = normal[1] / denominator;
    if (normal[2] < 0.0f) {
        float fold_x = (1.0f - fabsf(py)) * octa_sign(px);
        float fold_y = (1.0f - fabsf(px)) * octa_sign(py);
        px = fold_x;
        py = fold_y;
    }
    *u = quantize_octa_axis(px, bits);
    *v = quantize_octa_axis(py, bits);
    return true;
}

static bool near_unit(const float value[3], float *length_squared)
{
    float length2;
    if (!value || !isfinite(value[0]) || !isfinite(value[1])
            || !isfinite(value[2]))
        return false;
    length2 = value[0] * value[0] + value[1] * value[1]
        + value[2] * value[2];
    if (length_squared)
        *length_squared = length2;
    return length2 > 0.9025f && length2 < 1.1025f;
}

static float clamped_dot(float value)
{
    if (value < -1.0f)
        return -1.0f;
    if (value > 1.0f)
        return 1.0f;
    return value;
}

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)
{
    float current_length_squared;
    float current_length;
    float normalized_dot;
    float reconstructed[3];
    int8_t qu;
    int8_t qv;
    if (!profile || !current || !result)
        return false;
    memset(result, 0, sizeof(*result));
    result->action = W3CS_QUALITY_NORMAL_EXACT;
    memcpy(result->reconstructed, current, sizeof(result->reconstructed));
    if (!near_unit(current, &current_length_squared))
        return true;
    current_length = sqrtf(current_length_squared);
    if (have_previous && previous_reconstructed) {
        float previous_length_squared;
        if (near_unit(previous_reconstructed, &previous_length_squared)) {
            float hold_dot = current[0] * previous_reconstructed[0]
                + current[1] * previous_reconstructed[1]
                + current[2] * previous_reconstructed[2];
            if (hold_dot > 0.0f
                    && hold_dot * hold_dot
                        >= profile->normal_hold_cos_squared
                            * current_length_squared
                            * previous_length_squared) {
                result->action = W3CS_QUALITY_NORMAL_HOLD;
                memcpy(result->reconstructed, previous_reconstructed,
                       sizeof(result->reconstructed));
                normalized_dot = hold_dot
                    / sqrtf(current_length_squared * previous_length_squared);
                result->angular_error_degrees = acosf(
                    clamped_dot(normalized_dot))
                    * (float)(180.0 / W3CS_QUALITY_PI);
                return true;
            }
        }
    }
    if (!octa_encode_bits(current, profile->octa_normal_bits, &qu, &qv))
        return true;
    octa_decode(qu, qv, reconstructed);
    normalized_dot = (current[0] * reconstructed[0]
        + current[1] * reconstructed[1]
        + current[2] * reconstructed[2]) / current_length;
    result->angular_error_degrees = acosf(clamped_dot(normalized_dot))
        * (float)(180.0 / W3CS_QUALITY_PI);
    if (have_previous && previous_reconstructed
            && memcmp(reconstructed, previous_reconstructed,
                      sizeof(reconstructed)) == 0) {
        /* Same wire code is only a valid hold when that code still meets the
         * profile's angular bound. A coarse codebook can otherwise hide a
         * large source movement inside one cell. */
        if (!(normalized_dot >= profile->normal_minimum_dot))
            return true;
        result->action = W3CS_QUALITY_NORMAL_HOLD;
        memcpy(result->reconstructed, previous_reconstructed,
               sizeof(result->reconstructed));
        return true;
    }
    if (!(normalized_dot >= profile->normal_minimum_dot))
        return true;
    result->action = W3CS_QUALITY_NORMAL_EMIT;
    result->octa_u = qu;
    result->octa_v = qv;
    memcpy(result->reconstructed, reconstructed,
           sizeof(result->reconstructed));
    return true;
}

void w3cs_quality_cadence_init(
    struct w3cs_quality_cadence *cadence,
    uint32_t source_fps,
    uint32_t target_fps)
{
    if (!cadence)
        return;
    memset(cadence, 0, sizeof(*cadence));
    cadence->source_fps = source_fps;
    cadence->target_fps = target_fps ? target_fps : source_fps;
}

static void cadence_set_next(struct w3cs_quality_cadence *cadence,
                             uint64_t timestamp_ns)
{
    uint64_t whole = W3CS_QUALITY_NS_PER_SECOND / cadence->target_fps;
    uint32_t remainder = (uint32_t)(W3CS_QUALITY_NS_PER_SECOND
        % cadence->target_fps);
    cadence->next_due_ns = timestamp_ns + whole;
    cadence->period_remainder = remainder;
}

static void cadence_advance(struct w3cs_quality_cadence *cadence)
{
    uint64_t whole = W3CS_QUALITY_NS_PER_SECOND / cadence->target_fps;
    uint32_t remainder = (uint32_t)(W3CS_QUALITY_NS_PER_SECOND
        % cadence->target_fps);
    cadence->next_due_ns += whole;
    cadence->period_remainder += remainder;
    if (cadence->period_remainder >= cadence->target_fps) {
        ++cadence->next_due_ns;
        cadence->period_remainder -= cadence->target_fps;
    }
}

struct w3cs_quality_frame_decision w3cs_quality_cadence_decide(
    struct w3cs_quality_cadence *cadence,
    uint64_t timestamp_ns,
    uint32_t frame_flags)
{
    struct w3cs_quality_frame_decision decision = {
        false, false, W3CS_QUALITY_FRAME_DROPPED
    };
    bool forced = (frame_flags & (W3CS_QUALITY_FRAME_RECOVERY
        | W3CS_QUALITY_FRAME_GEOMETRY_ANCHOR
        | W3CS_QUALITY_FRAME_FORCED)) != 0u;
    if (!cadence || !cadence->target_fps) {
        decision.keep = true;
        decision.reason = W3CS_QUALITY_FRAME_PASSTHROUGH;
        return decision;
    }
    decision.require_anchor_base = cadence->target_fps < cadence->source_fps;
    if (!cadence->initialized || timestamp_ns < cadence->last_timestamp_ns) {
        bool clock_reset = cadence->initialized;
        cadence->initialized = true;
        cadence->last_timestamp_ns = timestamp_ns;
        cadence_set_next(cadence, timestamp_ns);
        decision.keep = true;
        decision.reason = clock_reset ? W3CS_QUALITY_FRAME_CLOCK_RESET
                                      : W3CS_QUALITY_FRAME_DUE;
        return decision;
    }
    cadence->last_timestamp_ns = timestamp_ns;
    if (cadence->target_fps >= cadence->source_fps) {
        decision.keep = true;
        decision.reason = forced ? W3CS_QUALITY_FRAME_FORCED_KEEP
                                 : W3CS_QUALITY_FRAME_PASSTHROUGH;
        return decision;
    }
    if (timestamp_ns >= cadence->next_due_ns) {
        do {
            cadence_advance(cadence);
        } while (cadence->next_due_ns <= timestamp_ns);
        decision.keep = true;
        decision.reason = forced ? W3CS_QUALITY_FRAME_FORCED_KEEP
                                 : W3CS_QUALITY_FRAME_DUE;
        return decision;
    }
    if (forced) {
        /* Keep the cadence phase. This matches the measured pass-through
         * filter: an anchor is an additional safe frame, not a new clock. */
        decision.keep = true;
        decision.reason = W3CS_QUALITY_FRAME_FORCED_KEEP;
    }
    return decision;
}

bool w3cs_quality_should_make_anchor(
    const struct w3cs_quality_profile *profile,
    uint32_t kept_since_anchor,
    bool have_valid_anchor)
{
    if (!profile || !have_valid_anchor)
        return true;
    return profile->anchor_interval_kept_frames != 0u
        && kept_since_anchor >= profile->anchor_interval_kept_frames;
}

bool w3cs_quality_estimate_accepted_capture(
    unsigned level,
    struct w3cs_quality_bandwidth_estimate *estimate)
{
    const struct w3cs_quality_profile *profile = w3cs_quality_profile(level);
    const double total_bytes = 21392193.0;
    const double frame_bytes = 19302553.0;
    const double reliable_bytes = 2089640.0;
    const double quant_wire_bytes = 9794795.0;
    const double quant_payload_bytes = 26364795.0;
    const double position_value_bytes = 9837620.0;
    const double entropy_bits = 14.119;
    double position_wire;
    double position_saving;
    double projected;
    if (!profile || !estimate)
        return false;
    memset(estimate, 0, sizeof(*estimate));
    position_wire = quant_wire_bytes
        * (position_value_bytes / quant_payload_bytes);
    position_saving = profile->max_position_drop_bits / entropy_bits;
    if (position_saving > 0.78)
        position_saving = 0.78;
    /* Every main quality level preserves all source frames. Only modeled
     * position-value savings are credited. Normal deadband is not credited
     * because the accepted trace cannot be lossily re-encoded. */
    projected = reliable_bytes + frame_bytes
        - position_wire * position_saving;
    if (level == 10u)
        projected = total_bytes;
    estimate->level = (uint8_t)level;
    estimate->position_drop_bits = profile->max_position_drop_bits;
    estimate->projected_total_bytes = (uint64_t)llround(projected);
    estimate->projected_ratio = projected / total_bytes;
    estimate->projected_saving_percent =
        (1.0 - estimate->projected_ratio) * 100.0;
    estimate->modeled_position_value_saving = position_saving;
    estimate->position_model_extrapolated =
        profile->max_position_drop_bits > 6u;
    return true;
}
