// Copyright (c) 2012-2021 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.

#include "umc_defs.h"
#ifdef MFX_ENABLE_H265_VIDEO_DECODE

#ifndef __UMC_H265_FRAME_H__
#define __UMC_H265_FRAME_H__

#include <stdlib.h>
#include "umc_h265_yuv.h"
#include "umc_h265_notify.h"
#include "umc_h265_heap.h"

namespace UMC_HEVC_DECODER
{
class H265Slice;
class H265DecoderFrameInfo;
class H265CodingUnit;

// Struct containing list 0 and list 1 reference picture lists for one slice.
// Length is plus 1 to provide for null termination.
class H265DecoderRefPicList
{
public:

    struct ReferenceInformation // flags use for reference frames of slice
    {
        H265DecoderFrame * refFrame;
        bool isLongReference;
    };

    ReferenceInformation *m_refPicList;

    H265DecoderRefPicList()
    {
        memset(m_refPicList1, 0, sizeof(m_refPicList1));

        m_refPicList = &(m_refPicList1[1]);

        m_refPicList1[0].refFrame = 0;
        m_refPicList1[0].isLongReference = 0;
    }

    void Reset()
    {
        memset(&m_refPicList1, 0, sizeof(m_refPicList1));
    }

    H265DecoderRefPicList (const H265DecoderRefPicList& copy)
    {
        m_refPicList = &(m_refPicList1[1]);
        MFX_INTERNAL_CPY(&m_refPicList1, &copy.m_refPicList1, sizeof(m_refPicList1));
    }

    H265DecoderRefPicList& operator=(const H265DecoderRefPicList & copy)
    {
        MFX_INTERNAL_CPY(&m_refPicList1, &copy.m_refPicList1, sizeof(m_refPicList1));
        return *this;
    }

private:
    ReferenceInformation m_refPicList1[MAX_NUM_REF_PICS + 3];
};

// HEVC decoder frame class
class H265DecoderFrame : public H265DecYUVBufferPadded, public RefCounter
{
public:

    int32_t  m_PicOrderCnt;    // Display order picture count mod MAX_PIC_ORDER_CNT.

    int32_t  m_frameOrder;
    int32_t  m_ErrorType;

    bool prepared;

    H265DecoderFrameInfo * GetAU() const
    {
        return m_pSlicesInfo;
    }

    H265DecoderFrame *m_pPreviousFrame;
    H265DecoderFrame *m_pFutureFrame;

    H265SEIPayLoad m_UserData;
    H265SEIPayLoad m_mastering_display;
    H265SEIPayLoad m_content_light_level_info;

    double           m_dFrameTime;
    bool             m_isOriginalPTS;

    DisplayPictureStruct_H265  m_DisplayPictureStruct_H265;

    // For type 1 calculation of m_PicOrderCnt. m_FrameNum is needed to
    // be used as previous frame num.

    bool post_procces_complete;

    int32_t m_index;
    int32_t m_UID;
    UMC::FrameType m_FrameType;

    UMC::MemID m_MemID;

    int32_t           m_RefPicListResetCount;
    int32_t           m_crop_left;
    int32_t           m_crop_right;
    int32_t           m_crop_top;
    int32_t           m_crop_bottom;
    int32_t           m_crop_flag;

    int32_t           m_aspect_width;
    int32_t           m_aspect_height;

    bool             m_pic_output;

    bool             m_isShortTermRef;
    bool             m_isLongTermRef;
    bool             m_isUsedAsReference;

    // Returns whether frame has all slices found
    bool IsFullFrame() const;
    // Set frame flag denoting that all slices for it were found
    void SetFullFrame(bool isFull);

    struct
    {
        uint8_t  isFull    : 1;
        uint8_t  isDecoded : 1;
        uint8_t  isDecodingStarted : 1;
        uint8_t  isDecodingCompleted : 1;
    } m_Flags;

    uint8_t  m_isDisplayable;
    uint8_t  m_wasDisplayed;
    uint8_t  m_wasOutputted;
    int32_t m_maxUIDWhenWasDisplayed;

    typedef std::list<RefCounter *>  ReferenceList;
    ReferenceList m_references;

    // Add target frame to the list of reference frames
    void AddReferenceFrame(H265DecoderFrame * frm);
    // Clear all references to other frames
    void FreeReferenceFrames();

    // Reinitialize frame structure before reusing frame
    void Reset();
    // Clean up data after decoding is done
    void FreeResources();

    // Accelerator for getting 'surface Index' FrameMID
    inline int32_t GetFrameMID() const
    {
        return m_frameData.GetFrameMID();
    }
public:
    // Delete unneeded references and set flags after decoding is done
    void OnDecodingCompleted();

    // Free resources if possible
    virtual void Free();

    // Returns whether frame has been decoded
    bool IsDecoded() const;

    H265DecoderFrame(UMC::MemoryAllocator *pMemoryAllocator, Heap_Objects * pObjHeap);

    virtual ~H265DecoderFrame();

    // The following methods provide access to the HEVC Decoder's doubly
    // linked list of H265DecoderFrames.  Note that m_pPreviousFrame can
    // be non-NULL even for an I frame.
    H265DecoderFrame *future() const  { return m_pFutureFrame; }

    void setPrevious(H265DecoderFrame *pPrev)
    {
        m_pPreviousFrame = pPrev;
    }

    void setFuture(H265DecoderFrame *pFut)
    {
        m_pFutureFrame = pFut;
    }

    bool        isDisplayable() const { return m_isDisplayable != 0; }

    void        SetisDisplayable(bool isDisplayable)
    {
        m_isDisplayable = isDisplayable ? 1 : 0;
    }

    bool IsDecodingStarted() const { return m_Flags.isDecodingStarted != 0;}
    void StartDecoding() { m_Flags.isDecodingStarted = 1;}

    bool IsDecodingCompleted() const { return m_Flags.isDecodingCompleted != 0;}
    // Flag frame as completely decoded
    void CompleteDecoding();

    // Check reference frames for error status and flag this frame if error is found
    void UpdateErrorWithRefFrameStatus();

    bool        wasDisplayed() const { return m_wasDisplayed != 0; }
    void        setWasDisplayed() { m_wasDisplayed = 1; }

    bool        wasOutputted() const { return m_wasOutputted != 0; }
    void        setWasOutputted(); // Flag frame after it was output

    bool        isDisposable() const { return (!m_isShortTermRef &&
                                               !m_isLongTermRef &&
                                               ((m_wasOutputted != 0 && m_wasDisplayed != 0) || (m_isDisplayable == 0)) &&
                                               !GetRefCounter()); }

    bool isShortTermRef() const
    {
        return m_isShortTermRef;
    }

    // Mark frame as short term reference frame
    void SetisShortTermRef(bool isRef);

    int32_t PicOrderCnt() const
    {
        return m_PicOrderCnt;
    }
    void setPicOrderCnt(int32_t PicOrderCnt)
    {
        m_PicOrderCnt = PicOrderCnt;
    }

    bool isLongTermRef() const
    {
        return m_isLongTermRef;
    }

    // Mark frame as long term reference frame
    void SetisLongTermRef(bool isRef);

    void IncreaseRefPicListResetCount()
    {
        m_RefPicListResetCount++;
    }

    void InitRefPicListResetCount()
    {
        m_RefPicListResetCount = 0;
    }

    int32_t RefPicListResetCount() const
    {
        return m_RefPicListResetCount;
    }

    // GetRefPicList
    // Returns pointer to start of specified ref pic list.
    H265_FORCEINLINE const H265DecoderRefPicList* GetRefPicList(int32_t sliceNumber, int32_t list) const
    {
        assert(list <= REF_PIC_LIST_1 && list >= 0);

        if (sliceNumber >= (int32_t)m_refPicList.size())
        {
            return 0;
        }

        return &m_refPicList[sliceNumber].m_refPicList[list];
    }

    int32_t GetError() const
    {
        return m_ErrorType;
    }

    void SetError(int32_t errorType)
    {
        m_ErrorType = errorType;
    }

    void SetErrorFlagged(int32_t errorType)
    {
        m_ErrorType |= errorType;
    }

    void AddSlice(H265Slice * pSlice);

protected:
    H265DecoderFrameInfo * m_pSlicesInfo;

    // Declare memory management tools
    UMC::MemoryAllocator *m_pMemoryAllocator;   // should be removed because it duplicated in base class

    Heap_Objects * m_pObjHeap;

    struct H265DecoderRefPicListPair
    {
    public:
        H265DecoderRefPicList m_refPicList[2];
    };

    // ML: OPT: std::vector<> results with relatively slow access code
    std::vector<H265DecoderRefPicListPair> m_refPicList;

    class FakeFrameInitializer
    {
    public:
        FakeFrameInitializer();
    };

    static FakeFrameInitializer g_FakeFrameInitializer;

    bool CheckReferenceFrameError();
};

// Returns if frame is not needed by decoder
inline bool isAlmostDisposable(H265DecoderFrame * pTmp)
{
    return (!pTmp->m_isShortTermRef &&
        !pTmp->m_isLongTermRef &&
        ((pTmp->m_wasOutputted != 0) || (pTmp->m_isDisplayable == 0)) &&
        !pTmp->GetRefCounter());
}

// Returns if frame is not needed by decoder
inline bool isInDisplayngStage(H265DecoderFrame * pTmp)
{
    return (pTmp->m_isDisplayable && pTmp->m_wasOutputted && !pTmp->m_wasDisplayed);
}

} // end namespace UMC_HEVC_DECODER

#endif // __UMC_H265_FRAME_H__
#endif // MFX_ENABLE_H265_VIDEO_DECODE
