/*
 * Copyright 2015 Rockchip Electronics Co. LTD
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define MODULE_TAG  "h265e_dpb"

#include <string.h>

#include "mpp_mem.h"
#include "mpp_debug.h"
#include "mpp_common.h"

#include "h265e_codec.h"
#include "h265e_dpb.h"
#include "h265e_slice.h"

void h265e_dpb_dump_frm(H265eDpb *dpb, const char *fmt)
{
    RK_S32 i = 0;
    char buf[256];
    RK_S32 pos = 0;
    RK_S32 frm_cnt = MPP_ARRAY_ELEMS(dpb->frame_list);

    pos += snprintf(buf, sizeof(buf) - 1, "total %2d ", frm_cnt);

    for (i = 0; i < frm_cnt; i++) {
        H265eDpbFrm *frm = &dpb->frame_list[i];

        pos += snprintf(buf + pos, sizeof(buf) - 1 - pos, "%04x ", frm->on_used);
    }
    mpp_log("%20s %s", fmt, buf);
}

MPP_RET calc_ref_pic_set_idxl0(H265eDpb *dpb, H265eSlice *slice, RK_S32 ref_idx)
{
    H265eReferencePictureSet * rps = (H265eReferencePictureSet*)&slice->m_localRPS;
    H265eDpbFrm *frame_list = dpb->frame_list;
    H265eRpsList *RpsList = &dpb->RpsList;
    RK_S32 poc_idx = rps->m_RealPoc[ref_idx];
    H265eDpbFrm* refPicSetLtCurr[MAX_REFS];
    H265eDpbFrm* refPic = NULL;
    RK_S32 numPocLtCurr = 0;
    RK_S32 i = 0;

    for (i = rps->num_negative_pic + rps->num_positive_pic + rps->num_long_term_pic - 1;
         i > rps->num_negative_pic + rps->num_positive_pic - 1; i--) {
        if (rps->m_used[i]) {
            refPic = get_lt_ref_pic(frame_list, slice, rps->m_RealPoc[i], rps->check_lt_msb[i]);
            refPicSetLtCurr[numPocLtCurr] = refPic;
            numPocLtCurr++;
        }
    }

    RpsList->m_RefPicListModification->m_RefPicSetIdxL0[0] = ref_idx;
    for (i = 0; i < numPocLtCurr; i++) {
        if (poc_idx == refPicSetLtCurr[i]->poc)
            RpsList->m_RefPicListModification->m_RefPicSetIdxL0[0] = rps->m_numberOfPictures - rps->num_long_term_pic + i;
    }

    return MPP_OK;
}

void h265e_dpb_set_ref_list(H265eDpb *dpb, H265eSlice *slice, RK_S32 delta_poc)
{
    RK_S32 i;
    RK_S32 ref_idx = -1;
    RK_S32 lt_cnt = 0, st_cnt = 0;
    H265eRpsList *RpsList = &dpb->RpsList;
    H265eReferencePictureSet * m_pRps = (H265eReferencePictureSet*)&slice->m_localRPS;
    H265eRefPicListModification* refPicListModification = RpsList->m_RefPicListModification;

    h265e_dbg_func("enter\n");
    refPicListModification->m_refPicListModificationFlagL0 = 0;
    refPicListModification->m_refPicListModificationFlagL1 = 0;

    for (i = 0; i < REF_PIC_LIST_NUM_IDX; i ++) {
        refPicListModification->m_RefPicSetIdxL0[i] = 0;
        refPicListModification->m_RefPicSetIdxL0[i] = 0;
    }

    refPicListModification->m_refPicListModificationFlagL0 = 0;

    if (m_pRps->m_numberOfPictures > 1) {
        for (i = 0; i < m_pRps->m_numberOfPictures; i++) {
            h265e_dbg_dpb("m_pRps->delta_poc[%d] = %d", i, m_pRps->delta_poc[i]);
            if (m_pRps->delta_poc[i] == delta_poc) {
                ref_idx = i;
                if (i > m_pRps->m_numberOfPictures - m_pRps->num_long_term_pic - 1)
                    lt_cnt++;
                else
                    st_cnt++;
                h265e_dbg_dpb("get %s ref ref_idx %d delta_poc %d", st_cnt ? "st" : "lt", ref_idx, delta_poc);
            }
        }
        if (lt_cnt != 1 && st_cnt == 0) {
            mpp_err("Warning: Did not find the right long term reference picture or more than one.");
            return;
        } else if (ref_idx != 0) {
            refPicListModification->m_refPicListModificationFlagL0 = 1;
            calc_ref_pic_set_idxl0(dpb, slice, ref_idx);
        }
    }
    refPicListModification->m_refPicListModificationFlagL1 = 0;
    h265e_dbg_func("leave\n");
}

/* get buffer at init */
MPP_RET h265e_dpb_init_curr(H265eDpb *dpb, H265eDpbFrm *frm)
{
    h265e_dbg_func("enter\n");
    mpp_assert(!frm->on_used);

    frm->dpb = dpb;

    if (!frm->slice) {
        frm->slice = mpp_calloc(H265eSlice, 1);
    }

    frm->inited = 1;
    frm->dpb_used = 1;
    frm->seq_idx = dpb->seq_idx;
    dpb->seq_idx++;

    h265e_dbg_func("leave\n");
    return MPP_OK;
}

MPP_RET h265e_dpb_get_curr(H265eDpb *dpb)
{
    RK_U32 i;

    h265e_dbg_func("enter\n");
    for (i = 0; i < MPP_ARRAY_ELEMS(dpb->frame_list); i++) {
        if (!dpb->frame_list[i].on_used) {
            dpb->curr = &dpb->frame_list[i];
            h265e_dbg_dpb("get free dpb slot_index %d", dpb->curr->slot_idx);
            break;
        }
    }
    h265e_dpb_init_curr(dpb, dpb->curr);
    h265e_dbg_func("leave\n");
    return MPP_OK;
}

/* put buffer at deinit */
MPP_RET h265e_dpb_frm_deinit(H265eDpbFrm *frm)
{
    MPP_FREE(frm->slice);
    frm->inited = 0;
    h265e_dbg_func("leave\n");
    return MPP_OK;
}

MPP_RET h265e_dpb_init(H265eDpb **dpb)
{
    MPP_RET ret = MPP_OK;
    H265eDpb *p = NULL;
    RK_U32 i;

    h265e_dbg_func("enter\n");
    if (NULL == dpb) {
        mpp_err_f("invalid parameter %p \n", dpb);
        return MPP_ERR_VALUE;
    }

    p = mpp_calloc_size(H265eDpb, sizeof(H265eDpb));

    if (NULL == p)
        return MPP_ERR_MALLOC;

    p->last_idr = 0;
    p->poc_cra = 0;
    p->max_ref_l0 = 1;
    p->max_ref_l1 = 0;

    H265eRpsList *rps_list = &p->RpsList;

    rps_list->lt_num = 0;
    rps_list->st_num = 0;

    memset(rps_list->poc, 0, sizeof(rps_list->poc));

    rps_list->m_RefPicListModification = mpp_calloc(H265eRefPicListModification, 1);


    for (i = 0; i < MPP_ARRAY_ELEMS(p->frame_list); i++)
        p->frame_list[i].slot_idx = i;

    mpp_assert(dpb);
    *dpb = p;
    h265e_dbg_func("leave\n");
    return ret;
}

MPP_RET h265e_dpb_deinit(H265eDpb *dpb)
{
    RK_U32 i;

    if (NULL == dpb)
        return MPP_OK;

    h265e_dbg_func("enter\n");
    for (i = 0; i < MPP_ARRAY_ELEMS(dpb->frame_list); i++) {
        if (dpb->frame_list[i].inited)
            h265e_dpb_frm_deinit(&dpb->frame_list[i]);
    }

    MPP_FREE(dpb->RpsList.m_RefPicListModification);

    MPP_FREE(dpb);

    h265e_dbg_func("leave\n");
    return MPP_OK;
}

enum NALUnitType get_nal_unit_type(H265eDpb *dpb, int curPOC)
{
    h265e_dbg_func("enter\n");
    if (curPOC == 0) {
        return NAL_IDR_W_RADL;
    }
    if (dpb->curr->is_key_frame) {
        return NAL_IDR_W_RADL;
    }
    if (dpb->poc_cra > 0) {
        if (curPOC < dpb->poc_cra) {
            // All leading pictures are being marked as TFD pictures here since current encoder uses all
            // reference pictures while encoding leading pictures. An encoder can ensure that a leading
            // picture can be still decodable when random accessing to a CRA/CRANT/BLA/BLANT picture by
            // controlling the reference pictures used for encoding that leading picture. Such a leading
            // picture need not be marked as a TFD picture.
            return NAL_RASL_R;
        }
    }
    if (dpb->last_idr > 0) {
        if (curPOC < dpb->last_idr) {
            return NAL_RADL_R;
        }
    }

    h265e_dbg_func("leave\n");
    return NAL_TRAIL_R;
}

static inline int getLSB(int poc, int maxLSB)
{
    if (poc >= 0) {
        return poc % maxLSB;
    } else {
        return (maxLSB - ((-poc) % maxLSB)) % maxLSB;
    }
}

void sort_delta_poc(H265eReferencePictureSet *rps)
{
    // sort in increasing order (smallest first)
    RK_S32 j, k;
    for (j = 1; j < rps->m_numberOfPictures; j++) {
        RK_S32 deltaPOC = rps->delta_poc[j];
        RK_U32 used = rps->m_used[j];
        RK_U32 refed = rps->m_ref[j];

        for (k = j - 1; k >= 0; k--) {
            int temp = rps->delta_poc[k];
            if (deltaPOC < temp) {
                rps->delta_poc[k + 1] = temp;
                rps->m_used[k + 1] =  rps->m_used[k];
                rps->m_ref[k + 1] = rps->m_ref[k];
                rps->delta_poc[k] = deltaPOC;
                rps->m_used[k] = used;
                rps->m_ref[k] = refed;
            }
        }
    }

    // flip the negative values to largest first
    RK_S32 numNegPics =  rps->num_negative_pic;
    for (j = 0, k = numNegPics - 1; j < numNegPics >> 1; j++, k--) {
        RK_S32 deltaPOC = rps->delta_poc[j];
        RK_U32 used = rps->m_used[j];
        RK_U32 refed = rps->m_ref[j];
        rps->delta_poc[j] = rps->delta_poc[k];
        rps->m_used[j] = rps->m_used[k];
        rps->m_ref[j] = rps->m_ref[k];
        rps->delta_poc[k] =  deltaPOC;
        rps->m_used[k] = used;
        rps->m_ref[k] = refed;
    }
}

void h265e_dpb_apply_rps(H265eDpb *dpb, H265eReferencePictureSet *rps, int curPoc)
{
    H265eDpbFrm *outPic = NULL;
    RK_S32 i;
    // loop through all pictures in the reference picture buffer
    RK_U32 index = 0;
    H265eDpbFrm *frame_list = &dpb->frame_list[0];

    h265e_dbg_func("enter\n");

    for (index = 0; index < MPP_ARRAY_ELEMS(dpb->frame_list); index++) {
        outPic = &frame_list[index];
        if (!outPic->inited || !outPic->slice->is_referenced) {
            continue;
        }

        // loop through all pictures in the Reference Picture Set
        // to see if the picture should be kept as reference picture
        for (i = 0; i < rps->num_positive_pic + rps->num_negative_pic; i++) {
            h265e_dbg_dpb("outPic->slice->poc %d,curPoc %d dealt %d", outPic->slice->poc, curPoc, rps->delta_poc[i]);
            if (!outPic->is_long_term && outPic->slice->poc == curPoc + rps->delta_poc[i]) {
                outPic->used_by_cur = (rps->m_used[i] == 1);
                outPic->is_long_term = 0;
            }
        }

        for (; i < rps->m_numberOfPictures; i++) {
            if (rps->check_lt_msb[i] == 0) {
                if (outPic->is_long_term && (outPic->slice->poc == rps->m_RealPoc[i])) {
                    outPic->used_by_cur = (rps->m_used[i] == 1);
                }
            } else {
                if (outPic->is_long_term && (outPic->slice->poc == rps->m_RealPoc[i])) {
                    outPic->used_by_cur = (rps->m_used[i] == 1);
                }
            }
        }
    }

    h265e_dbg_func("leave\n");
}

void h265e_dpb_dec_refresh_marking(H265eDpb *dpb, RK_S32 poc_cur, enum NALUnitType nalUnitType)
{
    RK_U32 index = 0;

    h265e_dbg_func("enter\n");

    if (nalUnitType == NAL_BLA_W_LP
        || nalUnitType == NAL_BLA_W_RADL
        || nalUnitType == NAL_BLA_N_LP
        || nalUnitType == NAL_IDR_W_RADL
        || nalUnitType == NAL_IDR_N_LP) { // IDR or BLA picture
        // mark all pictures as not used for reference
        H265eDpbFrm *frame_List = &dpb->frame_list[0];
        for (index = 0; index < MPP_ARRAY_ELEMS(dpb->frame_list); index++) {
            H265eDpbFrm *frame = &frame_List[index];
            if (frame->inited && (frame->poc != poc_cur)) {
                frame->slice->is_referenced = 0;
                frame->is_long_term = 0;
                if (frame->poc < poc_cur) {
                    frame->used_by_cur = 0;
                    frame->dpb_used = 0;
                    frame->status.val = 0;
                }
            }
        }

        if (nalUnitType == NAL_BLA_W_LP
            || nalUnitType == NAL_BLA_W_RADL
            || nalUnitType == NAL_BLA_N_LP) {
            dpb->poc_cra = poc_cur;
        }
    } else { // CRA or No DR
        if (dpb->refresh_pending == 1 && poc_cur > dpb->poc_cra) { // CRA reference marking pending
            H265eDpbFrm *frame_list = &dpb->frame_list[0];
            for (index = 0; index < MPP_ARRAY_ELEMS(dpb->frame_list); index++) {

                H265eDpbFrm *frame = &frame_list[index];
                if (frame->inited && frame->poc != poc_cur && frame->poc != dpb->poc_cra) {
                    frame->slice->is_referenced = 0;
                    frame->dpb_used = 0;
                }
            }

            dpb->refresh_pending = 0;
        }
        if (nalUnitType == NAL_CRA_NUT) { // CRA picture found
            dpb->refresh_pending = 1;
            dpb->poc_cra = poc_cur;
        }
    }
    h265e_dbg_func("leave\n");
}

// Function will arrange the long-term pictures in the decreasing order of poc_lsb_lt,
// and among the pictures with the same lsb, it arranges them in increasing delta_poc_msb_cycle_lt value
void h265e_dpb_arrange_lt_rps(H265eDpb *dpb, H265eSlice *slice)
{
    H265eReferencePictureSet *rps = slice->m_rps;
    RK_U32 tempArray[MAX_REFS];
    RK_S32 offset = rps->num_negative_pic + rps->num_positive_pic;
    RK_S32 i, j, ctr = 0;
    RK_S32 maxPicOrderCntLSB = 1 << slice->m_sps->m_bitsForPOC;
    RK_S32 numLongPics;
    RK_S32 currMSB = 0, currLSB = 0;
    (void)dpb;

    // Arrange long-term reference pictures in the correct order of LSB and MSB,
    // and assign values for pocLSBLT and MSB present flag
    RK_S32 longtermPicsPoc[MAX_REFS], longtermPicsLSB[MAX_REFS], indices[MAX_REFS];
    RK_S32 longtermPicsRealPoc[MAX_REFS];
    RK_S32 longtermPicsMSB[MAX_REFS];
    RK_U32 mSBPresentFlag[MAX_REFS];

    h265e_dbg_func("enter\n");
    if (!rps->num_long_term_pic) {
        return;
    }
    memset(longtermPicsPoc, 0, sizeof(longtermPicsPoc));  // Store POC values of LTRP
    memset(longtermPicsLSB, 0, sizeof(longtermPicsLSB));  // Store POC LSB values of LTRP
    memset(longtermPicsMSB, 0, sizeof(longtermPicsMSB));  // Store POC LSB values of LTRP
    memset(longtermPicsRealPoc, 0, sizeof(longtermPicsRealPoc));
    memset(indices, 0, sizeof(indices));                  // Indices to aid in tracking sorted LTRPs
    memset(mSBPresentFlag, 0, sizeof(mSBPresentFlag));    // Indicate if MSB needs to be present

    // Get the long-term reference pictures

    for (i = rps->m_numberOfPictures - 1; i >= offset; i--, ctr++) {
        longtermPicsPoc[ctr] = rps->poc[i];                                  // LTRP POC
        longtermPicsRealPoc[ctr] = rps->m_RealPoc[i];
        longtermPicsLSB[ctr] = getLSB(longtermPicsPoc[ctr], maxPicOrderCntLSB); // LTRP POC LSB
        indices[ctr] = i;
        longtermPicsMSB[ctr] = longtermPicsPoc[ctr] - longtermPicsLSB[ctr];
    }

    numLongPics = rps->num_long_term_pic;
    mpp_assert(ctr == numLongPics);

    // Arrange pictures in decreasing order of MSB;
    for (i = 0; i < numLongPics; i++) {
        for (j = 0; j < numLongPics - 1; j++) {
            if (longtermPicsMSB[j] < longtermPicsMSB[j + 1]) {
                MPP_SWAP(RK_S32, longtermPicsPoc[j], longtermPicsPoc[j + 1]);
                MPP_SWAP(RK_S32, longtermPicsRealPoc[j], longtermPicsRealPoc[j + 1]);
                MPP_SWAP(RK_S32, longtermPicsLSB[j], longtermPicsLSB[j + 1]);
                MPP_SWAP(RK_S32, longtermPicsMSB[j], longtermPicsMSB[j + 1]);
                MPP_SWAP(RK_S32, indices[j], indices[j + 1]);
            }
        }
    }

    for (i = 0; i < numLongPics; i++) {
        if (slice->gop_idx / maxPicOrderCntLSB > 0) {
            mSBPresentFlag[i] = 1;
        }
    }

    // tempArray for usedByCurr flag
    memset(tempArray, 0, sizeof(tempArray));
    for (i = 0; i < numLongPics; i++) {
        tempArray[i] = rps->m_used[indices[i]] ? 1 : 0;
    }

    // Now write the final values;
    ctr = 0;
    // currPicPoc = currMSB + currLSB
    currLSB = getLSB(slice->gop_idx, maxPicOrderCntLSB);
    currMSB = slice->gop_idx - currLSB;

    for (i = rps->m_numberOfPictures - 1; i >= offset; i--, ctr++) {
        rps->poc[i] = longtermPicsPoc[ctr];
        rps->delta_poc[i] = -slice->poc + longtermPicsRealPoc[ctr];

        rps->m_used[i] = tempArray[ctr];
        rps->m_pocLSBLT[i] = longtermPicsLSB[ctr];
        rps->m_deltaPOCMSBCycleLT[i] = (currMSB - (longtermPicsPoc[ctr] - longtermPicsLSB[ctr])) / maxPicOrderCntLSB;
        rps->m_deltaPocMSBPresentFlag[i] = mSBPresentFlag[ctr];

        mpp_assert(rps->m_deltaPOCMSBCycleLT[i] >= 0); // Non-negative value
    }

    for (i = rps->m_numberOfPictures - 1, ctr = 1; i >= offset; i--, ctr++) {
        for (j = rps->m_numberOfPictures - 1 - ctr; j >= offset; j--) {
            // Here at the encoder we know that we have set the full POC value for the LTRPs, hence we
            // don't have to check the MSB present flag values for this constraint.
            mpp_assert(rps->m_RealPoc[i] != rps->m_RealPoc[j]); // If assert fails, LTRP entry repeated in RPS!!!
        }
    }

    h265e_dbg_func("leave\n");
}

static H265eDpbFrm *h265e_find_cpb_in_dpb(H265eDpbFrm *frms, RK_S32 cnt, EncFrmStatus *frm)
{
    RK_S32 seq_idx = frm->seq_idx;
    RK_S32 i;

    if (!frm->valid)
        return NULL;

    h265e_dbg_dpb("frm %d start finding slot \n", frm->seq_idx);
    for (i = 0; i < cnt; i++) {
        if (!frms[i].inited) {
            continue;
        }
        EncFrmStatus *p = &frms[i].status;

        if (p->valid && p->seq_idx == seq_idx) {
            h265e_dbg_dpb("frm %d match slot %d valid %d\n",
                          p->seq_idx, i, p->valid);
            return &frms[i];
        }
    }
    mpp_err_f("can not find match frm %d\n", seq_idx);
    return NULL;
}

static H265eDpbFrm *h265e_find_cpb_frame(H265eDpbFrm *frms, RK_S32 cnt, EncFrmStatus *frm)
{
    RK_S32 seq_idx = frm->seq_idx;
    RK_S32 i;

    if (!frm->valid)
        return NULL;

    h265e_dbg_dpb("frm %d start finding slot \n", frm->seq_idx);
    for (i = 0; i < cnt; i++) {
        if (!frms[i].dpb_used) {
            continue;
        }

        EncFrmStatus *p = &frms[i].status;

        if (p->valid && p->seq_idx == seq_idx) {
            h265e_dbg_dpb("frm %d match slot %d valid %d\n",
                          p->seq_idx, i, p->valid);
            if (frm->save_pass1)
                return &frms[i];
            mpp_assert(p->is_non_ref == frm->is_non_ref);
            mpp_assert(p->is_lt_ref == frm->is_lt_ref);
            mpp_assert(p->lt_idx == frm->lt_idx);
            mpp_assert(p->temporal_id == frm->temporal_id);
            return &frms[i];
        }
    }

    mpp_err_f("can not find match frm %d\n", seq_idx);

    return NULL;
}

static MPP_RET h265e_check_frame_cpb(H265eDpbFrm *frm, RK_S32 cnt, EncFrmStatus *frms)
{
    EncFrmStatus *p = &frm->status;
    RK_S32 seq_idx, i;
    MPP_RET ret = MPP_NOK;
    h265e_dbg_func("enter\n");

    seq_idx = p->seq_idx;
    for (i = 0; i < cnt; i++) {

        if (!frms[i].valid) {
            continue;
        }

        if (frms[i].seq_idx == seq_idx) {
            ret =  MPP_OK;
        }
    }

    h265e_dbg_func("leave\n");
    return ret;
}


void h265e_dpb_cpb2rps(H265eDpb *dpb, RK_S32 curPoc, H265eSlice *slice, EncCpbStatus *cpb)
{
    h265e_dbg_func("enter\n");
    RK_S32 i;
    RK_S32 st_size = 0;
    RK_S32 lt_size = 0;
    RK_S32 nLongTermRefPicPoc[MAX_NUM_LONG_TERM_REF_PIC_POC];
    RK_S32 nLongTermRefPicRealPoc[MAX_NUM_LONG_TERM_REF_PIC_POC];
    RK_S32 nLongTermDealtPoc[MAX_NUM_LONG_TERM_REF_PIC_POC];
    RK_U32 isMsbValid[MAX_NUM_LONG_TERM_REF_PIC_POC];
    RK_U32 isShortTermValid[MAX_REFS];
    H265eRpsList *RpsList = &dpb->RpsList;
    H265eReferencePictureSet * rps = (H265eReferencePictureSet*)&slice->m_localRPS;
    RK_S32 idx_rps;
    H265eDpbFrm *p = NULL;
    RK_S32 ref_dealt_poc = 0;
    slice->m_bdIdx = -1;

    memset(isShortTermValid, 1, sizeof(RK_U32)*MAX_REFS);
    memset(rps, 0, sizeof(H265eReferencePictureSet));
    for (idx_rps = 0; idx_rps < 16; idx_rps++) {
        rps->delta_poc[idx_rps] = 0;
        rps->m_used[idx_rps] = 0;
        rps->m_ref[idx_rps] = 0;
    }

    memset(rps->delta_poc, 0, MAX_REFS * sizeof(int));

    if (cpb->curr.is_lt_ref)
        mpp_assert(slice->m_sps->m_bLongTermRefsPresent);

    idx_rps = 0;
    for (i = 0; i < MAX_CPB_REFS; i++) {
        EncFrmStatus *frm = &cpb->init[i];

        if (!frm->valid)
            continue;

        mpp_assert(!frm->is_non_ref);

        h265e_dbg_dpb("idx %d frm %d valid %d is_non_ref %d lt_ref %d\n",
                      i, frm->seq_idx, frm->valid, frm->is_non_ref, frm->is_lt_ref);

        p = h265e_find_cpb_frame(dpb->frame_list, MAX_REFS, frm);
        if (p) {
            if (!frm->is_lt_ref) {
                p->status.val = frm->val;
                rps->delta_poc[idx_rps] = p->poc - curPoc;
                rps->m_used[idx_rps] = 1;
                idx_rps++;
                st_size++;
                h265e_dbg_dpb("found st %d st_size %d %p deat_poc %d\n", i, st_size,
                              frm,  rps->delta_poc[idx_rps - 1]);
            } else {
                nLongTermRefPicPoc[lt_size] = p->gop_idx;
                nLongTermRefPicRealPoc[lt_size] = p->poc;
                nLongTermDealtPoc[lt_size] = p->poc - curPoc;
                isMsbValid[lt_size] = p->gop_idx >= (RK_S32)(1 << p->slice->m_sps->m_bitsForPOC);
                p->status.val = frm->val;
                h265e_dbg_dpb("found lt %d lt_size %d %p dealt poc %d\n", i, lt_size,
                              frm, nLongTermDealtPoc[lt_size]);
                lt_size++;
            }
        }
    }
    sort_delta_poc(rps);

    if (slice->m_sliceType == I_SLICE) {
        rps->m_interRPSPrediction = 0;
        rps->num_long_term_pic = 0;
        rps->num_negative_pic = 0;
        rps->num_positive_pic = 0;
        rps->m_numberOfPictures = 0;

    } else {
        p = h265e_find_cpb_frame(dpb->frame_list, MAX_REFS, &cpb->refr);
        if (p == NULL) {
            mpp_err("ref frame no found in refer index %d", cpb->refr.seq_idx);
        } else {
            ref_dealt_poc = p->poc - curPoc;
        }

        for (i = 0; i < st_size; i++) {
            rps->m_ref[i] = (rps->delta_poc[i] == ref_dealt_poc);
        }
    }

    if (lt_size > 0) {
        for ( i = 0; i < lt_size; i++) {
            h265e_dbg_dpb("numLongTermRefPic %d nShortTerm %d", lt_size, st_size);
            rps->poc[i + st_size] = nLongTermRefPicPoc[i];
            rps->m_RealPoc[i + st_size] = nLongTermRefPicRealPoc[i];
            rps->m_used[i + st_size] = 1;
            rps->delta_poc[i + st_size] = nLongTermDealtPoc[i];
            rps->check_lt_msb[i + st_size] = isMsbValid[i];
            if (cpb->refr.seq_idx == rps->poc[i + st_size])
                rps->m_ref[i + st_size] = 1;
            else
                rps->m_ref[i + st_size] = 0;
        }
    }

    rps->num_negative_pic = st_size;
    rps->num_positive_pic = 0;
    rps->num_long_term_pic = lt_size;
    rps->m_numberOfPictures = st_size + lt_size;
    slice->m_rps = rps;
    h265e_dpb_apply_rps(dpb, slice->m_rps, curPoc);
    h265e_dpb_arrange_lt_rps(dpb, slice);
    h265e_dpb_set_ref_list(dpb, slice, ref_dealt_poc);
    memcpy(&slice->m_RefPicListModification, RpsList->m_RefPicListModification,
           sizeof(H265eRefPicListModification));
    h265e_dbg_func("leave\n");
}

MPP_RET h265e_pskip_ref_check(H265eDpb *dpb, EncCpbStatus *cpb, H265eDpbFrm *frm)
{
    MPP_RET ret = MPP_OK;

    h265e_dbg_func("enter\n");

    if ((cpb->curr.force_pskip_is_ref) && (frm->slot_idx == dpb->curr->slice->m_refPicList[0][0]->slot_idx)) {
        h265e_dbg_dpb("hold refr buf as skip frm recon buf, poc %d slot idx %d.", frm->slice->poc, frm->slot_idx);
        ret = MPP_NOK;
    }

    if ((cpb->refr.force_pskip_is_ref) && (frm->slot_idx == dpb->curr->slice->m_refPicList[0][0]->prev_ref_idx)) {
        h265e_dbg_dpb("hold refr buf as skip frm recon buf, poc %d slot idx %d.", frm->slice->poc, frm->slot_idx);
        ret = MPP_NOK;
    }

    h265e_dbg_func("leave\n");
    return ret;
}

void h265e_dpb_free_unsed(H265eDpb *dpb, EncCpbStatus *cpb)
{
    RK_S32 i = 0;

    h265e_dbg_func("enter\n");

    if (cpb->curr.is_non_ref) {
        H265eDpbFrm *frm = h265e_find_cpb_frame(dpb->frame_list, MAX_REFS, &cpb->curr);
        if (frm) {
            h265e_dbg_dpb("free curr unreference buf poc %d", frm->slice->poc);
            frm->is_long_term = 0;
            frm->used_by_cur = 0;
            frm->dpb_used = 0;
            frm->slice->is_referenced = 0;
        }
    }

    for (i = 0; i < (RK_S32)MPP_ARRAY_ELEMS(dpb->frame_list); i++) {
        H265eDpbFrm *frm = &dpb->frame_list[i];
        if (!frm->dpb_used)
            continue;
        if (h265e_check_frame_cpb(frm, MAX_REFS, &cpb->final[0])) {
            if (!h265e_pskip_ref_check(dpb, cpb, frm)) {
                h265e_dbg_dpb("cpb final unreference buf poc %d", frm->slice->poc);
                frm->is_long_term = 0;
                frm->used_by_cur = 0;
                frm->dpb_used = 0;
                frm->slice->is_referenced = 0;
            }
        }
    }

    h265e_dbg_func("leave\n");
}

void h265e_dpb_proc_cpb(H265eDpb *dpb, EncCpbStatus *cpb)
{
    EncFrmStatus *curr = &cpb->curr;
    RK_U32 index = 0, i = 0;
    H265eDpbFrm *p = NULL;
    RK_U32 need_rebuild = 0;
    RK_S32 max_gop_id = 0, max_poc = 0;

    if (!dpb || !cpb)
        return;

    if (curr->is_idr) {
        for (index = 0; index < MPP_ARRAY_ELEMS(dpb->frame_list); index++) {
            H265eDpbFrm *frame = &dpb->frame_list[index];
            if (frame->inited) {
                frame->slice->is_referenced = 0;
                frame->is_long_term = 0;
                frame->used_by_cur = 0;
                frame->dpb_used = 0;
                frame->status.val = 0;
            }
        }
        return;
    }

    for (i = 0; i < MAX_CPB_REFS; i++) {
        EncFrmStatus *frm = &cpb->init[i];

        if (!frm->valid)
            continue;

        mpp_assert(!frm->is_non_ref);

        h265e_dbg_dpb("idx %d frm %d valid %d is_non_ref %d lt_ref %d\n",
                      i, frm->seq_idx, frm->valid, frm->is_non_ref, frm->is_lt_ref);

        p = h265e_find_cpb_in_dpb(dpb->frame_list, MAX_REFS, frm);
        if (!p->dpb_used) {
            p->dpb_used = 1;
            p->status.val = frm->val;
            p->slice->is_referenced = 1;
            need_rebuild = 1;
        }
    }

    if (need_rebuild) {
        h265e_dbg_dpb("cpb roll back found");
        for (index = 0; index < MPP_ARRAY_ELEMS(dpb->frame_list); index++) {
            H265eDpbFrm *frame = &dpb->frame_list[index];

            if (frame->dpb_used) {
                if (max_poc < frame->slice->poc) {
                    max_poc = frame->slice->poc;
                }
                if (max_gop_id < frame->slice->gop_idx) {
                    max_gop_id = frame->slice->gop_idx;
                }
            }
        }

        H265eDpbFrm *frame = dpb->curr;

        if (frame->inited) {
            frame->slice->is_referenced = 0;
            frame->is_long_term = 0;
            frame->used_by_cur = 0;
            frame->dpb_used = 0;
            frame->status.val = 0;
        }
        dpb->seq_idx = max_poc;
        dpb->gop_idx = max_gop_id;
    }

    for (index = 0; index < MPP_ARRAY_ELEMS(dpb->frame_list); index++) {
        H265eDpbFrm *frame = &dpb->frame_list[index];

        if (frame->inited && !frame->dpb_used) {
            h265e_dbg_dpb("reset index %d frame->inited %d rame->on_used %x",
                          index, frame->inited, frame->on_used);
            frame->status.val = 0;
        }
    }
}

void h265e_dpb_build_list(H265eDpb *dpb, EncCpbStatus *cpb)
{
    RK_S32 poc_cur = dpb->curr->slice->poc;
    H265eSlice* slice = dpb->curr->slice;
    RK_U32 bGPBcheck = 0;
    RK_S32 i;

    h265e_dbg_func("enter\n");
    if (get_nal_unit_type(dpb, poc_cur) == NAL_IDR_W_RADL ||
        get_nal_unit_type(dpb, poc_cur) == NAL_IDR_N_LP) {
        dpb->last_idr = poc_cur;
    }

    slice->last_idr = dpb->last_idr;
    slice->m_temporalLayerNonReferenceFlag = !slice->is_referenced;
    // Set the nal unit type
    slice->m_nalUnitType = get_nal_unit_type(dpb, poc_cur);

    // If the slice is un-referenced, change from _R "referenced" to _N "non-referenced" NAL unit type
    if (slice->m_temporalLayerNonReferenceFlag) {
        switch (slice->m_nalUnitType) {
        case NAL_TRAIL_R:
            slice->m_nalUnitType = NAL_TRAIL_N;
            break;
        case NAL_RADL_R:
            slice->m_nalUnitType = NAL_RADL_N;
            break;
        case NAL_RASL_R:
            slice->m_nalUnitType  = NAL_RASL_N;
            break;
        default:
            break;
        }
    }

    // Do decoding refresh marking if any
    h265e_dpb_dec_refresh_marking(dpb, poc_cur, slice->m_nalUnitType);
    h265e_dpb_cpb2rps(dpb, poc_cur, slice, cpb);

    slice->m_numRefIdx[L0] =  MPP_MIN(dpb->max_ref_l0, slice->m_rps->m_numberOfPictures); // Ensuring L0 contains just the -ve POC
    slice->m_numRefIdx[L1] =  MPP_MIN(dpb->max_ref_l1, slice->m_rps->m_numberOfPictures);

    h265e_slice_set_ref_list(dpb->frame_list, slice);

    // Slice type refinement
    if ((slice->m_sliceType == B_SLICE) && (slice->m_numRefIdx[L1] == 0)) {
        slice->m_sliceType = P_SLICE;
    }

    if (slice->m_sliceType == B_SLICE) {
        // TODO: Can we estimate this from lookahead?
        slice->m_colFromL0Flag = 0;

        RK_U32 bLowDelay = 1;
        RK_S32 curPOC = slice->poc;
        RK_S32 refIdx = 0;

        for (refIdx = 0; refIdx < slice->m_numRefIdx[L0] && bLowDelay; refIdx++) {
            if (slice->m_refPicList[L0][refIdx]->poc > curPOC) {
                bLowDelay = 0;
            }
        }

        for (refIdx = 0; refIdx < slice->m_numRefIdx[L1] && bLowDelay; refIdx++) {
            if (slice->m_refPicList[L1][refIdx]->poc > curPOC) {
                bLowDelay = 0;
            }
        }

        slice->m_bCheckLDC = bLowDelay;
    } else {
        slice->m_bCheckLDC = 1;
    }

    h265e_slice_set_ref_poc_list(slice);
    if (slice->m_sliceType == B_SLICE) {
        if (slice->m_numRefIdx[L0] == slice->m_numRefIdx[L1]) {
            bGPBcheck = 0;
            for (i = 0; i < slice->m_numRefIdx[L1]; i++) {
                if (slice->m_refPOCList[L1][i] != slice->m_refPOCList[L0][i]) {
                    bGPBcheck = 0;
                    break;
                }
            }
        }
    }

    slice->m_bLMvdL1Zero = bGPBcheck;
    slice->m_nextSlice = 0;
    if (slice->m_sliceType == I_SLICE) {
        slice->tot_poc_num = 0;
    } else {
        slice->tot_poc_num = slice->m_localRPS.m_numberOfPictures;
    }
    h265e_dpb_free_unsed(dpb, cpb);
    h265e_dbg_func("leave\n");
}

MPP_RET h265e_dpb_hal_start(H265eDpb *dpb, RK_S32 slot_idx)
{
    H265eDpbFrm *frm = &dpb->frame_list[slot_idx];

    frm->hal_used++;
    //h265e_dpb_dump_frms(dpb);
    return MPP_OK;
}

MPP_RET h265e_dpb_hal_end(H265eDpb *dpb, RK_S32 slot_idx)
{
    H265eDpbFrm *frm = &dpb->frame_list[slot_idx];

    frm->hal_used--;
    //h265e_dpb_dump_frms(dpb);
    return MPP_OK;
}
