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

#include "codec_id.h"
#include "decode.h"
#include "hwaccel_internal.h"
#include "internal.h"

#include "vulkan_video.h"

typedef struct FFVulkanDecodeDescriptor {
    enum AVCodecID                   codec_id;
    FFVulkanExtensions               decode_extension;
    VkQueueFlagBits                  queue_flags;
    VkVideoCodecOperationFlagBitsKHR decode_op;

    VkExtensionProperties ext_props;
} FFVulkanDecodeDescriptor;

typedef struct FFVulkanDecodeShared {
    FFVulkanContext s;
    FFVkVideoCommon common;
    AVVulkanDeviceQueueFamily *qf;
    FFVkExecPool exec_pool;

    AVBufferPool *buf_pool;

    VkVideoCapabilitiesKHR caps;
    VkVideoDecodeCapabilitiesKHR dec_caps;

    VkVideoSessionParametersKHR empty_session_params;

    /* Software-defined decoder context */
    void *sd_ctx;
    void (*sd_ctx_free)(struct FFVulkanDecodeShared *ctx);
} FFVulkanDecodeShared;

typedef struct FFVulkanDecodeContext {
    FFVulkanDecodeShared *shared_ctx;
    AVBufferRef *session_params;

    int dedicated_dpb; /* Oddity  #1 - separate DPB images */
    int external_fg;   /* Oddity  #2 - hardware can't apply film grain */

    /* Workaround for NVIDIA drivers tested with CTS version 1.3.8 for AV1.
     * The tests were incorrect as the OrderHints were offset by 1. */
    int quirk_av1_offset;

    /* Thread-local state below */
    struct HEVCHeaderSet *hevc_headers;
    size_t hevc_headers_size;

    uint32_t                       *slice_off;
    unsigned int                    slice_off_max;
} FFVulkanDecodeContext;

typedef struct FFVulkanDecodePicture {
    AVFrame                        *dpb_frame;      /* Only used for out-of-place decoding. */

    struct {
        VkImageView                     ref[AV_NUM_DATA_POINTERS];        /* Image representation view (reference) */
        VkImageView                     out[AV_NUM_DATA_POINTERS];        /* Image representation view (output-only) */
        VkImageView                     dst[AV_NUM_DATA_POINTERS];        /* Set to img_view_out if no layered refs are used */
        VkImageAspectFlags              aspect[AV_NUM_DATA_POINTERS];     /* Image plane mask bits */
        VkImageAspectFlags              aspect_ref[AV_NUM_DATA_POINTERS]; /* Only used for out-of-place decoding */
    } view;

    VkSemaphore                     sem;
    uint64_t                        sem_value;

    /* Current picture */
    VkVideoPictureResourceInfoKHR   ref;
    VkVideoReferenceSlotInfoKHR     ref_slot;

    /* Picture refs. H264 has the maximum number of refs (36) of any supported codec. */
    VkVideoPictureResourceInfoKHR   refs     [36];
    VkVideoReferenceSlotInfoKHR     ref_slots[36];

    /* Main decoding struct */
    VkVideoDecodeInfoKHR            decode_info;

    /* Slice data */
    AVBufferRef                    *slices_buf;
    size_t                          slices_size;

    /* Vulkan functions needed for destruction, as no other context is guaranteed to exist */
    PFN_vkWaitSemaphores            wait_semaphores;
    PFN_vkDestroyImageView          destroy_image_view;
    PFN_vkInvalidateMappedMemoryRanges invalidate_memory_ranges;
} FFVulkanDecodePicture;

/**
 * Initialize decoder.
 */
int ff_vk_decode_init(AVCodecContext *avctx);

/**
 * Synchronize the contexts between 2 threads.
 */
int ff_vk_update_thread_context(AVCodecContext *dst, const AVCodecContext *src);

/**
 * Initialize hw_frames_ctx with the parameters needed to decode the stream
 * using the parameters from avctx.
 *
 * NOTE: if avctx->internal->hwaccel_priv_data exists, will partially initialize
 * the context.
 */
int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx);

/**
 * Removes current session parameters to recreate them
 */
int ff_vk_params_invalidate(AVCodecContext *avctx, int t, const uint8_t *b, uint32_t s);

/**
 * Prepare a frame, creates the image view, and sets up the dpb fields.
 */
int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic,
                               FFVulkanDecodePicture *vkpic, int is_current,
                               int alloc_dpb);

/**
 * Software-defined decoder version of ff_vk_decode_prepare_frame.
 */
int ff_vk_decode_prepare_frame_sdr(FFVulkanDecodeContext *dec, AVFrame *pic,
                                   FFVulkanDecodePicture *vkpic, int is_current,
                                   enum FFVkShaderRepFormat rep_fmt, int alloc_dpb);

/**
 * Add slice data to frame.
 */
int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp,
                           const uint8_t *data, size_t size, int add_startcode,
                           uint32_t *nb_slices, const uint32_t **offsets);

/**
 * Decode a frame.
 */
int ff_vk_decode_frame(AVCodecContext *avctx,
                       AVFrame *pic,    FFVulkanDecodePicture *vp,
                       AVFrame *rpic[], FFVulkanDecodePicture *rvkp[]);

/**
 * Free a frame and its state.
 */
void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, FFVulkanDecodePicture *vp);

/**
 * Get an FFVkBuffer suitable for decoding from.
 */
int ff_vk_get_decode_buffer(FFVulkanDecodeContext *ctx, AVBufferRef **buf,
                            void *create_pNext, size_t size);

/**
 * Create VkVideoSessionParametersKHR wrapped in an AVBufferRef.
 */
int ff_vk_decode_create_params(AVBufferRef **par_ref, void *logctx, FFVulkanDecodeShared *ctx,
                               const VkVideoSessionParametersCreateInfoKHR *session_params_create);

/**
 * Flush decoder.
 */
void ff_vk_decode_flush(AVCodecContext *avctx);

/**
 * Free decoder.
 */
int ff_vk_decode_uninit(AVCodecContext *avctx);

#endif /* AVCODEC_VULKAN_DECODE_H */
