/*
 * 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 "h264e_sps"

#include <string.h>

#include "mpp_common.h"

#include "mpp_enc_ref.h"
#include "mpp_bitwrite.h"
#include "h264e_debug.h"
#include "h264e_sps.h"

typedef struct H264eLevelInfo_t {
    H264Level       level;
    RK_S32          max_MBPS;       /* Max macroblock rate per second */
    RK_S32          max_MBs;        /* Max frame size */
    RK_S32          max_DpbMBs;     /* Max decoded picture buffer size */
    RK_S32          max_BR;         /* Max video bitrate */
    const char      *name;
} H264eLevelInfo;

H264eLevelInfo level_infos[] = {
    /*  level             max_MBs  max_MBPS  max_DpbMBs  max_BR  name  */
    {   H264_LEVEL_1_0,     1485,       99,        396,      64, "1"   },
    {   H264_LEVEL_1_b,     1485,       99,        396,     128, "1.b" },
    {   H264_LEVEL_1_1,     3000,      396,        900,     192, "1.1" },
    {   H264_LEVEL_1_2,     6000,      396,       2376,     384, "1.2" },
    {   H264_LEVEL_1_3,    11880,      396,       2376,     768, "1.3" },
    {   H264_LEVEL_2_0,    11880,      396,       2376,    2000, "2"   },
    {   H264_LEVEL_2_1,    19800,      792,       4752,    4000, "2.1" },
    {   H264_LEVEL_2_2,    20250,     1620,       8100,    4000, "2.2" },
    {   H264_LEVEL_3_0,    40500,     1620,       8100,   10000, "3"   },
    {   H264_LEVEL_3_1,   108000,     3600,      18000,   14000, "3.1" },
    {   H264_LEVEL_3_2,   216000,     5120,      20480,   20000, "3.2" },
    {   H264_LEVEL_4_0,   245760,     8192,      32768,   20000, "4"   },
    {   H264_LEVEL_4_1,   245760,     8192,      32768,   50000, "4.1" },
    {   H264_LEVEL_4_2,   522240,     8704,      34816,   50000, "4.2" },
    {   H264_LEVEL_5_0,   589824,    22080,     110400,  135000, "5"   },
    {   H264_LEVEL_5_1,   983040,    36864,     184320,  240000, "5.1" },
    {   H264_LEVEL_5_2,  2073600,    36864,     184320,  240000, "5.2" },
    {   H264_LEVEL_6_0,  4177920,   139264,     696320,  240000, "6"   },
    {   H264_LEVEL_6_1,  8355840,   139264,     696320,  480000, "6.1" },
    {   H264_LEVEL_6_2, 16711680,   139264,     696320,  800000, "6.2" },
};

MPP_RET h264e_sps_update(H264eSps *sps, MppEncCfgSet *cfg)
{
    H264eVui *vui = &sps->vui;
    MppEncPrepCfg *prep = &cfg->prep;
    MppEncRcCfg *rc = &cfg->rc;
    MppEncH264Cfg *h264 = &cfg->h264;
    MppEncRefCfg ref = cfg->ref_cfg;
    MppEncCpbInfo *info = mpp_enc_ref_cfg_get_cpb_info(ref);
    MppFrameFormat fmt = prep->format;
    RK_S32 gop = rc->gop;
    RK_S32 width = prep->width;
    RK_S32 height = prep->height;
    RK_S32 aligned_w = MPP_ALIGN(width, 16);
    RK_S32 aligned_h = MPP_ALIGN(height, 16);
    RK_S32 crop_right = MPP_ALIGN(width, 16) - width;
    RK_S32 crop_bottom = MPP_ALIGN(height, 16) - height;
    /* default 720p */
    H264Level level_idc = h264->level;

    // default sps
    // profile baseline
    sps->profile_idc = h264->profile;
    switch (sps->profile_idc) {
    case H264_PROFILE_BASELINE : {
        sps->constraint_set0 = 1;
        sps->constraint_set1 = 1;
        sps->constraint_set2 = 0;
        sps->constraint_set3 = 0;
        sps->constraint_set4 = 0;
        sps->constraint_set5 = 0;
    } break;
    case H264_PROFILE_MAIN : {
        sps->constraint_set0 = 0;
        sps->constraint_set1 = 1;
        sps->constraint_set2 = 0;
        sps->constraint_set3 = 0;
        sps->constraint_set4 = 0;
        sps->constraint_set5 = 0;
    } break;
    case H264_PROFILE_HIGH :
    case H264_PROFILE_HIGH10 :
    case H264_PROFILE_HIGH422 :
    case H264_PROFILE_HIGH444 : {
        sps->constraint_set0 = 0;
        sps->constraint_set1 = 0;
        sps->constraint_set2 = 0;
        sps->constraint_set3 = 0;
        sps->constraint_set4 = 0;
        sps->constraint_set5 = 0;
    } break;
    default : {
        sps->constraint_set0 = 0;
        sps->constraint_set1 = 0;
        sps->constraint_set2 = 0;
        sps->constraint_set3 = 0;
        sps->constraint_set4 = 0;
        sps->constraint_set5 = 0;
    } break;
    }

    //updata constraint_set0~5
    RK_U32 set = h264->constraint_set;
    RK_U8 constraint_force = (set >> 0) & 0x3f;
    RK_U8 force_flag = (set >> 16) & 0x3f;

    if (force_flag & 1)
        sps->constraint_set0 = (constraint_force & 1) ? 1 : 0;
    if (force_flag & 2)
        sps->constraint_set1 = (constraint_force & 2) ? 1 : 0;
    if (force_flag & 4)
        sps->constraint_set2 = (constraint_force & 4) ? 1 : 0;
    if (force_flag & 8)
        sps->constraint_set3 = (constraint_force & 8) ? 1 : 0;
    if (force_flag & 16)
        sps->constraint_set4 = (constraint_force & 16) ? 1 : 0;
    if (force_flag & 32)
        sps->constraint_set5 = (constraint_force & 32) ? 1 : 0;

    // level_idc is connected with frame size
    {
        RK_S32 mbs = (aligned_w * aligned_h) >> 8;
        RK_S32 i;
        RK_S32 min_level = 10;

        for (i = 0; i < (RK_S32)MPP_ARRAY_ELEMS(level_infos); i++) {
            if (level_infos[i].max_MBs >= mbs) {
                min_level = level_infos[i].level;

                if (min_level > (RK_S32)level_idc &&
                    min_level != H264_LEVEL_1_b) {
                    level_idc = min_level;
                    h264e_dbg_sps("set level to %s\n", level_infos[i].name);
                }

                break;
            }
        }
    }
    sps->level_idc = level_idc;

    sps->sps_id = 0;
    sps->chroma_format_idc = (fmt == MPP_FMT_YUV400) ? H264_CHROMA_400 : H264_CHROMA_420;

    // set max frame number and poc lsb according to gop size
    sps->pic_order_cnt_type = h264->hw_cfg.hw_poc_type;
    sps->log2_max_poc_lsb_minus4 = h264->log2_max_poc_lsb;
    sps->log2_max_frame_num_minus4 = h264->log2_max_frame_num;

    mpp_assert(gop >= 0);
    if (gop == 0) {
        // only one I then all P frame
        sps->log2_max_frame_num_minus4 = 12;
        sps->log2_max_poc_lsb_minus4 = 12;
    } else if (gop == 1) {
        // all I frame
        sps->log2_max_frame_num_minus4 = 12;
        sps->log2_max_poc_lsb_minus4 = 12;
    } else {
        // normal case
        RK_S32 log2_gop = MPP_MIN(mpp_log2(gop), 16);
        RK_S32 log2_frm_num = (log2_gop <= 4) ? (0) : (log2_gop - 4);
        RK_S32 log2_poc_lsb = (log2_gop <= 3) ? (0) : (log2_gop - 3);

        if (sps->log2_max_frame_num_minus4 < log2_frm_num)
            sps->log2_max_frame_num_minus4 = log2_frm_num;

        if (log2_poc_lsb > 12)
            log2_poc_lsb = 12;

        if (sps->log2_max_poc_lsb_minus4 < log2_poc_lsb)
            sps->log2_max_poc_lsb_minus4 = log2_poc_lsb;
    }

    // max one reference frame
    sps->num_ref_frames = info->dpb_size;

    sps->gaps_in_frame_num_value_allowed = !h264->gaps_not_allowed;

    // default 720p without cropping
    sps->pic_width_in_mbs = aligned_w >> 4;
    sps->pic_height_in_mbs = aligned_h >> 4;
    sps->frame_mbs_only = 1;

    // baseline disable 8x8
    sps->direct8x8_inference = h264->transform8x8_mode;
    if (crop_right || crop_bottom) {
        sps->cropping = 1;
        sps->crop.left = 0;
        sps->crop.right = crop_right;
        sps->crop.top = 0;
        sps->crop.bottom = crop_bottom;
    } else {
        sps->cropping = 0;
        memset(&sps->crop, 0, sizeof(sps->crop));
    }

    memset(vui, 0, sizeof(*vui));
    vui->vui_en = h264->vui.vui_en;
    vui->timing_info_present = 1;
    vui->time_scale = rc->fps_out_num * 2;
    vui->num_units_in_tick = rc->fps_out_denom;
    vui->fixed_frame_rate = !rc->fps_out_flex;
    vui->vidformat = MPP_FRAME_VIDEO_FMT_UNSPECIFIED;

    if (prep->range == MPP_FRAME_RANGE_JPEG) {
        vui->signal_type_present = 1;
        vui->fullrange = 1;
    }

    if ((prep->colorprim <= MPP_FRAME_PRI_JEDEC_P22 &&
         prep->colorprim != MPP_FRAME_PRI_UNSPECIFIED) ||
        (prep->colortrc <= MPP_FRAME_TRC_ARIB_STD_B67 &&
         prep->colortrc != MPP_FRAME_TRC_UNSPECIFIED) ||
        (prep->color <= MPP_FRAME_SPC_ICTCP &&
         prep->color != MPP_FRAME_SPC_UNSPECIFIED)) {
        vui->signal_type_present = 1;
        vui->color_description_present = 1;
        vui->colorprim = prep->colorprim;
        vui->colortrc = prep->colortrc;
        vui->colmatrix = prep->color;
    }

    vui->bitstream_restriction = 1;
    vui->motion_vectors_over_pic_boundaries = 1;
    /* the max and default value has been changed to 15 since the 2016 standard */
    vui->log2_max_mv_length_horizontal = 15;
    vui->log2_max_mv_length_vertical = 15;
    vui->max_dec_frame_buffering = info->dpb_size;

    return MPP_OK;
}

MPP_RET h264e_sps_to_packet(H264eSps *sps, MppPacket packet, RK_S32 *offset,
                            RK_S32 *len, MppEncCfgSet *cfg)
{
    void *pos = mpp_packet_get_pos(packet);
    void *data = mpp_packet_get_data(packet);
    size_t size = mpp_packet_get_size(packet);
    size_t length = mpp_packet_get_length(packet);
    void *p = pos + length;
    RK_S32 buf_size = (data + size) - (pos + length);
    MppWriteCtx bit_ctx;
    MppWriteCtx *bit = &bit_ctx;
    RK_S32 sps_size = 0;
    static const RK_U32 SubWidthC[4]  = { 1, 2, 2, 1};
    static const RK_U32 SubHeightC[4] = { 1, 2, 1, 1};

    mpp_writer_init(bit, p, buf_size);

    /* start_code_prefix 00 00 00 01 */
    mpp_writer_put_raw_bits(bit, 0, 24);
    mpp_writer_put_raw_bits(bit, 1, 8);
    /* forbidden_zero_bit */
    mpp_writer_put_raw_bits(bit, 0, 1);
    /* nal_ref_idc */
    mpp_writer_put_raw_bits(bit, H264_NALU_PRIORITY_HIGHEST, 2);
    /* nal_unit_type */
    mpp_writer_put_raw_bits(bit, H264_NALU_TYPE_SPS, 5);

    /* profile_idc */
    mpp_writer_put_bits(bit, sps->profile_idc, 8);
    /* constraint_set0_flag */
    mpp_writer_put_bits(bit, sps->constraint_set0, 1);
    /* constraint_set1_flag */
    mpp_writer_put_bits(bit, sps->constraint_set1, 1);
    /* constraint_set2_flag */
    mpp_writer_put_bits(bit, sps->constraint_set2, 1);
    /* constraint_set3_flag */
    mpp_writer_put_bits(bit, sps->constraint_set3, 1);
    /* constraint_set4_flag */
    mpp_writer_put_bits(bit, sps->constraint_set4, 1);
    /* constraint_set5_flag */
    mpp_writer_put_bits(bit, sps->constraint_set5, 1);
    /* reserved_zero_2bits */
    mpp_writer_put_bits(bit, 0, 2);

    /* level_idc */
    mpp_writer_put_bits(bit, sps->level_idc, 8);
    /* seq_parameter_set_id */
    mpp_writer_put_ue(bit, sps->sps_id);

    if (sps->profile_idc >= H264_PROFILE_HIGH) {
        /* chroma_format_idc */
        mpp_writer_put_ue(bit, sps->chroma_format_idc);
        /* bit_depth_luma_minus8 */
        mpp_writer_put_ue(bit, 0);
        /* bit_depth_chroma_minus8 */
        mpp_writer_put_ue(bit, 0);
        /* qpprime_y_zero_transform_bypass_flag */
        mpp_writer_put_bits(bit, 0, 1);
        /* seq_scaling_matrix_present_flag */
        mpp_writer_put_bits(bit, 0, 1);
    }

    /* log2_max_frame_num_minus4 */
    mpp_writer_put_ue(bit, sps->log2_max_frame_num_minus4);
    /* pic_order_cnt_type */
    /* accodring usr cfg cfg->h264.poc_type, hw may no support
       will convert after get stream
    */
    mpp_writer_put_ue(bit, cfg->h264.poc_type);
    if (cfg->h264.poc_type == 0) {
        /* log2_max_pic_order_cnt_lsb_minus4 */
        mpp_writer_put_ue(bit, sps->log2_max_poc_lsb_minus4);
    }

    /* max_num_ref_frames */
    mpp_writer_put_ue(bit, sps->num_ref_frames);
    /* gaps_in_frame_num_value_allowed_flag */
    mpp_writer_put_bits(bit, sps->gaps_in_frame_num_value_allowed, 1);

    /* pic_width_in_mbs_minus1 */
    mpp_writer_put_ue(bit, sps->pic_width_in_mbs - 1);
    /* pic_height_in_map_units_minus1 */
    mpp_writer_put_ue(bit, sps->pic_height_in_mbs - 1);

    /* frame_mbs_only_flag */
    mpp_writer_put_bits(bit, sps->frame_mbs_only, 1);
    /* direct_8x8_inference_flag */
    mpp_writer_put_bits(bit, sps->direct8x8_inference, 1);
    /* frame_cropping_flag */
    mpp_writer_put_bits(bit, sps->cropping, 1);
    if (sps->cropping) {
        /* frame_crop_left_offset */
        mpp_writer_put_ue(bit, sps->crop.left / SubWidthC[sps->chroma_format_idc]);
        /* frame_crop_right_offset */
        mpp_writer_put_ue(bit, sps->crop.right / SubWidthC[sps->chroma_format_idc]);
        /* frame_crop_top_offset */
        mpp_writer_put_ue(bit, sps->crop.top / SubHeightC[sps->chroma_format_idc]);
        /* frame_crop_bottom_offset */
        mpp_writer_put_ue(bit, sps->crop.bottom / SubHeightC[sps->chroma_format_idc]);
    }

    /* vui_parameters_present_flag */
    mpp_writer_put_bits(bit, sps->vui.vui_en, 1);
    if (sps->vui.vui_en) {
        H264eVui *vui = &sps->vui;

        /* aspect_ratio_info_present_flag */
        mpp_writer_put_bits(bit, vui->aspect_ratio_info_present, 1);
        if (vui->aspect_ratio_info_present) {
            /* aspect_ratio_idc */
            mpp_writer_put_bits(bit, vui->aspect_ratio_idc, 8);
            if (H264_EXTENDED_SAR == vui->aspect_ratio_idc) {
                /* sar_width */
                mpp_writer_put_bits(bit, vui->sar_width, 16);
                /* sar_height */
                mpp_writer_put_bits(bit, vui->sar_height, 16);
            }
        }

        /* overscan_info_present */
        mpp_writer_put_bits(bit, vui->overscan_info_present, 1);
        if (vui->overscan_info_present) {
            /* overscan_appropriate_flag */
            mpp_writer_put_bits(bit, vui->overscan_appropriate_flag, 1);
        }

        /* video_signal_type_present_flag */
        mpp_writer_put_bits(bit, vui->signal_type_present, 1);
        if (vui->signal_type_present) {
            /* video_format */
            mpp_writer_put_bits(bit, vui->vidformat, 3);
            /* video_full_range_flag */
            mpp_writer_put_bits(bit, vui->fullrange, 1);
            /* colour_description_present_flag */
            mpp_writer_put_bits(bit, vui->color_description_present, 1);
            if (vui->color_description_present) {
                /* colour_primaries */
                mpp_writer_put_bits(bit, vui->colorprim, 8);
                /* transfer_characteristics */
                mpp_writer_put_bits(bit, vui->colortrc, 8);
                /* matrix_coefficients */
                mpp_writer_put_bits(bit, vui->colmatrix, 8);
            }
        }

        /* chroma_loc_info_present_flag */
        mpp_writer_put_bits(bit, vui->chroma_loc_info_present, 1);
        if (vui->chroma_loc_info_present) {
            /* chroma_sample_loc_type_top_field */
            mpp_writer_put_ue(bit, vui->chroma_loc_top);
            /* chroma_sample_loc_type_bottom_field */
            mpp_writer_put_ue(bit, vui->chroma_loc_bottom);
        }

        /* timing_info_present_flag */
        mpp_writer_put_bits(bit, vui->timing_info_present, 1);
        if (vui->timing_info_present) {
            /* num_units_in_tick msb */
            mpp_writer_put_bits(bit, vui->num_units_in_tick >> 16, 16);
            /* num_units_in_tick lsb */
            mpp_writer_put_bits(bit, vui->num_units_in_tick & 0xffff, 16);
            /* time_scale msb */
            mpp_writer_put_bits(bit, vui->time_scale >> 16, 16);
            /* time_scale lsb */
            mpp_writer_put_bits(bit, vui->time_scale & 0xffff, 16);
            /* fixed_frame_rate_flag */
            mpp_writer_put_bits(bit, vui->fixed_frame_rate, 1);
        }

        /* nal_hrd_parameters_present_flag */
        mpp_writer_put_bits(bit, vui->nal_hrd_parameters_present, 1);
        /* vcl_hrd_parameters_present_flag */
        mpp_writer_put_bits(bit, vui->vcl_hrd_parameters_present, 1);
        /* pic_struct_present_flag */
        mpp_writer_put_bits(bit, vui->pic_struct_present, 1);

        /* bit_stream_restriction_flag */
        mpp_writer_put_bits(bit, vui->bitstream_restriction, 1);
        if (vui->bitstream_restriction) {
            /* motion_vectors_over_pic_boundaries */
            mpp_writer_put_bits(bit, vui->motion_vectors_over_pic_boundaries, 1);
            /* max_bytes_per_pic_denom */
            mpp_writer_put_ue(bit, vui->max_bytes_per_pic_denom);
            /* max_bits_per_mb_denom */
            mpp_writer_put_ue(bit, vui->max_bits_per_mb_denom);
            /* log2_mv_length_horizontal */
            mpp_writer_put_ue(bit, vui->log2_max_mv_length_horizontal);
            /* log2_mv_length_vertical */
            mpp_writer_put_ue(bit, vui->log2_max_mv_length_vertical);
            /* num_reorder_frames */
            mpp_writer_put_ue(bit, vui->num_reorder_frames);
            /* max_dec_frame_buffering */
            mpp_writer_put_ue(bit, vui->max_dec_frame_buffering);
        }
    }

    mpp_writer_trailing(bit);

    sps_size = mpp_writer_bytes(bit);
    if (len)
        *len = sps_size;
    if (offset)
        *offset = length;

    mpp_packet_set_length(packet, length + sps_size);

    return MPP_OK;
}

MPP_RET h264e_sps_dump(H264eSps *sps)
{
    (void) sps;
    return MPP_OK;
}
