/*
* Copyright (c) 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_tracked_buffer.h
//!
//! \brief    Encoder needs to track and store certain surface/resource/buffers, to be used as reference for future frames
//!           Given the refFrame list of current frame, a slot is dynamically allocated to index/track these resources
//!           Whenever a RefFrame is no longer used, its corresponding slot will be re-used to minimize memory allocation
//!           "Tracked buffer" so far includes MbCode/MvData/MvTemporal buffer, and Csc/Ds/DsRecon surface
//!

#ifndef __CODECHAL_ENCODE_TRACKED_BUFFER_H__
#define __CODECHAL_ENCODE_TRACKED_BUFFER_H__

#include "codechal.h"
#include "codechal_encode_allocator.h"
#include "codec_def_common_encode.h"

//!
//! Tracked buffer 
//!
class CodechalEncodeTrackedBuffer
{
public:
    //!
    //! \brief  Get the buffer index allocated for current frame
    //!
    //! \return the buffer index allocated for current frame
    //!
    inline uint8_t GetCurrIndex()
    {
        return m_trackedBufCurrIdx;
    }

    //!
    //! \brief  Get the buffer index allocated for MbCode
    //!
    //! \return the buffer index allocated for MbCode
    //!
    inline uint8_t GetCurrIndexMbCode()
    {
        return m_mbCodeCurrIdx;
    }

    //!
    //! \brief  Get the current MbCode buffer
    //!
    //! \return the current MbCode buffer
    //!
    inline MOS_RESOURCE* GetCurrMbCodeBuffer()
    {
        return m_trackedBufCurrMbCode;
    }

    //!
    //! \brief  Get the current MvData buffer
    //!
    //! \return the current MvData buffer
    //!
    inline MOS_RESOURCE* GetCurrMvDataBuffer()
    {
        return m_trackedBufCurrMvData;
    }

    //!
    //! \brief  Get the current MV temporal buffer
    //!
    //! \return the current MV temporal buffer
    //!
    MOS_RESOURCE* GetMvTemporalBuffer(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurrMvTemporal;
        }
        else
        {
            return  (MOS_RESOURCE*)m_allocator->GetResource(m_standard, mvTemporalBuffer, bufIndex);
        }
    }

    //!
    //! \brief  Get the current CSC surface
    //!
    //! \return the current CSC surface
    //!
    MOS_SURFACE* GetCscSurface(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurrCsc;
        }
        else
        {
            return (MOS_SURFACE*)m_allocator->GetResource(m_standard, cscSurface, bufIndex);
        }
    }

    //!
    //! \brief  Get the current 4x downscaled surface
    //!
    //! \return the current 4x downscaled surface
    //!
    MOS_SURFACE* Get4xDsSurface(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurrDs4x;
        }
        else
        {
            return  (MOS_SURFACE*)m_allocator->GetResource(m_standard, ds4xSurface, bufIndex);
        }
    }

    //!
    //! \brief  Get the current 2x downscaled surface
    //!
    //! \return the current 2x downscaled surface
    //!
    MOS_SURFACE* Get2xDsSurface(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurrDs2x;
        }
        else
        {
            return  (MOS_SURFACE*)m_allocator->GetResource(m_standard, ds2xSurface, bufIndex);
        }
    }

    //!
    //! \brief  Get the current 16x downscaled surface
    //!
    //! \return the current 16x downscaled surface
    //!
    MOS_SURFACE* Get16xDsSurface(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurrDs16x;
        }
        else
        {
            return  (MOS_SURFACE*)m_allocator->GetResource(m_standard, ds16xSurface, bufIndex);
        }
    }

    //!
    //! \brief  Get the current 32x downscaled surface
    //!
    //! \return the current 32x downscaled surface
    //!
    MOS_SURFACE* Get32xDsSurface(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurrDs32x;
        }
        else
        {
            return  (MOS_SURFACE*)m_allocator->GetResource(m_standard, ds32xSurface, bufIndex);
        }
    }

    //!
    //! \brief  Get the current 4x DsRecon surface
    //!
    //! \return the current 4x DsRecon buffer
    //!
    inline MOS_SURFACE* Get4xDsReconSurface(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurr4xDsRecon;
        }
        else
        {
            return (MOS_SURFACE*)m_allocator->GetResource(m_standard, ds4xRecon, bufIndex);
        }
    }

    //!
    //! \brief  Get the current 8x DsRecon surface
    //!
    //! \return the current 8x DsRecon buffer
    //!
    inline MOS_SURFACE* Get8xDsReconSurface(uint8_t bufIndex)
    {
        if (bufIndex == CODEC_CURR_TRACKED_BUFFER)
        {
            return m_trackedBufCurr8xDsRecon;
        }
        else
        {
            return (MOS_SURFACE*)m_allocator->GetResource(m_standard, ds8xRecon, bufIndex);
        }
    }

    //!
    //! \brief  Get the wait flag for tracked buffer
    //!
    inline bool GetWait()
    {
        return m_waitTrackedBuffer;
    }

    //!
    //! \brief  Get the wait flag for CSC surface
    //!
    inline bool GetWaitCsc()
    {
        return m_waitCscSurface;
    }

    //!
    //! \brief  Get the allocation flag
    //!
    inline bool IsMbCodeAllocationNeeded()
    {
        return m_allocateMbCode;
    }

    //!
    //! \brief  Set allocation flag
    //!
    inline void SetAllocationFlag(bool flag)
    {
        m_allocateMbCode = flag;
    }

    //!
    //! \brief  Allocate resource for current frame
    //!
    //! \return MOS_STATUS
    //!         MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateForCurrFrame();

    //!
    //! \brief  Resize the Tracked Buffer upon resolution change
    //!
    //! \return void
    //!
    void Resize();

    //!
    //! \brief  Release the existing CSC surfaces upon resolution change
    //!
    //! \return void
    //!
    void ResizeCsc();

    //!
    //! \brief    Allocate for Preenc, so far only set the buffer index for DS surface
    //! \param    [in] bufIndex
    //!           buffer index for DS surface
    //!
    //! \return   void
    //!
    void AllocateForCurrFramePreenc(uint8_t bufIndex);

    //!
    //! \brief  Reset used for current frame flag
    //!
    void ResetUsedForCurrFrame();

    //!
    //! \brief    Encoder pre enc look up buffer index
    //! \param    [in] frameIdx
    //!           frame Index
    //! \param    [in] inCache
    //!           Indicate if it's in cache
    //! \return   uint8_t
    //!           emptyEntry
    //!
    uint8_t PreencLookUpBufIndex(
        uint8_t         frameIdx,
        bool            *inCache);

    //!
    //! \brief  Allocate Mb Code Resources
    //!
    //! \param  [in] bufIndex
    //!         buffer index used
    //!
    //! \return MOS_STATUS
    //!         MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateMbCodeResources(uint8_t bufIndex);

    //!
    //! \brief  Allocate Mv Data Resources
    //!
    //! \param  [in] bufIndex
    //!         buffer index used
    //!
    //! \return MOS_STATUS
    //!         MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateMvDataResources(uint8_t bufIndex);

    //!
    //! \brief    Allocate CSC surface or pick an existing one from the pool
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateSurfaceCsc();

    //!
    //! \brief    Allocate Copy surface or pick an existing one from the pool
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateSurfaceCopy(MOS_FORMAT, uint32_t);

    MOS_STATUS ResizeSurfaceDS();

    MOS_STATUS ResizeDsReconSurfacesVdenc();

    //!
    //! \brief    Allocate DS surface or pick an existing one from the pool
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateSurfaceDS();

    //!
    //! \brief    Allocate 2xDS surface or pick an existing one from the pool
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateSurface2xDS();

    //!
    //! \brief  Allocate Mv temporal buffer
    //!
    //! \param  [in] bufIndex
    //!         buffer index used
    //!
    //! \return MOS_STATUS
    //!         MOS_STATUS_SUCCESS if success, else fail reason
    //!
    virtual MOS_STATUS AllocateMvTemporalBuffer(uint8_t bufIndex);

    //!
    //! \brief    Constructor
    //!
    CodechalEncodeTrackedBuffer(CodechalEncoderState* encoder);

    virtual ~CodechalEncodeTrackedBuffer();

protected:
    //!
    //! \brief  Get slot for current frame
    //!
    //! \param  [in] refList
    //!         Reference frame list
    //! \param  [in] numRefFrame
    //!         Number of reference frame
    //! \param  [in] usedAsRef
    //!         Current frame is used as reference or not
    //!
    //! \return uint8_t
    //!         Index found
    //!
    uint8_t LookUpBufIndex(
        PCODEC_PICTURE refList,
        uint8_t        numRefFrame,
        bool           usedAsRef);

    //!
    //! \brief  Get slot for current frame's CSC surface
    //!
    //! \return uint8_t
    //!         Index found
    //!
    uint8_t LookUpBufIndexCsc();

    //!
    //! \brief  Release MbCode buffer
    //!
    //! \param  [in] bufIndex
    //!         buffer index to be released
    //!
    //! \return void
    //!
    void ReleaseMbCode(uint8_t bufIndex);

    //!
    //! \brief  Release MvData buffer
    //!
    //! \param  [in] bufIndex
    //!         buffer index to be released
    //!
    //! \return void
    //!
    void ReleaseMvData(uint8_t bufIndex);

    //!
    //! \brief    Release CSC surface
    //!
    //! \return   MOS_STATUS
    //!           MOS_STATUS_SUCCESS if success, else fail reason
    //!
    void ReleaseSurfaceCsc(uint8_t index);

    //!
    //! \brief  Release DS surface
    //!
    //! \param  [in] index
    //!         buffer index to be released
    //!
    //! \return void
    //!
    void ReleaseSurfaceDS(uint8_t index);

    //!
    //! \brief  Release DsRecon buffer
    //!
    //! \param  [in] bufIndex
    //!         buffer index to be released
    //!
    //! \return void
    //!
    void ReleaseDsRecon(uint8_t bufIndex);

    //!
    //! \brief  Defer-deallocate buffer used before resolution reset
    //!
    virtual void DeferredDeallocateOnResChange();

    CodechalEncoderState*           m_encoder = nullptr;                        //!< Pointer to ENCODER base class
    CodechalEncodeAllocator*        m_allocator = nullptr;                      //!< Pointer to resource allocator

    MOS_RESOURCE*                   m_trackedBufCurrMbCode = nullptr;           //!< Pointer to current MbCode buffer
    MOS_RESOURCE*                   m_trackedBufCurrMvData = nullptr;           //!< Pointer to current MvData buffer
    MOS_RESOURCE*                   m_trackedBufCurrMvTemporal = nullptr;       //!< Pointer to current MV temporal buffer
    MOS_SURFACE*                    m_trackedBufCurrCsc = nullptr;              //!< Pointer to current CSC surface
    MOS_SURFACE*                    m_trackedBufCurrDs4x = nullptr;             //!< Pointer to current 4x downscaled surface
    MOS_SURFACE*                    m_trackedBufCurrDs2x = nullptr;             //!< Pointer to current 2x downscaled surface
    MOS_SURFACE*                    m_trackedBufCurrDs16x = nullptr;            //!< Pointer to current 16x downscaled surface
    MOS_SURFACE*                    m_trackedBufCurrDs32x = nullptr;            //!< Pointer to current 32x downscaled surface
    MOS_SURFACE*                    m_trackedBufCurr4xDsRecon = nullptr;        //!< Pointer to current 4x DsRecon buffer
    MOS_SURFACE*                    m_trackedBufCurr8xDsRecon = nullptr;        //!< Pointer to current 8x DsRecon buffer

    uint32_t                        m_standard;                                 //!< The encode state's standard
    uint8_t                         m_trackedBufCurrIdx = 0;                    //!< current tracked buffer index
    uint8_t                         m_mbCodeCurrIdx = 0;                        //!< current MbCode buffer index
    bool                            m_allocateMbCode = false;                   //!< need to allocate MbCode buffer for current frame
    bool                            m_mbCodeIsTracked = true;                   //!< tracked buffer algorithm used to manage MbCode buffer

private:
    CodechalEncodeTrackedBuffer(const CodechalEncodeTrackedBuffer&) = delete;
    CodechalEncodeTrackedBuffer& operator=(const CodechalEncodeTrackedBuffer&) = delete;

    virtual void LookUpBufIndexMbCode()
    {
        m_mbCodeCurrIdx = m_trackedBufCurrIdx;
    }

    //!
    //! \brief  Allocate Ds Recon Surfaces Vdenc
    //!
    //! \param  [in] bufIndex
    //!         buffer index used
    //!
    //! \return MOS_STATUS
    //!         MOS_STATUS_SUCCESS if success, else fail reason
    //!
    MOS_STATUS AllocateDsReconSurfacesVdenc();

    MOS_INTERFACE*          m_osInterface = nullptr;                            //!< OS interface

    uint8_t                 m_trackedBufNonRefIdx = 0;                          //!< current tracked buffer index when frame won't be used as ref
    uint8_t                 m_trackedBufCountNonRef = 0;                        //!< counting number of tracked buffer when ring buffer is used
    uint8_t                 m_trackedBufCountResize = 0;                        //!< 3 buffers to be delay-destructed during res change
    uint8_t                 m_trackedBufPenuIdx = 0;                            //!< 2nd-to-last tracked buffer index
    uint8_t                 m_trackedBufAnteIdx = 0;                            //!< 3rd-to-last tracked buffer index
    bool                    m_waitTrackedBuffer = false;                        //!< wait to re-use tracked buffer

    uint8_t                 m_cscBufNonRefIdx = 0;                              //!< current CSC buffer index when ring buffer is used
    uint8_t                 m_cscBufCountNonRef = 0;                            //!< counting number of CSC surface when ring buffer is used
    uint8_t                 m_cscBufCurrIdx = 0;                                //!< curr copy buffer index
    uint8_t                 m_cscBufPenuIdx = 0;                                //!< 2nd-to-last CSC buffer index
    uint8_t                 m_cscBufAnteIdx = 0;                                //!< 3rd-to-last CSC buffer index
    bool                    m_waitCscSurface = false;                           //!< wait to re-use CSC surface

    struct tracker
    {
        uint8_t             ucSurfIndex7bits;                                   //!< 0xFF means the entry can be re-used
        bool                bUsedforCurFrame;                                   //!< Used for FEI Preenc to mark whether this enty can be reused in multi-call case
    };
    tracker                 m_tracker[CODEC_NUM_TRACKED_BUFFERS];
};

#endif  // __CODECHAL_ENCODE_TRACKED_BUFFER_H__