/*
 * Copyright © 2018-2026, VideoLAN and dav2d authors
 * Copyright © 2018-2026, Two Orioles, LLC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef DAV2D_SRC_INTERNAL_H
#define DAV2D_SRC_INTERNAL_H

#include <stdatomic.h>

#include "ccso.h"
#include "dav2d/data.h"

typedef struct Dav2dFrameContext Dav2dFrameContext;
typedef struct Dav2dTileState Dav2dTileState;
typedef struct Dav2dTaskContext Dav2dTaskContext;
typedef struct Dav2dTask Dav2dTask;

#include "common/attributes.h"

#include "src/cdef.h"
#include "src/cdf.h"
#include "src/data.h"
#include "src/env.h"
#include "src/filmgrain.h"
#include "src/intra_edge.h"
#include "src/ipred.h"
#include "src/itx.h"
#include "src/levels.h"
#include "src/lf_mask.h"
#include "src/deblock.h"
#include "src/looprestoration.h"
#include "src/mc.h"
#include "src/msac.h"
#include "src/pal.h"
#include "src/picture.h"
#include "src/recon.h"
#include "src/refmvs.h"
#include "src/stx.h"
#include "src/thread.h"

typedef struct Dav2dDSPContext {
    Dav2dFilmGrainDSPContext fg;
    Dav2dIntraPredDSPContext ipred;
    Dav2dMCDSPContext mc;
    Dav2dInvTxfmDSPContext itx;
    Dav2dStxDSPContext stx;
    Dav2dDeblockDSPContext lf;
    Dav2dCcsoDSPContext ccso;
    Dav2dCdefDSPContext cdef;
    Dav2dLoopRestorationDSPContext lr;
} Dav2dDSPContext;

struct Dav2dTileGroup {
    Dav2dData data;
    int start, end;
};

enum TaskType {
    DAV2D_TASK_TYPE_INIT,
    DAV2D_TASK_TYPE_INIT_CDF,
    DAV2D_TASK_TYPE_TILE_ENTROPY,
    DAV2D_TASK_TYPE_ENTROPY_PROGRESS,
    DAV2D_TASK_TYPE_TILE_MV_RESOLUTION,
    DAV2D_TASK_TYPE_TILE_RECONSTRUCTION,
    DAV2D_TASK_TYPE_DEBLOCK_COLS,
    DAV2D_TASK_TYPE_DEBLOCK_ROWS,
    DAV2D_TASK_TYPE_CDEF,
    DAV2D_TASK_TYPE_LOOP_RESTORATION,
    DAV2D_TASK_TYPE_RECONSTRUCTION_PROGRESS,
    DAV2D_TASK_TYPE_FG_PREP,
    DAV2D_TASK_TYPE_FG_APPLY,
};

struct Dav2dContext {
    Dav2dFrameContext *fc;
    unsigned n_fc;

    Dav2dTaskContext *tc;
    unsigned n_tc;

    // cache of OBUs that make up a single frame before we submit them
    // to a frame worker to be decoded
    struct Dav2dTileGroup *tile;
    int n_tile_data_alloc;
    int n_tile_data;
    int n_tiles;
    Dav2dMemPool *seq_hdr_pool;
    Dav2dRef *seq_hdr_ref;
    Dav2dSequenceHeader *seq_hdr;
    Dav2dMemPool *frame_hdr_pool;
    Dav2dRef *frame_hdr_ref;
    Dav2dFrameHeader *frame_hdr;

    Dav2dRef *content_light_ref;
    Dav2dContentLightLevel *content_light;
    Dav2dRef *mastering_display_ref;
    Dav2dMasteringDisplay *mastering_display;
    Dav2dRef *itut_t35_ref;
    Dav2dITUTT35 *itut_t35;
    int n_itut_t35;

    // decoded output picture queue
    Dav2dData in;
    struct OutputQueue {
        Dav2dThreadPicture p;
        int res;
        // FIXME event/frame_flags
    } *dpb; // output buffer management
    int dpb_in, dpb_out, dpb_sz, dpb_poc, drain;
    atomic_int flush_mem, *flush;
    struct {
#if 0
        Dav2dThreadPicture *out_delayed;
#endif
        unsigned next;
    } frame_thread;

    // task threading (refer to tc[] for per_thread thingies)
    struct TaskThreadData {
        pthread_mutex_t lock;
        pthread_cond_t cond;
        atomic_uint first;
        unsigned cur;
        // This is used for delayed reset of the task cur pointer when
        // such operation is needed but the thread doesn't enter a critical
        // section (typically when executing the next sbrow task locklessly).
        // See src/thread_task.c:reset_task_cur().
        atomic_uint reset_task_cur;
        atomic_int cond_signaled;
        struct {
            int exec, finished;
            pthread_cond_t cond;
            const Dav2dPicture *in;
            Dav2dPicture *out;
            enum TaskType type;
            atomic_int progress[2]; /* [0]=started, [1]=completed */
            union {
                struct {
                    ALIGN(int8_t grain_lut_8bpc[3][GRAIN_HEIGHT + 1][GRAIN_WIDTH], 16);
                    ALIGN(uint8_t scaling_8bpc[3][256], 64);
                };
                struct {
                    ALIGN(int16_t grain_lut_16bpc[3][GRAIN_HEIGHT + 1][GRAIN_WIDTH], 16);
                    ALIGN(uint8_t scaling_16bpc[3][4096], 64);
                };
            };
        } delayed_fg;
        int inited;
        // how block decoding (entropy, motion vector resolving, reconstruction)
        // is split
        // 1: all in a single pass
        // 2: first entropy decoding as one pass, then motion vector resolving +
        //    reconstruction in a 2nd pass
        // 3: each in a separate pass
        int n_passes;
    } task_thread;

    // reference/entropy state
    Dav2dMemPool *segmap_pool;
    Dav2dMemPool *refmvs_pool;
    Dav2dMemPool *ccsomap_pool;
    struct {
        Dav2dThreadPicture p;
        Dav2dRef *segmap;
        Dav2dRef *refmvs;
        Dav2dRef *ccsomap;
        uint8_t refpoc[7];
    } refs[8];
    Dav2dMemPool *cdf_pool;
    CdfThreadContext cdf[8];
    Dav2dMemPool *fgm_pool;
    Dav2dRef *fgm[8];
    Dav2dMemPool *ci_pool;
    Dav2dRef *ci_ref;

    // uv segmap, not part of the reference state
    Dav2dMemPool *segmap_uv_pool;

    Dav2dDSPContext dsp[3 /* 8, 10, 12 bits/component */];
    Dav2dPalDSPContext pal_dsp;
    Dav2dRefmvsDSPContext refmvs_dsp;

    Dav2dPicAllocator allocator;
    int apply_grain;
    int operating_point;
    unsigned operating_point_idc;
    int all_layers;
    int max_spatial_id;
    unsigned frame_size_limit;
    int strict_std_compliance;
    int output_invisible_frames;
    enum Dav2dInloopFilterType inloop_filters;
    enum Dav2dDecodeFrameType decode_frame_type;
#if 0
    enum PictureFlags frame_flags;
    enum Dav2dEventFlags event_flags;
    Dav2dDataProps cached_error_props;
    int cached_error;
#endif

    Dav2dLogger logger;

    Dav2dMemPool *picture_pool;
    Dav2dMemPool *pic_ctx_pool;
};

struct Dav2dTask {
    unsigned frame_idx;         // frame thread id
    enum TaskType type;         // task work
    int sby;                    // sbrow

    // task dependencies
    int recon_progress, deblock_progress;
    int deps_skip;
    struct Dav2dTask *next; // only used in task queue
};

struct Dav2dFrameContext {
    Dav2dRef *seq_hdr_ref;
    Dav2dSequenceHeader *seq_hdr;
    Dav2dRef *frame_hdr_ref;
    Dav2dFrameHeader *frame_hdr;
    Dav2dThreadPicture refp[7];
    Dav2dThreadPicture cur;
    Dav2dRef *mvs_ref;
    refmvs_temporal_block *mvs, *ref_mvs[7];
    Dav2dRef *ref_mvs_ref[7];
    Dav2dRef *cur_segmap_ref, *prev_segmap_ref;
    uint8_t *cur_segmap;
    const uint8_t *prev_segmap;
    Dav2dRef *cur_ccsomap_ref, *prev_ccsomap_ref[3];
    uint8_t *cur_ccsomap;
    const uint8_t *prev_ccsomap[3];
    uint8_t refpoc[7], refrefpoc[7][7], refcnt[7];
    union {
        int8_t refdir_with_intra[1 /* intra */ + 7 + 1 /* tip */];
        struct {
            int8_t refdir_intra;
            uint8_t refdir[7 + 1 /* tip */];
        };
    };
    int8_t furthest_future_refidx;
    uint8_t absrefdist[7];
    int8_t refdist[7];
    union refpair skip_mode_refs;
    uint8_t gmv_warp_allowed[7];
    int use_pri_sec_cdf;
    CdfThreadContext in_cdf, out_cdf, src_cdf[2];
    struct Dav2dTileGroup *tile;
    int n_tile_data_alloc;
    int n_tile_data;

    // for scalable references
    struct ScalableMotionParams {
        int scale; // if no scaling, this is 0
        int step;
    } svc[7][2 /* x, y */];

    const Dav2dContext *c;
    Dav2dTileState *ts;
    int n_ts;
    const Dav2dDSPContext *dsp;
    struct {
        recon_b_fn recon_b;
        filter_sbrow_fn filter_sbrow;
        filter_sbrow_fn filter_sbrow_deblock_cols;
        filter_sbrow_fn filter_sbrow_deblock_rows;
        void (*filter_sbrow_cdef)(Dav2dTaskContext *tc, int sby);
        filter_sbrow_fn filter_sbrow_lr;
        backup_prefilter_data_fn backup_prefilter_data;
        read_coef_blocks_fn read_coef_blocks;
        copy_pal_block_fn copy_pal_block_y;
        read_pal_plane_fn read_pal_plane;
    } bd_fn;

    size_t prefilter_data_sz;
    pixel *prefilter_data[3];
    int prefilter_data_full_frame;
    ptrdiff_t b4_stride;
    int bw, bh, sb256w, sb256h, sbh, sb_shift, sb_step;
    int ss_ver, ss_hor;
    uint32_t dq[DAV2D_MAX_SEGMENTS][3 /* plane */][2 /* dc/ac */];
    const uint8_t *qm[N_RECT_TX_SIZES][3 /* plane */];
    BlockContext *a;
    int a_sz /* w*tile_rows */;
    refmvs_frame rf;
    int bitdepth_max;
    enum BlockSize root_bs;

    struct {
        int next_tile_row[2 /* 0: reconstruction, 1: entropy */];
        atomic_int entropy_progress;
        atomic_int deblock_progress; // in sby units
        atomic_uint *frame_progress, *copy_db_progress;
        // indexed using t->by * f->b4_stride + t->bx
        Av2Block *b;
        struct CodedBlockInfo {
            int16_t eob[3 /* plane */];
            uint16_t txtp[3 /* plane */];
        } *cbi;
        // indexed using (t->by >> 1) * (f->b4_stride >> 1) + (t->bx >> 1)
        pixel (*pal)[8 /* idx */];
        // iterated over inside tile state
        uint8_t *pal_idx;
        coef *cf;
        uint8_t *partition;
        int prog_sz;
        int cbi_sz, pal_sz, pal_idx_sz, cf_sz, part_sz;
        // start offsets per tile
        unsigned *tile_start_off;
        int scheduled;
    } frame_thread;

    // loopfilter
    struct {
        Av2Filter *mask;
        Av2Restoration *lr_mask;
        uint8_t *segmap_uv;
        int mask_sz /* w*h */, lr_mask_sz, uv_segmap_sz;
        ptrdiff_t uv_segmap_stride;
        int cdef_buf_plane_sz[2]; /* stride*sbh*4 */
        int cdef_buf_sbh;
        /* 0-1: (stride*sbh*4) << sb128 if n_tc > 1, else stride*4;
         * 2-3: stride*(n_tile_rows-1)*4 if n_tc==1, double that otherwise */
        int lr_buf_plane_sz[4];
        int re_sz /* h */;
        int base_q;
        int gdf_ref_dst_idx;
        const uint8_t *ns_subclass_lut;
        const uint8_t *pc_subclass_lut;
        const int16_t (*pc_filters)[13];
        uint8_t *tx_db_right_edge[2];
        uint8_t *cdef_line_buf, *lr_line_buf;
        pixel *cdef_line[2 /* pre, post */][3 /* plane */];
        pixel *lr_db_line[3 /* plane */];
        pixel *lr_cdef_line[3 /* plane */];

        // in-loop filter per-frame state keeping
        uint8_t *start_of_tile_row;
        int start_of_tile_row_sz;
        pixel *p[3];
        int restore_planes; // enum LrRestorePlanes
    } lf;

    struct {
        pthread_mutex_t lock;
        pthread_cond_t cond;
        struct TaskThreadData *ttd;
        struct Dav2dTask *tasks, *tile_tasks[3], init_task;
        int num_tasks, num_tile_tasks;
        atomic_int init_done;
        atomic_int done[2];
        int retval;
        int update_set; // whether we need to update CDF reference
        atomic_int error;
        atomic_int task_counter, entropy_task_counter;
        struct Dav2dTask *task_head, *task_tail;
        // Points to the task directly before the cur pointer in the queue.
        // This cur pointer is theoretical here, we actually keep track of the
        // "prev_t" variable. This is needed to not loose the tasks in
        // [head;cur-1] when picking one for execution.
        struct Dav2dTask *task_cur_prev;
        struct { // async task insertion
            atomic_int merge;
            pthread_mutex_t lock;
            Dav2dTask *head, *tail;
        } pending_tasks;
    } task_thread;

    // threading (refer to tc[] for per-thread things)
    struct FrameTileThreadData {
        int (*lowest_pixel_mem)[7][2];
        int lowest_pixel_mem_sz;
    } tile_thread;
};

struct Dav2dTileState {
    CdfContext cdf;
    MsacContext msac;

    struct {
        int col_start, col_end, row_start, row_end; // in 4px units
        int col, row; // in tile units
    } tiling;

    // in sby units, TILE_ERROR after a decoding error
    atomic_int progress[3 /* pass */];
    struct {
        uint8_t *pal_idx;
        pixel (*pal)[8];
        struct CodedBlockInfo *cbi;
        coef *cf;
        uint8_t *partition[2];
    } frame_thread[2 /* 0: reconstruction, 1: entropy */];

    // in fullpel units, [0] = Y, [1] = UV, used for progress requirements
    // each entry is one tile-sbrow; middle index is refidx
    int (*lowest_pixel)[7][2];

    uint32_t dqmem[DAV2D_MAX_SEGMENTS][3 /* plane */][2 /* dc/ac */];
    const uint32_t (*dq)[3][2];
    int last_qidx;

    Av2RestorationUnit *lr_ref[3];

    struct NsWienerBank {
        uint8_t bank_size[16], bank_idx[16];
        int8_t filter[4][16][32];
    } ns_wiener_bank[3];
};

struct Dav2dTaskContext {
    const Dav2dContext *c;
    const Dav2dFrameContext *f;
    Dav2dTileState *ts;
    int bx, by, cbx, cby, sdp_cfl_disallowed, intra_region;
    BlockContext l, *a;
    struct SBEdgeCtx a_sb_cache;
    uint64_t is_coded[2 /* luma, chroma */][64];
    struct {
        int col_start;
        int row_start;
        union {
            struct {
                int alpha, beta;
            } bawp[3 /* plane */];
        };
    } pb;
    refmvs_tile rt;
    // chroma backups
    uint16_t /*enum TxfmType*/ chroma_txtp[16 * 16][2]; // why 2?
    int16_t chroma_eob[16 * 16][2];
    coef *cf_uv;
    ALIGN(union, 64) {
        int16_t cf_y_8bpc [32 * 32];
        int32_t cf_y_16bpc[32 * 32];
    };
    ALIGN(union, 64) {
        int16_t cf_uv_8bpc [2][64 * 64];
        int32_t cf_uv_16bpc[2][64 * 64];
    };
    union {
        uint8_t  al_pal_8bpc [2 /* a/l */][64 /* bx/y4 */][8 /* palette_idx */];
        uint16_t al_pal_16bpc[2 /* a/l */][64 /* bx/y4 */][8 /* palette_idx */];
    };
    uint8_t luma_intra_dir_mode_map[16 * 16];
    uint8_t luma_fsc_map[16 * 16];
    ALIGN(union, 64) {
        struct {
            int16_t compinter[2][64 * 64];
            uint8_t seg_mask[64 * 64];
            union {
                uint8_t p_8bpc[2][24 * 128];
                uint16_t p_16bpc[2][24 * 96];
            };
            union {
                // stride=192 for non-SVC, or 320 for SVC
                uint8_t  emu_edge_8bpc [320 * (256 + 7)];
                uint16_t emu_edge_16bpc[320 * (256 + 7)];
            };
        };
        struct {
            uint8_t pal_idx_y[64 * 64];
            union {
                struct {
                    uint8_t interintra_8bpc[64 * 64];
                    uint8_t edge_8bpc[64 * 8 + 2 * 1 + 2 * 9];
                    ALIGN(uint8_t pal_8bpc[8 /* palette_idx */], 8);
                };
                struct {
                    uint16_t interintra_16bpc[64 * 64];
                    uint16_t edge_16bpc[64 * 8 + 2 * 1 + 3 * 6];
                    ALIGN(uint16_t pal_16bpc[8 /* palette_idx */], 16);
                };
            };
            union {
                int8_t levels[33 * 33];
                struct {
                    uint8_t pal_order[64][8];
                    uint8_t pal_ctx[64];
                };
            };
        };
    } scratch;

    uint8_t txtp_map[16 * 16]; // inter-only
    union {
        Dav2dWarpedMotionParams warpmv[2];
        // refined mvs after tip/opfl/refinemv
        union mv rmv[16 * 16 /* y * 16 + x */][2 /* refined, tmv */][2 /* ref */];
    };
    Av2Filter *lf_mask;
    int top_pre_cdef_toggle;
    uint8_t u_has_cf;

    struct {
        enum {
            PASS_ENTROPY = 1 << 0,
            PASS_MVRES   = 1 << 1,
            PASS_RECON   = 1 << 2,
            PASS_ALL     = PASS_ENTROPY | PASS_MVRES | PASS_RECON,
        } pass;
        struct thread_data td;
        struct TaskThreadData *ttd;
        struct FrameTileThreadData *fttd;
        int flushed;
        int die;
    } task_thread;
};

struct OutputQueue *dav2d_queue_output(Dav2dContext *c, Dav2dThreadPicture *p);

#endif /* DAV2D_SRC_INTERNAL_H */
