// Copyright (c) 2019-2020 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 "mfx_common.h"
#if defined(MFX_ENABLE_HEVCE_WEIGHTED_PREDICTION)

#include "hevcehw_base_weighted_prediction_lin.h"
#include "hevcehw_base_va_packer_lin.h"
#include "va/va.h"

using namespace HEVCEHW;
using namespace HEVCEHW::Base;

void Linux::Base::WeightPred::SubmitTask(const FeatureBlocks& /*blocks*/, TPushST Push) 
{
    Push(BLK_PatchDDITask
            , [](StorageW& global, StorageW& s_task) -> mfxStatus
    {
        auto& esSlice = Task::SSH::Get(s_task);
        auto& ddiPar  = Glob::DDI_SubmitParam::Get(global);
        auto  itPPS   = std::find_if(std::begin(ddiPar), std::end(ddiPar)
            , [](DDIExecParam& ep) { return (ep.Function == VAEncPictureParameterBufferType); });

        MFX_CHECK(itPPS != std::end(ddiPar) && itPPS->In.pData, MFX_ERR_UNKNOWN);

        auto& ddiPPS    = *(VAEncPictureParameterBufferHEVC*)itPPS->In.pData;
        const mfxExtCodingOption3& CO3 = ExtBuffer::Get(Glob::VideoParam::Get(global));

        ddiPPS.pic_fields.bits.enable_gpu_weighted_prediction = IsOn(CO3.FadeDetection); // should be set for I as well
        bool bNeedPWT   = (esSlice.type != 2
            && (ddiPPS.pic_fields.bits.weighted_bipred_flag || ddiPPS.pic_fields.bits.weighted_pred_flag));
        MFX_CHECK(bNeedPWT, MFX_ERR_NONE);

        auto& ph    = Glob::PackedHeaders::Get(global);
        auto  itSSH = ph.SSH.begin();

        auto SetPWT = [&](VAEncSliceParameterBufferHEVC& slice)
        {
            const mfxU16 Y = 0, Cb = 1, Cr = 2, Weight = 0, Offset = 1;

            slice.luma_log2_weight_denom         = (mfxU8)esSlice.luma_log2_weight_denom;
            slice.delta_chroma_log2_weight_denom = (mfxI8)(esSlice.chroma_log2_weight_denom - slice.luma_log2_weight_denom);
            slice.pred_weight_table_bit_offset   = itSSH->PackInfo.at(PACK_PWTOffset);
            slice.pred_weight_table_bit_length   = itSSH->PackInfo.at(PACK_PWTLength);

            mfxI16 wY = (1 << slice.luma_log2_weight_denom);
            mfxI16 wC = (1 << esSlice.chroma_log2_weight_denom);

            for (mfxU16 i = 0; i < 15; i++)
            {
                slice.luma_offset_l0[i]            = (mfxI8)esSlice.pwt[0][i][Y][Offset];
                slice.delta_luma_weight_l0[i]      = (mfxI8)(esSlice.pwt[0][i][Y][Weight] - wY);
                slice.chroma_offset_l0[i][0]       = (mfxI8)esSlice.pwt[0][i][Cb][Offset];
                slice.chroma_offset_l0[i][1]       = (mfxI8)esSlice.pwt[0][i][Cr][Offset];
                slice.delta_chroma_weight_l0[i][0] = (mfxI8)(esSlice.pwt[0][i][Cb][Weight] - wC);
                slice.delta_chroma_weight_l0[i][1] = (mfxI8)(esSlice.pwt[0][i][Cr][Weight] - wC);
                slice.luma_offset_l1[i]            = (mfxI8)esSlice.pwt[1][i][Y][Offset];
                slice.delta_luma_weight_l1[i]      = (mfxI8)(esSlice.pwt[1][i][Y][Weight] - wY);
                slice.chroma_offset_l1[i][0]       = (mfxI8)esSlice.pwt[1][i][Cb][Offset];
                slice.chroma_offset_l1[i][1]       = (mfxI8)esSlice.pwt[1][i][Cr][Offset];
                slice.delta_chroma_weight_l1[i][0] = (mfxI8)(esSlice.pwt[1][i][Cb][Weight] - wC);
                slice.delta_chroma_weight_l1[i][1] = (mfxI8)(esSlice.pwt[1][i][Cr][Weight] - wC);
            }
        };

        std::for_each(std::begin(ddiPar), std::end(ddiPar)
            , [&](DDIExecParam& ep)
        {
            if (ep.Function != VAEncSliceParameterBufferType)
                return;

            auto pBegin = (VAEncSliceParameterBufferHEVC*)ep.In.pData;
            auto pEnd   = pBegin + (std::max<mfxU32>(ep.In.Num, 1) * !!pBegin);

            assert(ep.In.Size == sizeof(VAEncSliceParameterBufferHEVC));

            std::for_each(pBegin, pEnd, SetPWT);
        });

        return MFX_ERR_NONE;
    });
}

#endif //defined(MFX_ENABLE_HEVCE_WEIGHTED_PREDICTION)
