/*
 * FFv1 codec
 *
 * Copyright (c) 2026 Lynne <dev@lynne.ee>
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef VULKAN_RANGECODER_SUBGROUP_H
#define VULKAN_RANGECODER_SUBGROUP_H

#extension GL_KHR_shader_subgroup_basic : require
#extension GL_KHR_shader_subgroup_ballot : require
#extension GL_KHR_shader_subgroup_shuffle : require
#extension GL_KHR_shader_subgroup_shuffle_relative : require

#define CONTEXT_SIZE 32
#define MAX_OVERREAD 2

#define RC_LANES gl_WorkGroupSize.x
#define RC_K (CONTEXT_SIZE/RC_LANES)

struct RCStates {
    uint v[RC_K];
};

#ifdef DECODE
#define RC_BTYPE readonly buffer
#else
#define RC_BTYPE uniform
#endif

layout (set = 0, binding = 0, scalar) RC_BTYPE rangecoder_buf {
    uint8_t rangecoder_state[512];
};

shared uint8_t zero_one_state[512];

struct RangeCoder {
    uint     bs_start;
    uint     bs_off;
    uint     bs_end;
    uint     low;
    uint     range;
    uint16_t outstanding_count;
    int16_t  outstanding_byte;
};

RangeCoder rc;

uint rac_range1(uint range, uint state24)
{
    uint hi, lo;
    umulExtended(range, state24, hi, lo);
    return hi;
}

#ifdef ENCODE
uint rc_top;
uint rc_oc;
int rc_ob;
uint rc_nev;
uint rc_ev[64/RC_LANES];

void rac_init_enc(in RangeCoder c)
{
    for (uint i = gl_SubgroupInvocationID; i < 512; i += RC_LANES)
        zero_one_state[i] = rangecoder_state[i];
    barrier();

    rc = c;
    rc_top = rc.low + rc.range;
    rc_oc = uint(rc.outstanding_count);
    rc_ob = int(rc.outstanding_byte);
    rc_nev = 0;
    [[unroll]] for (uint k = 0; k < 64/RC_LANES; k++)
        rc_ev[k] = 0;
}

void rac_emit_block(uint low, uint cnt)
{
    uint lane = gl_SubgroupInvocationID;
    bool deferred = lane < cnt && low - 0xFF01u < 0xFFu;
    if (rc_ob >= 0 && rc_oc == 0 && subgroupBallot(deferred).x == 0) {
        uint digit = (low >> 8) & 0xFFu;
        uint prev = subgroupShuffleUp(digit, 1);
        uint b = ((lane == 0 ? uint(rc_ob) : prev) + (low >> 16)) & 0xFFu;
        if (lane < cnt)
            slice_data[rc.bs_off + lane].v = uint8_t(b);
        rc.bs_off += cnt;
        rc_ob = int(subgroupBroadcast(digit, cnt - 1));
    } else {
        for (uint i = 0; i < cnt; i++) {
            uint l = subgroupBroadcast(low, i);
            if (rc_ob < 0) {
                rc_ob = int(l >> 8);
            } else if (l - 0xFF01u < 0xFFu) {
                rc_oc++;
            } else {
                uint carry = l >> 16;
                if (lane == 0)
                    slice_data[rc.bs_off].v = uint8_t(uint(rc_ob) + carry);
                for (uint k = 0; k < rc_oc; k += RC_LANES)
                    if (lane < rc_oc - k)
                        slice_data[rc.bs_off + 1 + k + lane].v = uint8_t(carry - 1u);
                rc.bs_off += 1 + rc_oc;
                rc_oc = 0;
                rc_ob = int((l >> 8) & 0xFFu);
            }
        }
    }
}

void rac_emit(void)
{
    [[unroll]] for (uint k = 0; k < 64/RC_LANES; k++)
        if (rc_nev > k*RC_LANES)
            rac_emit_block(rc_ev[k], min(rc_nev - k*RC_LANES, RC_LANES));
    rc_nev = 0;
}

void rac_renorm_enc(void)
{
    uint low = rc_top - rc.range;
    rc.range <<= 8;
    rc_top = ((low & 0xFFu) << 8) + rc.range;
    [[unroll]] for (uint k = 0; k < 64/RC_LANES; k++)
        rc_ev[k] = gl_SubgroupInvocationID + k*RC_LANES == rc_nev ? low : rc_ev[k];
    rc_nev++;
}

void put_rac_range1(uint range1, bool bit)
{
    rc_top = bit ? rc_top : rc_top - range1;
    rc.range = bit ? range1 : rc.range - range1;
    [[dont_flatten]] if (rc.range < 0x100)
        rac_renorm_enc();
}

void put_rac(uint state24, bool bit)
{
    put_rac_range1(rac_range1(rc.range, state24), bit);
}

void put_rac_equi(bool bit)
{
    put_rac_range1(rc.range >> 1, bit);
}

uint rac_terminate(void)
{
    rac_emit();

    uint range1 = (rc.range * 129) >> 8;
    rc.range -= range1;
    rc_top -= range1;
    if (rc.range < 0x100)
        rac_renorm_enc();

    rc_top = rc_top - rc.range + 0x1FEu;
    rc.range = 0xFFu;
    rac_renorm_enc();
    rc_top = rc_top - rc.range + 0xFFu;
    rc.range = 0xFFu;
    rac_renorm_enc();
    rac_emit();

    rc.low = rc_top - rc.range;
    rc.outstanding_count = uint16_t(rc_oc);
    rc.outstanding_byte = int16_t(rc_ob);
    return rc.bs_off - rc.bs_start;
}

void put_isymbol_tail(int e, RCStates st24, uint a, bool neg)
{
    uint s[21];
    [[unroll]] for (uint i = 0; i < 11; i++)
        if (i <= e + 1)
            s[i] = subgroupBroadcast(st24.v[i/RC_LANES], i % RC_LANES);
    [[unroll]] for (uint i = 0; i < 9; i++)
        if (i < e)
            s[11 + i] = subgroupBroadcast(st24.v[(22 + i)/RC_LANES], (22 + i) % RC_LANES);
    s[20] = subgroupBroadcast(st24.v[(11 + e)/RC_LANES], (11 + e) % RC_LANES);

    uint range = rc.range;
    uint top = rc_top;
    uint r = rac_range1(rc.range, s[0]);
    rc.range -= r;
    rc_top -= r;
    [[unroll]] for (int i = 0; i < 9; i++)
        if (i < e)
            rc.range = rac_range1(rc.range, s[1 + i]);
    r = rac_range1(rc.range, s[1 + e]);
    rc.range -= r;
    rc_top -= r;
    [[dont_flatten]] if (rc.range < 0x100) {
        rc.range = range;
        rc_top = top;
        put_rac(s[0], false);
        [[unroll]] for (int i = 0; i < 9; i++)
            if (i < e)
                put_rac(s[1 + i], true);
        put_rac(s[1 + e], false);
    }
    [[unroll]] for (int i = 8; i >= 0; i--)
        if (i < e)
            put_rac(s[11 + i], bitfieldExtract(a, i, 1) != 0);
    put_rac(s[20], neg);
}

void put_isymbol_esc(int e, RCStates st, RCStates st24, uint a, bool neg,
                     out uint s10, out uint s31)
{
    uint s[10];
    [[unroll]] for (uint i = 0; i < 10; i++)
        s[i] = subgroupBroadcast(st24.v[i/RC_LANES], i % RC_LANES);
    s10 = subgroupBroadcast(st.v[10/RC_LANES], 10 % RC_LANES);
    s31 = subgroupBroadcast(st.v[31/RC_LANES], 31 % RC_LANES);
    put_rac(s[0], false);
    [[unroll]] for (int i = 1; i < 10; i++)
        put_rac(s[i], true);
    if (rc_nev > 40)
        rac_emit();

    for (int i = 9; i < e; i++) {
        uint nx = rangecoder_state[256 + s10];
        put_rac(s10 << 24, true);
        if (rc_nev > 60)
            rac_emit();
        s10 = nx;
    }
    uint nx = rangecoder_state[s10];
    put_rac(s10 << 24, false);
    s10 = nx;

    for (int i = e - 1; i >= 9; i--) {
        bool b = bitfieldExtract(a, i, 1) != 0;
        uint n0 = rangecoder_state[s31];
        uint n1 = rangecoder_state[256 + s31];
        put_rac(s31 << 24, b);
        if (rc_nev > 60)
            rac_emit();
        s31 = b ? n1 : n0;
    }
    if (rc_nev > 50)
        rac_emit();

    [[unroll]] for (int i = 8; i >= 0; i--)
        s[i] = subgroupBroadcast(st24.v[(22 + i)/RC_LANES], (22 + i) % RC_LANES);
    uint ss = subgroupBroadcast(st24.v[21/RC_LANES], 21 % RC_LANES);
    [[unroll]] for (int i = 8; i >= 0; i--)
        put_rac(s[i], bitfieldExtract(a, i, 1) != 0);
    put_rac(ss, neg);
}

void put_isymbol(RCStates st, int v, out uint s10, out uint s31)
{
    RCStates st24;
    [[unroll]] for (uint k = 0; k < RC_K; k++)
        st24.v[k] = st.v[k] << 24;
    uint a = abs(v);
    int e = findMSB(a);
    bool neg = v < 0;
    s10 = 0;
    s31 = 0;

    if (v != 0) {
        if (e < 4) {
            if (e < 2) {
                if (e == 1)
                    put_isymbol_tail(1, st24, a, neg);
                else
                    put_isymbol_tail(0, st24, a, neg);
            } else if (e == 3) {
                put_isymbol_tail(3, st24, a, neg);
            } else {
                put_isymbol_tail(2, st24, a, neg);
            }
        } else if (e < 7) {
            if (e == 4)
                put_isymbol_tail(4, st24, a, neg);
            else if (e == 5)
                put_isymbol_tail(5, st24, a, neg);
            else
                put_isymbol_tail(6, st24, a, neg);
        } else if (e < 10) {
            if (e == 7)
                put_isymbol_tail(7, st24, a, neg);
            else if (e == 8)
                put_isymbol_tail(8, st24, a, neg);
            else
                put_isymbol_tail(9, st24, a, neg);
        } else {
            put_isymbol_esc(e, st, st24, a, neg, s10, s31);
        }
    } else {
        put_rac(subgroupBroadcast(st24.v[0], 0), true);
    }
}
#endif

#ifdef DECODE
uint rc_win;
uint rc_winx[RC_K];
uint rc_dist;
uint rc_next;
uint rc_pos;

void rac_load_window(void)
{
    rc.bs_off += rc_pos;
    rc_pos &= ~31u;
    uint o = rc.bs_off + gl_SubgroupInvocationID;
    rc_win = uint(~(o < rc.bs_end ? u8buf(uint64_t(slice_data) + o).v : uint8_t(0)));
    [[unroll]] for (uint k = 1; k < RC_K; k++) {
        o += RC_LANES;
        rc_winx[k] = uint(~(o < rc.bs_end ? u8buf(uint64_t(slice_data) + o).v : uint8_t(0)));
    }
}

void rac_init_dec(in RangeCoder c)
{
    for (uint i = gl_SubgroupInvocationID; i < 512; i += RC_LANES)
        zero_one_state[i] = rangecoder_state[i];
    barrier();

    rc = c;
    rc_dist = rc.range - rc.low - 1;
    rc_pos = 0;
    rac_load_window();
    rc_next = subgroupBroadcast(rc_win, 0);
}

void rac_check_window(void)
{
    if (rc_pos > 10)
        rac_load_window();
}

void refill(void)
{
    rc.range <<= 8;
    rc_dist = (rc_dist << 8) | rc_next;
    if (RC_K == 1)
        rc_next = subgroupBroadcast(rc_win, ++rc_pos);
    else if (++rc_pos < RC_LANES)
        rc_next = subgroupBroadcast(rc_win, rc_pos);
    else
        rc_next = subgroupBroadcast(rc_winx[rc_pos/RC_LANES], rc_pos % RC_LANES);
}

void rac_renorm(void)
{
    if (expectEXT(rc.range < 0x100, false))
        refill();
}

bool get_rac_internal(uint range1)
{
    bool bit = rc_dist < range1;
    uint ranged = rc.range - range1;
    uint distd = rc_dist - range1;
    rc.range = bit ? range1 : ranged;
    rc_dist = bit ? rc_dist : distd;
    return bit;
}

bool get_rac(uint state24)
{
    bool bit = get_rac_internal(rac_range1(rc.range, state24));
    rac_renorm();
    return bit;
}

bool get_rac_equi(void)
{
    rac_check_window();
    bool bit = get_rac_internal(rc.range >> 1);
    rac_renorm();
    return bit;
}

int get_isymbol_tail(int e, uint range, uint range1, RCStates sx, int pred, int sgn,
                     out uint read, out uint bits)
{
    uint m[9];
    [[unroll]] for (int k = 8; k >= 0; k--)
        if (k + 1 < e)
            m[k] = subgroupBroadcast(sx.v[(22 + k)/RC_LANES], (22 + k) % RC_LANES);
    uint ss = subgroupBroadcast(sx.v[(10 + e)/RC_LANES], (10 + e) % RC_LANES);

    rc.range = range - range1;
    rc_dist -= range1;
    rac_renorm();

    uint a = 0;
    [[unroll]] for (int k = 8; k >= 0; k--) {
        if (k + 1 < e) {
            a = (a << 1) + uint(get_rac_internal(rac_range1(rc.range, m[k])));
            rac_renorm();
        }
    }

    a += 1u << (e - 1);
    int sa = int(a)*sgn;
    int vp = pred + sa;
    int vn = vp - 2*sa;
    bool neg = get_rac_internal(rac_range1(rc.range, ss));
    int v = neg ? vn : vp;
    read = (2u << e) - 1u + (((1u << (e - 1)) - 1u) << 22) + (1u << (10 + e));
    bits = (a << 22) + (1u << e) - 2u - (1u << (21 + e)) + (neg ? 1u << (10 + e) : 0u);
    return v;
}

const int AVERROR_INVALIDDATA = -0x41444E49;

int get_isymbol_esc(inout RCStates st, RCStates sx, int pred, int sgn,
                    out uint read, out uint bits)
{
    bool esc = c_bits > 10;
    int n = 11;
    if (esc) {
        uint s10 = subgroupBroadcast(st.v[10/RC_LANES], 10 % RC_LANES);
        bool one;
        [[dont_unroll]] do {
            s10 = rangecoder_state[s10 + 256];
            n++;
            rac_check_window();
            one = get_rac(s10 << 24);
        } while (one && n < 33);
        st.v[10/RC_LANES] = gl_SubgroupInvocationID == 10 % RC_LANES ? s10 : st.v[10/RC_LANES];

        if (one) {
            read = 0x7FFu;
            bits = 0x7FEu;
            return pred + sgn*AVERROR_INVALIDDATA;
        }
    }

    uint s31 = subgroupBroadcast(st.v[31/RC_LANES], 31 % RC_LANES);
    rac_check_window();
    bool b = get_rac(s31 << 24);
    uint a = b ? 0x3 : 0x2;
    for (n -= 2; n >= 11; n--) {
        s31 = rangecoder_state[s31 + (b ? 256 : 0)];
        rac_check_window();
        b = get_rac(s31 << 24);
        a = (a << 1) | uint(b);
    }
    st.v[31/RC_LANES] = gl_SubgroupInvocationID == 31 % RC_LANES ? s31 : st.v[31/RC_LANES];

    rac_check_window();
    [[unroll]] for (int k = 8; k >= 0; k--) {
        uint sk = subgroupBroadcast(sx.v[(22 + k)/RC_LANES], (22 + k) % RC_LANES);
        a = (a << 1) | uint(get_rac(sk));
    }

    uint ss = subgroupBroadcast(sx.v[21/RC_LANES], 21 % RC_LANES);
    bool neg = get_rac_internal(rac_range1(rc.range, ss));
    int sa = int(a)*sgn;
    read = 0xFFE007FFu;
    bits = ((esc ? 0x1FFu : 0x3FFu) << 1) | ((a & 0x3FFu) << 22) | (uint(neg) << 21);
    return neg ? pred - sa : pred + sa;
}

int get_isymbol(inout RCStates st, int pred, int sgn, out uint read, out uint bits)
{
    RCStates sx;
    [[unroll]] for (uint k = 0; k < RC_K; k++)
        sx.v[k] = st.v[k] << 24;
    uint s[11];
    [[unroll]] for (uint i = 0; i < 11; i++)
        s[i] = subgroupBroadcast(sx.v[i/RC_LANES], i % RC_LANES);

    read = 1u;
    bits = 1u;

    uint range = rc.range;
    uint dist = rc_dist;
    uint range0 = rac_range1(range, s[0]);
    uint r[11];
    r[0] = range - range0;
    r[1] = rac_range1(r[0], s[1]);
    rc.range = r[0];
    rc_dist = dist - range0;
    uint lim = max(rc_dist, 0xffu);

    int v;
    while (true) {
        uint skip;
        [[unroll]] for (int i = 2; i < 6; i++)
            r[i] = rac_range1(r[i - 1], s[i]);

        if (r[5] > lim) {
            [[unroll]] for (int i = 6; i < 11; i++)
                r[i] = rac_range1(r[i - 1], s[i]);
            if (r[6] > lim) {
                if (r[7] > lim) {
                    if (r[8] > lim) {
                        if (r[9] > lim) {
                            if (r[10] > lim) {
                                rc.range = r[10];
                                v = get_isymbol_esc(st, sx, pred, sgn, read, bits);
                                break;
                            } else if (r[10] <= rc_dist) {
                                v = get_isymbol_tail(10, r[9], r[10], sx, pred, sgn, read, bits);
                                break;
                            } else {
                                skip = 10;
                                rc.range = r[10];
                            }
                        } else if (r[9] <= rc_dist) {
                            v = get_isymbol_tail(9, r[8], r[9], sx, pred, sgn, read, bits);
                            break;
                        } else {
                            skip = 9;
                            rc.range = r[9];
                        }
                    } else if (r[8] <= rc_dist) {
                        v = get_isymbol_tail(8, r[7], r[8], sx, pred, sgn, read, bits);
                        break;
                    } else {
                        skip = 8;
                        rc.range = r[8];
                    }
                } else if (r[7] <= rc_dist) {
                    v = get_isymbol_tail(7, r[6], r[7], sx, pred, sgn, read, bits);
                    break;
                } else {
                    skip = 7;
                    rc.range = r[7];
                }
            } else if (r[6] <= rc_dist) {
                v = get_isymbol_tail(6, r[5], r[6], sx, pred, sgn, read, bits);
                break;
            } else {
                skip = 6;
                rc.range = r[6];
            }
        } else if (r[4] > lim) {
            if (r[5] <= rc_dist) {
                v = get_isymbol_tail(5, r[4], r[5], sx, pred, sgn, read, bits);
                break;
            } else {
                skip = 5;
                rc.range = r[5];
            }
        } else if (r[3] > lim) {
            if (r[4] <= rc_dist) {
                v = get_isymbol_tail(4, r[3], r[4], sx, pred, sgn, read, bits);
                break;
            } else {
                skip = 4;
                rc.range = r[4];
            }
        } else if (r[2] > lim) {
            if (r[3] <= rc_dist) {
                v = get_isymbol_tail(3, r[2], r[3], sx, pred, sgn, read, bits);
                break;
            } else {
                skip = 3;
                rc.range = r[3];
            }
        } else if (r[1] > lim) {
            if (r[2] <= rc_dist) {
                v = get_isymbol_tail(2, r[1], r[2], sx, pred, sgn, read, bits);
                break;
            } else {
                skip = 2;
                rc.range = r[2];
            }
        } else if (range0 > min(dist, range - 0x100)) {
            if (dist < range0) {
                rc.range = range0;
                rc_dist = dist;
                v = pred;
                break;
            }
            skip = 0;
            rc.range = r[0];
        } else if (r[1] <= rc_dist) {
            v = get_isymbol_tail(1, r[0], r[1], sx, pred, sgn, read, bits);
            break;
        } else {
            skip = 1;
            rc.range = r[1];
        }

        refill();
        lim = max(rc_dist, 0xffu);
        r[0] = rc.range;
        r[1] = skip > 0 ? rc.range + skip - 1 : rac_range1(rc.range, s[1]);
        range0 = 0;
        uint skipped = (2u << skip) - 2u;
        [[unroll]] for (uint k = 0; k < RC_K; k++)
            sx.v[k] = subgroupInverseBallot(uvec4(skipped >> (k*RC_LANES), 0, 0, 0)) ? ~0u : sx.v[k];
        [[unroll]] for (uint i = 2; i < 11; i++)
            s[i] = subgroupBroadcast(sx.v[i/RC_LANES], i % RC_LANES);
    }

    return v;
}
#endif

#endif /* VULKAN_RANGECODER_SUBGROUP_H */
