/*
* Copyright (c) 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.
*/
//!
//! \file     decode_mpeg2_pipeline.cpp
//! \brief    Defines the interface for mpeg2 decode pipeline
//!
#include "decode_mpeg2_pipeline.h"
#include "decode_utils.h"
#include "media_user_settings_mgr_g12.h"
#include "codechal_setting.h"
#include "decode_mpeg2_feature_manager.h"
#include "decode_huc_packet_creator_base.h" 

namespace decode{

Mpeg2Pipeline::Mpeg2Pipeline(
    CodechalHwInterface *   hwInterface,
    CodechalDebugInterface *debugInterface)
    : DecodePipeline(hwInterface, debugInterface)
{
}

MOS_STATUS Mpeg2Pipeline::Initialize(void *settings)
{
    DECODE_FUNC_CALL();

    DECODE_CHK_STATUS(DecodePipeline::Initialize(settings));
    m_basicFeature = dynamic_cast<Mpeg2BasicFeature*>(m_featureManager->GetFeature(FeatureIDs::basicFeature));
    DECODE_CHK_NULL(m_basicFeature);

    // Create basic GPU context
    DecodeScalabilityPars scalPars;
    MOS_ZeroMemory(&scalPars, sizeof(scalPars));
    DECODE_CHK_STATUS(m_mediaContext->SwitchContext(VdboxDecodeFunc, &scalPars, &m_scalability));
    m_decodeContext = m_osInterface->pfnGetGpuContext(m_osInterface);

    HucPacketCreatorBase *hucPktCreator = dynamic_cast<HucPacketCreatorBase *>(this);
    DECODE_CHK_NULL(hucPktCreator);
    m_mpeg2BsCopyPkt    = hucPktCreator->CreateHucCopyPkt(this, m_task, m_hwInterface);
    DECODE_CHK_NULL(m_mpeg2BsCopyPkt);
    MediaPacket *packet = dynamic_cast<MediaPacket *>(m_mpeg2BsCopyPkt);
    DECODE_CHK_NULL(packet);
    DECODE_CHK_STATUS(RegisterPacket(DecodePacketId(this, mpeg2BsCopyPktId), packet));
    DECODE_CHK_STATUS(packet->Init());

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::Prepare(void *params)
{
    DECODE_FUNC_CALL();

    DECODE_CHK_NULL(params);
    DECODE_CHK_STATUS(DecodePipeline::Prepare(params));
    DECODE_CHK_STATUS(CopyBitstreamBuffer());

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::CopyDummyBitstream()
{
    DECODE_FUNC_CALL();

    HucCopyPktItf::HucCopyParams copyParams = {};

    for (uint16_t slcIdx = 0; slcIdx < m_basicFeature->m_totalNumSlicesRecv; slcIdx++)
    {
        // Copy dummy slice to local buffer
        if (!m_basicFeature->m_copyDummySlicePresent && ((m_basicFeature->m_sliceRecord[slcIdx].prevSliceMbEnd !=
            m_basicFeature->m_sliceRecord[slcIdx].sliceStartMbOffset && !m_basicFeature->m_sliceRecord[slcIdx].skip) ||
            m_basicFeature->m_incompletePicture))
        {
            m_basicFeature->m_copyDummySlicePresent = true;
            copyParams.srcBuffer  = &(m_basicFeature->m_resMpeg2DummyBistream->OsResource);
            copyParams.srcOffset  = 0;
            copyParams.destBuffer = &(m_basicFeature->m_copiedDataBuf->OsResource);
            copyParams.destOffset = m_basicFeature->m_nextCopiedDataOffset;
            copyParams.copyLength = sizeof(m_basicFeature->Mpeg2DummyBsBuf);
            m_mpeg2BsCopyPkt->PushCopyParams(copyParams);

            m_basicFeature->m_dummySliceDataOffset = m_basicFeature->m_nextCopiedDataOffset;
        }
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::CopyBitstreamBuffer()
{
    DECODE_FUNC_CALL();

    HucCopyPktItf::HucCopyParams copyParams = {};

    if (m_basicFeature->m_copiedDataNeeded)
    {
        m_basicFeature->m_copiedDataBufferInUse = true;
        if ((m_basicFeature->m_nextCopiedDataOffset + m_basicFeature->m_dataSize) >
            m_basicFeature->m_copiedDataBufferSize)
        {
            DECODE_ASSERTMESSAGE("Copied data buffer is not large enough.");
            m_basicFeature->m_slicesInvalid = true;
            return MOS_STATUS_UNKNOWN;
        }

        uint32_t size = MOS_ALIGN_CEIL(m_basicFeature->m_dataSize, 16);
        copyParams.srcBuffer  = &(m_basicFeature->m_resDataBuffer.OsResource);
        copyParams.srcOffset  = 0;
        copyParams.destBuffer = &(m_basicFeature->m_copiedDataBuf->OsResource);
        copyParams.destOffset = m_basicFeature->m_nextCopiedDataOffset;
        copyParams.copyLength = m_basicFeature->m_dataSize;
        m_mpeg2BsCopyPkt->PushCopyParams(copyParams);

        m_basicFeature->m_copiedDataOffset = m_basicFeature->m_nextCopiedDataOffset;
        m_basicFeature->m_nextCopiedDataOffset += MOS_ALIGN_CEIL(size, MHW_CACHELINE_SIZE);

        bool immediateSubmit = true;
        DECODE_CHK_STATUS(ActivatePacket(DecodePacketId(this, mpeg2BsCopyPktId), immediateSubmit, 0, 0));
        m_activePacketList.back().frameTrackingRequested = false;
        DECODE_CHK_STATUS(ExecuteActivePackets());
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::UserFeatureReport()
{
    DECODE_FUNC_CALL();
    DECODE_CHK_STATUS(DecodePipeline::UserFeatureReport());
#if (_DEBUG || _RELEASE_INTERNAL)
    WriteUserFeature(__MEDIA_USER_FEATURE_VALUE_APOGEIOS_MPEG2D_ENABLE_ID, 1, m_osInterface->pOsContext);
#endif

#ifdef _MMC_SUPPORTED
    CODECHAL_DEBUG_TOOL(
        if (m_mmcState != nullptr) {
            m_mmcState->UpdateUserFeatureKey(&(m_basicFeature->m_destSurface));
        })
#endif
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::Uninitialize()
{
    DECODE_FUNC_CALL();
    return DecodePipeline::Uninitialize();
}

MOS_STATUS Mpeg2Pipeline::ActivateDecodePackets()
{
    DECODE_FUNC_CALL();

    bool immediateSubmit = false;

    if (m_basicFeature->m_copyDummySlicePresent)
    {
        DECODE_CHK_STATUS(ActivatePacket(DecodePacketId(this, mpeg2BsCopyPktId), immediateSubmit, 0, 0));
    }

    DECODE_CHK_STATUS(ActivatePacket(DecodePacketId(this, mpeg2DecodePacketId), immediateSubmit, 0, 0));

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::CreateFeatureManager()
{
    DECODE_FUNC_CALL();
    m_featureManager = MOS_New(DecodeMpeg2FeatureManager, m_allocator, m_hwInterface);
    DECODE_CHK_NULL(m_featureManager);
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::CreateSubPackets(DecodeSubPacketManager& subPacketManager, CodechalSetting &codecSettings)
{
    DECODE_FUNC_CALL();
    DECODE_CHK_STATUS(DecodePipeline::CreateSubPackets(subPacketManager, codecSettings));
    return MOS_STATUS_SUCCESS;
}

#if USE_CODECHAL_DEBUG_TOOL
MOS_STATUS Mpeg2Pipeline::DumpPicParams(
    CodecDecodeMpeg2PicParams *picParams)
{
    DECODE_FUNC_CALL();

    if (!m_debugInterface->DumpIsEnabled(CodechalDbgAttr::attrPicParams))
    {
        return MOS_STATUS_SUCCESS;
    }

    DECODE_CHK_NULL(picParams);

    std::ostringstream oss;
    oss.setf(std::ios::showbase | std::ios::uppercase);

    oss << "m_currPic FrameIdx: " << +picParams->m_currPic.FrameIdx << std::endl;
    oss << "m_currPic PicFlags: " << +picParams->m_currPic.PicFlags << std::endl;
    oss << "m_forwardRefIdx: " << +picParams->m_forwardRefIdx << std::endl;
    oss << "m_backwardRefIdx: " << +picParams->m_backwardRefIdx << std::endl;
    oss << "m_topFieldFirst: " << +picParams->m_topFieldFirst << std::endl;
    oss << "m_secondField: " << +picParams->m_secondField << std::endl;
    oss << "m_statusReportFeedbackNumber: " << +picParams->m_statusReportFeedbackNumber << std::endl;
    //Dump union w0
    oss << "w0 m_value: " << +picParams->W0.m_value << std::endl;
    oss << "m_scanOrder: " << +picParams->W0.m_scanOrder << std::endl;
    oss << "m_intraVlcFormat: " << +picParams->W0.m_intraVlcFormat << std::endl;
    oss << "m_quantizerScaleType: " << +picParams->W0.m_quantizerScaleType << std::endl;
    oss << "m_concealmentMVFlag: " << +picParams->W0.m_concealmentMVFlag << std::endl;
    oss << "m_frameDctPrediction: " << +picParams->W0.m_frameDctPrediction << std::endl;
    oss << "m_topFieldFirst: " << +picParams->W0.m_topFieldFirst << std::endl;
    oss << "m_intraDCPrecision: " << +picParams->W0.m_intraDCPrecision << std::endl;
    //Dump union w1
    oss << "w1 m_value: " << +picParams->W1.m_value << std::endl;
    oss << "m_fcode11: " << +picParams->W1.m_fcode11 << std::endl;
    oss << "m_fcode10: " << +picParams->W1.m_fcode10 << std::endl;
    oss << "m_fcode01: " << +picParams->W1.m_fcode01 << std::endl;
    oss << "m_fcode00: " << +picParams->W1.m_fcode00 << std::endl;
    oss << "m_horizontalSize: " << +picParams->m_horizontalSize << std::endl;
    oss << "m_verticalSize: " << +picParams->m_verticalSize << std::endl;
    oss << "m_pictureCodingType: " << +picParams->m_pictureCodingType << std::endl;

    const char *fileName = m_debugInterface->CreateFileName(
        "_DEC",
        CodechalDbgBufferType::bufPicParams,
        CodechalDbgExtType::txt);

    std::ofstream ofs(fileName, std::ios::out);
    ofs << oss.str();
    ofs.close();

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::DumpSliceParams(
    CodecDecodeMpeg2SliceParams *sliceParams,
    uint32_t                     numSlices)
{
    DECODE_FUNC_CALL();

    if (!m_debugInterface->DumpIsEnabled(CodechalDbgAttr::attrSlcParams))
    {
        return MOS_STATUS_SUCCESS;
    }

    DECODE_CHK_NULL(sliceParams);

    const char *fileName = m_debugInterface->CreateFileName(
        "_DEC",
        CodechalDbgBufferType::bufSlcParams,
        CodechalDbgExtType::txt);

    std::ostringstream oss;
    oss.setf(std::ios::showbase | std::ios::uppercase);

    CodecDecodeMpeg2SliceParams *sliceControl = nullptr;

    for (uint16_t i = 0; i < numSlices; i++)
    {
        sliceControl = &sliceParams[i];

        oss << "===================================================================" << std::endl;
        oss << "Data for Slice number = " << +i << std::endl;
        oss << "m_sliceDataSize: " << +sliceControl->m_sliceDataSize << std::endl;
        oss << "m_sliceDataOffset: " << +sliceControl->m_sliceDataOffset << std::endl;
        oss << "m_macroblockOffset: " << +sliceControl->m_macroblockOffset << std::endl;
        oss << "m_sliceHorizontalPosition: " << +sliceControl->m_sliceHorizontalPosition << std::endl;
        oss << "m_sliceVerticalPosition: " << +sliceControl->m_sliceVerticalPosition << std::endl;
        oss << "m_quantiserScaleCode: " << +sliceControl->m_quantiserScaleCode << std::endl;
        oss << "m_numMbsForSlice: " << +sliceControl->m_numMbsForSlice << std::endl;
        oss << "m_numMbsForSliceOverflow: " << +sliceControl->m_numMbsForSliceOverflow << std::endl;
        oss << "m_reservedBits: " << +sliceControl->m_reservedBits << std::endl;
        oss << "m_startCodeBitOffset: " << +sliceControl->m_startCodeBitOffset << std::endl;

        std::ofstream ofs;
        if (i == 0)
        {
            ofs.open(fileName, std::ios::out);
        }
        else
        {
            ofs.open(fileName, std::ios::app);
        }
        ofs << oss.str();
        ofs.close();
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::DumpMbParams(
    CodecDecodeMpeg2MbParmas *mbParams)
{
    DECODE_FUNC_CALL();

    if (!m_debugInterface->DumpIsEnabled(CodechalDbgAttr::attrMbParams))
    {
        return MOS_STATUS_SUCCESS;
    }

    DECODE_CHK_NULL(mbParams);

    std::ostringstream oss;
    oss.setf(std::ios::showbase | std::ios::uppercase);

    oss << "m_mbAddr: " << +mbParams->m_mbAddr << std::endl;
    //Dump union MBType
    oss << "MBType.m_intraMb: " << +mbParams->MBType.m_intraMb << std::endl;
    oss << "MBType.m_motionFwd: " << +mbParams->MBType.m_motionFwd << std::endl;
    oss << "MBType.m_motionBwd: " << +mbParams->MBType.m_motionBwd << std::endl;
    oss << "MBType.m_motion4mv: " << +mbParams->MBType.m_motion4mv << std::endl;
    oss << "MBType.m_h261Lpfilter: " << +mbParams->MBType.m_h261Lpfilter << std::endl;
    oss << "MBType.m_fieldResidual: " << +mbParams->MBType.m_fieldResidual << std::endl;
    oss << "MBType.m_mbScanMethod: " << +mbParams->MBType.m_mbScanMethod << std::endl;
    oss << "MBType.m_motionType: " << +mbParams->MBType.m_motionType << std::endl;
    oss << "MBType.m_hostResidualDiff: " << +mbParams->MBType.m_hostResidualDiff << std::endl;
    oss << "MBType.m_mvertFieldSel: " << +mbParams->MBType.m_mvertFieldSel << std::endl;
    oss << "m_mbSkipFollowing: " << +mbParams->m_mbSkipFollowing << std::endl;
    oss << "m_mbDataLoc: " << +mbParams->m_mbDataLoc << std::endl;
    oss << "m_codedBlockPattern: " << +mbParams->m_codedBlockPattern << std::endl;

    //Dump NumCoeff[CODEC_NUM_BLOCK_PER_MB]
    for (uint16_t i = 0; i < CODEC_NUM_BLOCK_PER_MB; ++i)
    {
        oss << "m_numCoeff[" << +i << "]: " << +mbParams->m_numCoeff[i] << std::endl;
    }

    //Dump motion_vectors[8],printing them in 4 value chunks per line
    for (uint8_t i = 0; i < 2; ++i)
    {
        oss << "m_motionVectors[" << +i * 4 << "-" << (+i * 4) + 3 << "]: ";
        for (uint8_t j = 0; j < 4; j++)
            oss << +mbParams->m_motionVectors[i * 4 + j] << " ";
        oss << std::endl;
    }

    const char *fileName = m_debugInterface->CreateFileName(
        "_DEC",
        CodechalDbgBufferType::bufMbParams,
        CodechalDbgExtType::txt);

    std::ofstream ofs(fileName, std::ios::out);
    ofs << oss.str();
    ofs.close();

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Mpeg2Pipeline::DumpIQParams(
    CodecMpeg2IqMatrix *matrixData)
{
    DECODE_FUNC_CALL();

    if (!m_debugInterface->DumpIsEnabled(CodechalDbgAttr::attrIqParams))
    {
        return MOS_STATUS_SUCCESS;
    }

    DECODE_CHK_NULL(matrixData);

    std::ostringstream oss;
    oss.setf(std::ios::showbase | std::ios::uppercase);

    if (matrixData->m_loadIntraQuantiserMatrix)
    {
        oss << "intra_QmatrixData:" << std::endl;

        for (uint8_t i = 0; i < 56; i += 8)
        {
            oss << "Qmatrix[" << +i / 8 << "]: ";
            for (uint8_t j = 0; j < 8; j++)
                oss << +matrixData->m_intraQuantiserMatrix[i + j] << " ";
            oss << std::endl;
        }
    }
    if (matrixData->m_loadNonIntraQuantiserMatrix)
    {
        oss << "non_intra_QmatrixData:" << std::endl;

        for (uint8_t i = 0; i < 56; i += 8)
        {
            oss << "Qmatrix[" << +i / 8 << "]: ";
            for (uint8_t j = 0; j < 8; j++)
                oss << +matrixData->m_nonIntraQuantiserMatrix[i + j] << " ";
            oss << std::endl;
        }
    }
    if (matrixData->m_loadChromaIntraQuantiserMatrix)
    {
        oss << "chroma_intra_QmatrixData:" << std::endl;

        for (uint8_t i = 0; i < 56; i += 8)
        {
            oss << "Qmatrix[" << +i / 8 << "]: ";
            for (uint8_t j = 0; j < 8; j++)
                oss << +matrixData->m_chromaIntraQuantiserMatrix[i + j] << " ";
            oss << std::endl;
        }
    }
    if (matrixData->m_loadChromaNonIntraQuantiserMatrix)
    {
        oss << "chroma_non_intra_QmatrixData:" << std::endl;

        for (uint8_t i = 0; i < 56; i += 8)
        {
            oss << "Qmatrix[" << +i / 8 << "]: ";
            for (uint8_t j = 0; j < 8; j++)
                oss << +matrixData->m_chromaNonIntraQuantiserMatrix[i + j] << " ";
            oss << std::endl;
        }
    }

    const char *fileName = m_debugInterface->CreateFileName(
        "_DEC",
        CodechalDbgBufferType::bufIqParams,
        CodechalDbgExtType::txt);

    std::ofstream ofs(fileName, std::ios::out);
    ofs << oss.str();
    ofs.close();
    return MOS_STATUS_SUCCESS;
}
#endif

}
