/*
* Copyright (c) 2016-2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file     codechal_encode_hevc.h
//! \brief    Defines base class for HEVC dual-pipe encoder.
//!

#ifndef __CODECHAL_ENCODE_HEVC_H__
#define __CODECHAL_ENCODE_HEVC_H__

#include "codechal_encode_hevc_base.h"
#include "codechal_kernel_hme.h"

#define  HUC_CMD_LIST_MODE 1
#define  HUC_BATCH_BUFFER_END 0x05000000

//! QP type
enum {
    QP_TYPE_CONSTANT = 0,
    QP_TYPE_FRAME,
    QP_TYPE_CU_LEVEL
};

//!
//! \struct HucCommandData
//! \brief  The struct of Huc commands data
//!
struct HucCommandData
{
    uint32_t        TotalCommands;       //!< Total Commands in the Data buffer
    struct
    {
        uint16_t    ID;              //!< Command ID, defined and order must be same as that in DMEM
        uint16_t    SizeOfData;      //!< data size in uint32_t
        uint32_t    data[40];
    } InputCOM[10];
};

//! \class    CodechalEncHevcState
//! \brief    HEVC dual-pipe encoder base class
//! \details  This class defines the base class for HEVC dual-pipe encoder, it includes
//!        common member fields, functions, interfaces etc shared by all GENs.
//!        Gen specific definitions, features should be put into their corresponding classes.
//!        To create a HEVC dual-pipe encoder instance, client needs to call CodechalEncHevcState::CreateHevcState()
//!
class CodechalEncHevcState : public CodechalEncodeHevcBase
{
public:
    //! QP type
    enum {
        QP_TYPE_CONSTANT = 0,
        QP_TYPE_FRAME,
        QP_TYPE_CU_LEVEL
    };

    enum {
        BRCINIT_USEHUCBRC                  = 0x0001,
        BRCINIT_ISCBR                      = 0x0010,
        BRCINIT_ISVBR                      = 0x0020,
        BRCINIT_ISAVBR                     = 0x0040,
        BRCINIT_ISQVBR                     = 0x0080,
        BRCINIT_FIELD_PIC                  = 0x0100,
        BRCINIT_ISICQ                      = 0x0200,
        BRCINIT_ISVCM                      = 0x0400,
        BRCINIT_IGNORE_PICTURE_HEADER_SIZE = 0x2000,
        BRCINIT_ISCQP                      = 0x4000,
        BRCINIT_DISABLE_MBBRC              = 0x8000
    };

    //!
    //! \brief    HEVC BRC buffers
    //!
    struct HevcEncBrcBuffers
    {
        MOS_RESOURCE            resBrcHistoryBuffer;                                                    // BRC history buffer
        MOS_RESOURCE            resBrcPakStatisticBuffer[CODECHAL_ENCODE_RECYCLED_BUFFER_NUM];
        uint32_t                uiCurrBrcPakStasIdxForRead;
        uint32_t                uiCurrBrcPakStasIdxForWrite;
        MOS_RESOURCE            resBrcImageStatesReadBuffer[CODECHAL_ENCODE_RECYCLED_BUFFER_NUM];   // read only BRC image state buffer
        MOS_RESOURCE            resBrcImageStatesWriteBuffer[CODECHAL_ENCODE_RECYCLED_BUFFER_NUM];  //!< Write only HEVC BRC image state buffers
        uint32_t                dwBrcHcpPicStateSize;
        MOS_SURFACE             sBrcConstantDataBuffer[CODECHAL_ENCODE_RECYCLED_BUFFER_NUM];
        MOS_RESOURCE            resMbBrcConstDataBuffer[CODECHAL_ENCODE_RECYCLED_BUFFER_NUM];
        uint32_t                dwBrcConstantSurfaceWidth;
        uint32_t                dwBrcConstantSurfaceHeight;
        MOS_SURFACE             sBrcIntraDistortionBuffer;                      //!< BRC Intra distortion buffer
        MOS_SURFACE             sMeBrcDistortionBuffer;
        uint32_t                dwMeBrcDistortionBottomFieldOffset;
        MOS_SURFACE             sBrcMbQpBuffer;
        uint32_t                dwBrcMbQpBottomFieldOffset;
        MOS_RESOURCE            resBrcPicHeaderInputBuffer;
        MOS_RESOURCE            resBrcPicHeaderOutputBuffer;
        MOS_RESOURCE            resMbEncAdvancedDsh;
        MOS_RESOURCE            resMbEncBrcBuffer;
        MOS_SURFACE             sBrcRoiSurface;                 // BRC ROI surface
        PMOS_SURFACE            pMbStatisticsSurface;
        PCODECHAL_ENCODE_BUFFER pMvAndDistortionSumSurface;
        PMHW_KERNEL_STATE       pMbEncKernelStateInUse;
        CmSurface2D*            brcIntraDistortionSurface = nullptr;
        CmSurface2D*            meBrcDistortionSurface    = nullptr;
        CmBuffer*               mvAndDistortionSumSurface = nullptr;
    };

    //!
    //! \struct EncStatsBuffers
    //! \brief  MbEnc statistic buffers
    //!
    struct EncStatsBuffers
    {
        MOS_SURFACE             m_puStatsSurface;
        MOS_SURFACE             m_8x8PuHaarDist;
        CODECHAL_ENCODE_BUFFER  m_8x8PuFrameStats;
        MOS_SURFACE             m_mbEncStatsSurface;
        CODECHAL_ENCODE_BUFFER  m_mbEncFrameStats;
    };

    static const uint32_t                       m_8x8PuFrameStatsSize = 32;                     //!< The size of 8x8 PU frame statistic buffer
    static const uint32_t                       m_mbEncFrameStatsSize = 32;
    static constexpr uint32_t                   NUM_FORMAT_CONV_FRAMES = (CODECHAL_MAX_CUR_NUM_REF_FRAME_HEVC + 1);  //!< Number of format conversion frames

    uint32_t m_widthAlignedLcu32  = 0;  //!< Picture width aligned to LCU32
    uint32_t m_heightAlignedLcu32 = 0;  //!< Picture height aligned to LCU32

    uint32_t m_numRegionsInSlice = 1;  //!< Number of Regions

    // Resources for the render engine
    MOS_SURFACE m_formatConvertedSurface[NUM_FORMAT_CONV_FRAMES];  //!< // Handle of the format converted surface

    // ME
    CodechalKernelHme                           *m_hmeKernel = nullptr;                         //!< ME kernel object
    PMHW_KERNEL_STATE                            m_meKernelState        = nullptr;                         //!< ME kernel state
    PCODECHAL_ENCODE_BINDING_TABLE_GENERIC       m_meKernelBindingTable = nullptr;                         //!< ME kernel binding table

    // BRC
    PMHW_KERNEL_STATE                           m_brcKernelStates       = nullptr;              //!< Pointer to BRC kernel state
    PCODECHAL_ENCODE_BINDING_TABLE_GENERIC      m_brcKernelBindingTable = nullptr;              //!< BRC kernel binding table
    PMOS_SURFACE                                m_brcDistortion         = nullptr;              //!< Pointer to BRC distortion surface
    HevcEncBrcBuffers                           m_brcBuffers;                                   //!< BRC buffers
    uint32_t                                    m_numBrcKrnStates;                              //!< Number of BRC kernel states
    uint8_t                                     m_slidingWindowSize = 0;                        //!< Sliding window size in number of frames
    bool                                        m_roiRegionSmoothEnabled = false;               //!< ROI region smooth transition enable flag
    HEVC_BRC_FRAME_TYPE                         m_currFrameBrcLevel = HEVC_BRC_FRAME_TYPE_I;    //!< frame brc level

    // MBENC
    PMHW_KERNEL_STATE                           m_mbEncKernelStates       = nullptr;  //!< Pointer to MbEnc kernel state
    PCODECHAL_ENCODE_BINDING_TABLE_GENERIC      m_mbEncKernelBindingTable = nullptr;  //!< MbEnc kernel binding table
    uint32_t                                    m_numMbEncEncKrnStates    = 0;        //!< Number of MbEnc kernel states
    EncStatsBuffers                             m_encStatsBuffers;

    // ScalingAndConversion
    PMHW_KERNEL_STATE                      m_scalingAndConversionKernelState        = nullptr;  //!< Pointer to ScalingAndConversion kernel state
    PCODECHAL_ENCODE_BINDING_TABLE_GENERIC m_scalingAndConversionKernelBindingTable = nullptr;  //!< ScalingAndConversion kernel binding table

    bool m_pakOnlyTest                                       = false;  //!< PAK only test enable flag
    char m_pakOnlyDataFolder[MOS_USER_CONTROL_MAX_DATA_SIZE] = {0};    //!< Pak only test data folder name
    bool m_cqpEnabled                                        = false;  //!< CQP Rate Control
    bool m_sseSupported                                      = false;  //!< PAK SSE support flag

protected:
    //!
    //! \brief    Constructor
    //!
    CodechalEncHevcState(CodechalHwInterface* hwInterface,
        CodechalDebugInterface* debugInterface,
        PCODECHAL_STANDARD_INFO standardInfo);

public:
    //!
    //! \brief    Copy constructor
    //!
    CodechalEncHevcState(const CodechalEncHevcState&) = delete;

    //!
    //! \brief    Copy assignment operator
    //!
    CodechalEncHevcState& operator=(const CodechalEncHevcState&) = delete;

    //!
    //! \brief    Destructor
    //!
    virtual ~CodechalEncHevcState();

    //!
    //! \brief    Help function to initialize surface parameters for 1D buffer
    //!
    //! \param    [in, out] params
    //!           Pointer to surface codec parameters
    //! \param    [in] buffer
    //!           Pointer to buffer resource
    //! \param    [in] size
    //!           Buffer size
    //! \param    [in] offset
    //!           Offset within the buffer
    //! \param    [in] cacheabilityControl
    //!           Buffer cache control setting
    //! \param    [in] bindingTableOffset
    //!           Binding table offset for the buffer
    //! \param    [in] isWritable
    //!           True if buffer is writable, false if it is read only
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS InitSurfaceCodecParams1D(
        CODECHAL_SURFACE_CODEC_PARAMS* params,
        PMOS_RESOURCE buffer,
        uint32_t size,
        uint32_t offset,
        uint32_t cacheabilityControl,
        uint32_t bindingTableOffset,
        bool isWritable);

    //!
    //! \brief    Help function to initialize surface parameters for 2D surface
    //!
    //! \param    [in, out] params
    //!           Pointer to surface codec parameters
    //! \param    [in] surface
    //!           Pointer to surface resource
    //! \param    [in] cacheabilityControl
    //!           Cache control setting for the surface
    //! \param    [in] bindingTableOffset
    //!           Binding table offset for the surface
    //! \param    [in] verticalLineStride
    //!           Vertical line stride for the surface
    //! \param    [in] isWritable
    //!           True if surface is writable, false if it is read only
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS InitSurfaceCodecParams2D(
        CODECHAL_SURFACE_CODEC_PARAMS* params,
        PMOS_SURFACE surface,
        uint32_t cacheabilityControl,
        uint32_t bindingTableOffset,
        uint32_t verticalLineStride,
        bool isWritable);

    //!
    //! \brief    Help function to initialize surface parameters for VME surface
    //!
    //! \param    [in, out] params
    //!           Pointer to surface codec parameters
    //! \param    [in] surface
    //!           Pointer to surface resource
    //! \param    [in] cacheabilityControl
    //!           Cache control setting for the surface
    //! \param    [in] bindingTableOffset
    //!           Binding table offset for the surface
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS InitSurfaceCodecParamsVME(
        CODECHAL_SURFACE_CODEC_PARAMS* params,
        PMOS_SURFACE surface,
        uint32_t cacheabilityControl,
        uint32_t bindingTableOffset);

    //!
    //! \brief    Help function to calcuate ROI Ratio need by BRC Kernel
    //!
    //! \return   ROI ratio
    //!
    uint8_t CalculateROIRatio();

    //!
    //! \brief    Help function to calcuate the temporal difference between current and reference picture
    //!
    //! \param    [in] refPic
    //!           Reference picture.
    //!
    //! \return   Temporal difference between current and reference picture
    //!
    int16_t ComputeTemporalDifference(const CODEC_PICTURE& refPic);

    //!
    //! \brief    Help function to get start code offset
    //! \details  Search the start code from address addr to (addr + size)
    //!
    //! \param    [in] addr
    //!           Pointer to memory location to start searching for start code
    //! \param    [in] size
    //!           End of the memory address to search for start code is [addr + size]
    //!
    //! \return   Offset of start code
    //!
    uint32_t GetStartCodeOffset(uint8_t* addr, uint32_t size);

    //!
    //! \brief    Help function to get picture header size
    //!
    //! \return   Size of picture header
    //!
    uint32_t GetPicHdrSize();

    //!
    //! \brief    Wait for PAK engine ready
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS WaitForPak();

    //!
    //! \brief    Wait for reference frame ready
    //!
    //! \param    [in] mbCodeIdx
    //!           Mb code index for reference frame
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS WaitForRefFrameReady(uint8_t mbCodeIdx);

    //!
    //! \brief    Add HCP_WEIGHT_OFFSET_STATE command to command buffer
    //!
    //! \param    [in, out] cmdBuffer
    //!           Pointer to the command buffer
    //! \param    [in] hevcSlcParams
    //!           Pointer to HEVC slice parameters
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS AddHcpWeightOffsetStateCmd(
        PMOS_COMMAND_BUFFER             cmdBuffer,      
        PMHW_BATCH_BUFFER               batchBuffer,
        PCODEC_HEVC_ENCODE_SLICE_PARAMS hevcSlcParams);

    //!
    //! \brief    Put slice level commands in command buffer
    //!
    //! \param    [in] cmdBuffer
    //!           Pointer to command buffer
    //! \param    [in] params
    //!           Pointer to slice state parameters
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS SendHwSliceEncodeCommand(
        PMOS_COMMAND_BUFFER             cmdBuffer,
        PMHW_VDBOX_HEVC_SLICE_STATE     params);

    //!
    //! \brief    Allocate encoder states resources
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateEncStatsResources();

    //!
    //! \brief    Free encoder states resources
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS FreeEncStatsResources();
    
    //!
    //! \brief    Get Current Frame BRC Level 
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS GetFrameBrcLevel();

    //! Inherited virtual functions
    virtual bool CheckSupportedFormat(PMOS_SURFACE surface);
    virtual MOS_STATUS Initialize(CodechalSetting * settings);
    virtual MOS_STATUS AllocateBrcResources();
    virtual MOS_STATUS FreeBrcResources();
    virtual MOS_STATUS InitializePicture(const EncoderParams& params);
    virtual MOS_STATUS SetSequenceStructs();
    virtual MOS_STATUS SetPictureStructs();
    virtual MOS_STATUS SetSliceStructs();
    virtual MOS_STATUS ReadHcpStatus(PMOS_COMMAND_BUFFER cmdBuffer);
    virtual MOS_STATUS UserFeatureKeyReport();
    virtual MOS_STATUS ValidateRefFrameData(PCODEC_HEVC_ENCODE_SLICE_PARAMS slcParams);
    virtual MOS_STATUS ExecutePictureLevel();
    virtual MOS_STATUS ExecuteSliceLevel();

    //!
    //! \brief    Read stats for BRC from PAK
    //!
    //! \param    [in] cmdBuffer
    //!            Pointer to command buffer
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS ReadBrcPakStats(PMOS_COMMAND_BUFFER cmdBuffer);

    //!
    //! \brief    Setup ME curbe params
    //!
    //! \param    [in, out] curbeParams
    //!           ME curbe params to be initialized in this function
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS SetMeCurbeParams(
        CodechalKernelHme::CurbeParam &curbeParams);

    //!
    //! \brief    Setup ME surface params
    //!
    //! \param    [in] surfaceParams
    //!           ME curbe params
    //! \param    [in, out] surfaceParams
    //!           ME surface params to be initialized in this function
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS SetMeSurfaceParams(
        CodechalKernelHme::SurfaceParams     &surfaceParams);

    //!
    //! \brief    Top level function for ME kernel
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS EncodeMeKernel();

    //!
    //! \brief    Allocate ENC resources when LCU size is 64
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS AllocateEncResourcesLCU64()
    {
        return MOS_STATUS_SUCCESS;
    }

    //!
    //! \brief    Get max supported number of reference frames
    //!
    //! \param    [out] maxNumRef0
    //!           Max suppoted number of referenace frame 0
    //! \param    [out] maxNumRef1
    //!           Max suppoted number of referenace frame 1
    //!
    //! \return   void
    //!
    virtual void GetMaxRefFrames(uint8_t& maxNumRef0, uint8_t& maxNumRef1) = 0;

    //!
    //! \brief    Prepare and add Hcp Pipe Mode Select Cmd
    //!
    //! \param    [out] cmdBuffer
    //!           CmdBuffer to add the cmd
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS AddHcpPipeModeSelectCmd(MOS_COMMAND_BUFFER* cmdBuffer);

    //!
    //! \brief    Prepare and add all Hcp Surface State Cmds
    //!
    //! \param    [out] cmdBuffer
    //!           CmdBuffer to add the cmd
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS AddHcpSurfaceStateCmds(MOS_COMMAND_BUFFER* cmdBuffer);

    //!
    //! \brief    Prepare and add Hcp Picture State Cmd
    //!
    //! \param    [out] cmdBuffer
    //!           CmdBuffer to add the cmd
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS AddHcpPictureStateCmd(MOS_COMMAND_BUFFER* cmdBuffer);

    //!
    //! \brief    Create ROI surfaces for BRC LCU Update kernel
    //!           
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS SetupROISurface();
    //!
    //! \brief    Generate codechal dumps for HME kernel
    //!           
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS DumpHMESurfaces();
};
#endif  // __CODECHAL_ENCODE_HEVC_H__
