/*
 * 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 AVCODEC_HW_BASE_ENCODE_H
#define AVCODEC_HW_BASE_ENCODE_H

#include "avcodec.h"
#include "libavutil/hwcontext.h"
#include "libavutil/fifo.h"

#define MAX_DPB_SIZE 16
#define MAX_PICTURE_REFERENCES 2
#define MAX_REORDER_DELAY 16
#define MAX_ASYNC_DEPTH 64
#define MAX_REFERENCE_LIST_NUM 2

static inline const char *ff_hw_base_encode_get_pictype_name(const int type)
{
    const char * const picture_type_name[] = { "IDR", "I", "P", "B" };
    return picture_type_name[type];
}

enum {
    FF_HW_PICTURE_TYPE_IDR = 0,
    FF_HW_PICTURE_TYPE_I   = 1,
    FF_HW_PICTURE_TYPE_P   = 2,
    FF_HW_PICTURE_TYPE_B   = 3,
};

enum {
    // Codec supports controlling the subdivision of pictures into slices.
    FF_HW_FLAG_SLICE_CONTROL         = 1 << 0,
    // Codec only supports constant quality (no rate control).
    FF_HW_FLAG_CONSTANT_QUALITY_ONLY = 1 << 1,
    // Codec is intra-only.
    FF_HW_FLAG_INTRA_ONLY            = 1 << 2,
    // Codec supports B-pictures.
    FF_HW_FLAG_B_PICTURES            = 1 << 3,
    // Codec supports referencing B-pictures.
    FF_HW_FLAG_B_PICTURE_REFERENCES  = 1 << 4,
    // Codec supports non-IDR key pictures (that is, key pictures do
    // not necessarily empty the DPB).
    FF_HW_FLAG_NON_IDR_KEY_PICTURES  = 1 << 5,
};

typedef struct FFHWBaseEncodePicture {
    struct FFHWBaseEncodePicture *next;

    int64_t         display_order;
    int64_t         encode_order;
    int64_t         pts;
    int64_t         duration;
    int             force_idr;

    void           *opaque;
    AVBufferRef    *opaque_ref;

    int             type;
    int             b_depth;
    int             encode_issued;
    int             encode_complete;

    AVFrame        *input_image;
    AVFrame        *recon_image;

    void           *priv_data;

    // Whether this picture is a reference picture.
    int             is_reference;

    // The contents of the DPB after this picture has been decoded.
    // This will contain the picture itself if it is a reference picture,
    // but not if it isn't.
    int                     nb_dpb_pics;
    struct FFHWBaseEncodePicture *dpb[MAX_DPB_SIZE];
    // The reference pictures used in decoding this picture. If they are
    // used by later pictures they will also appear in the DPB. ref[0][] for
    // previous reference frames. ref[1][] for future reference frames.
    int                     nb_refs[MAX_REFERENCE_LIST_NUM];
    struct FFHWBaseEncodePicture *refs[MAX_REFERENCE_LIST_NUM][MAX_PICTURE_REFERENCES];
    // The previous reference picture in encode order.  Must be in at least
    // one of the reference list and DPB list.
    struct FFHWBaseEncodePicture *prev;
    // Reference count for other pictures referring to this one through
    // the above pointers, directly from incomplete pictures and indirectly
    // through completed pictures.
    int             ref_count[2];
    int             ref_removed[2];
} FFHWBaseEncodePicture;

typedef struct FFHWEncodePictureOperation {
    // Alloc memory for the picture structure and initialize the API-specific internals
    // based of the given frame.
    FFHWBaseEncodePicture * (*alloc)(AVCodecContext *avctx, const AVFrame *frame);
    // Issue the picture structure, which will send the frame surface to HW Encode API.
    int (*issue)(AVCodecContext *avctx, const FFHWBaseEncodePicture *base_pic);
    // Get the output AVPacket.
    int (*output)(AVCodecContext *avctx, const FFHWBaseEncodePicture *base_pic, AVPacket *pkt);
    // Free the picture structure.
    int (*free)(AVCodecContext *avctx, FFHWBaseEncodePicture *base_pic);
}  FFHWEncodePictureOperation;

typedef struct FFHWBaseEncodeContext {
    const AVClass *class;
    void  *log_ctx;

    // Hardware-specific hooks.
    const struct FFHWEncodePictureOperation *op;

    // Global options.

    // Number of I frames between IDR frames.
    int             idr_interval;

    // Desired B frame reference depth.
    int             desired_b_depth;

    // The required size of surfaces.  This is probably the input
    // size (AVCodecContext.width|height) aligned up to whatever
    // block size is required by the codec.
    int             surface_width;
    int             surface_height;

    // The block size for slice calculations.
    int             slice_block_width;
    int             slice_block_height;

    // The hardware device context.
    AVBufferRef    *device_ref;
    AVHWDeviceContext *device;

    // The hardware frame context containing the input frames.
    AVBufferRef    *input_frames_ref;
    AVHWFramesContext *input_frames;

    // The hardware frame context containing the reconstructed frames.
    AVBufferRef    *recon_frames_ref;
    AVHWFramesContext *recon_frames;

    // Current encoding window, in display (input) order.
    FFHWBaseEncodePicture *pic_start, *pic_end;
    // The next picture to use as the previous reference picture in
    // encoding order. Order from small to large in encoding order.
    FFHWBaseEncodePicture *next_prev[MAX_PICTURE_REFERENCES];
    int                  nb_next_prev;

    // Next input order index (display order).
    int64_t         input_order;
    // Number of frames that output is behind input.
    int64_t         output_delay;
    // Next encode order index.
    int64_t         encode_order;
    // Number of frames decode output will need to be delayed.
    int64_t         decode_delay;
    // Next output order index (in encode order).
    int64_t         output_order;

    // Timestamp handling.
    int64_t         first_pts;
    int64_t         dts_pts_diff;
    int64_t         ts_ring[MAX_REORDER_DELAY * 3 +
                            MAX_ASYNC_DEPTH];

    // Frame type decision.
    int gop_size;
    int closed_gop;
    int gop_per_idr;
    int p_per_i;
    int max_b_depth;
    int b_per_p;
    int force_idr;
    int idr_counter;
    int gop_counter;
    int end_of_stream;
    int p_to_gpb;

    // Whether the driver supports ROI at all.
    int             roi_allowed;

    // The encoder does not support cropping information, so warn about
    // it the first time we encounter any nonzero crop fields.
    int             crop_warned;
    // If the driver does not support ROI then warn the first time we
    // encounter a frame with ROI side data.
    int             roi_warned;

    // The frame to be filled with data.
    AVFrame         *frame;

    // Whether the HW supports sync buffer function.
    // If supported, encode_fifo/async_depth will be used together.
    // Used for output buffer synchronization.
    int             async_encode;

    // Store buffered pic.
    AVFifo          *encode_fifo;
    // Max number of frame buffered in encoder.
    int             async_depth;

    /** Tail data of a pic, now only used for av1 repeat frame header. */
    AVPacket        *tail_pkt;
} FFHWBaseEncodeContext;

int ff_hw_base_encode_set_output_property(FFHWBaseEncodeContext *ctx, AVCodecContext *avctx,
                                          FFHWBaseEncodePicture *pic, AVPacket *pkt, int flag_no_delay);

int ff_hw_base_encode_receive_packet(FFHWBaseEncodeContext *ctx, AVCodecContext *avctx, AVPacket *pkt);

int ff_hw_base_init_gop_structure(FFHWBaseEncodeContext *ctx, AVCodecContext *avctx,
                                  uint32_t ref_l0, uint32_t ref_l1,
                                  int flags, int prediction_pre_only);

int ff_hw_base_get_recon_format(FFHWBaseEncodeContext *ctx, const void *hwconfig,
                                enum AVPixelFormat *fmt);

int ff_hw_base_encode_free(FFHWBaseEncodePicture *pic);

int ff_hw_base_encode_init(AVCodecContext *avctx, FFHWBaseEncodeContext *ctx);

int ff_hw_base_encode_close(FFHWBaseEncodeContext *ctx);

#define HW_BASE_ENCODE_COMMON_OPTIONS \
    { "idr_interval", \
      "Distance (in I-frames) between key frames", \
      OFFSET(common.base.idr_interval), AV_OPT_TYPE_INT, \
      { .i64 = 0 }, 0, INT_MAX, FLAGS }, \
    { "b_depth", \
      "Maximum B-frame reference depth", \
      OFFSET(common.base.desired_b_depth), AV_OPT_TYPE_INT, \
      { .i64 = 1 }, 1, INT_MAX, FLAGS }, \
    { "async_depth", "Maximum processing parallelism. " \
      "Increase this to improve single channel performance.", \
      OFFSET(common.base.async_depth), AV_OPT_TYPE_INT, \
      { .i64 = 2 }, 1, MAX_ASYNC_DEPTH, FLAGS }

#endif /* AVCODEC_HW_BASE_ENCODE_H */