// Copyright (c) 2012-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 "umc_defs.h"
#if defined(UMC_ENABLE_VP9_AV1_DECODE)

#include "umc_structures.h"
#include "umc_vp9_bitstream.h"
#include "umc_vp9_frame.h"

namespace UMC_VP9_DECODER
{

VP9Bitstream::VP9Bitstream()
{
    Reset(0, 0);
}

VP9Bitstream::VP9Bitstream(uint8_t * const pb, const uint32_t maxsize)
{
    Reset(pb, maxsize);
}

// Reset the bitstream with new data pointer
void VP9Bitstream::Reset(uint8_t * const pb, const uint32_t maxsize)
{
    Reset(pb, 0, maxsize);
}

// Reset the bitstream with new data pointer and bit offset
void VP9Bitstream::Reset(uint8_t * const pb, int32_t offset, const uint32_t maxsize)
{
    m_pbs       = pb;
    m_pbsBase   = pb;
    m_bitOffset = offset;
    m_maxBsSize = maxsize;

}

// Return bitstream array base address and size
void VP9Bitstream::GetOrg(uint8_t **pbs, uint32_t *size)
{
    *pbs       = m_pbsBase;
    *size      = m_maxBsSize; 
}

uint32_t VP9Bitstream::GetBits(uint32_t nbits)
{
    mfxU32 bits = 0;
    for (; nbits > 0; --nbits)
    {
        bits <<= 1;
        bits |= GetBit();
    }

    return bits;
}

uint32_t VP9Bitstream::GetUe()
{
    uint32_t zeroes = 0;
    while (GetBit() == 0)
        ++zeroes;

    return zeroes == 0 ?
        0 : ((1 << zeroes) | GetBits(zeroes)) - 1; 
}

int32_t VP9Bitstream::GetSe()
{
    int32_t val = GetUe();
    uint32_t sign = (val & 1);
    val = (val + 1) >> 1;

    return
        sign ? val : -int32_t(val); 
}

void GetFrameSize(VP9Bitstream* bs, VP9DecoderFrame* frame)
{
    frame->width = bs->GetBits(16) + 1;
    frame->height = bs->GetBits(16) + 1;

    GetDisplaySize(bs, frame);
}

void GetDisplaySize(VP9Bitstream* bs, VP9DecoderFrame* frame)
{
    frame->displayWidth = frame->width;
    frame->displayHeight = frame->height;

    if (bs->GetBit())
    {
        frame->displayWidth = bs->GetBits(16) + 1;
        frame->displayHeight = bs->GetBits(16) + 1;
    }
}

void GetBitDepthAndColorSpace(VP9Bitstream* bs, VP9DecoderFrame* frame)
{
    if (frame->profile >= 2)
        frame->bit_depth = bs->GetBit() ? 12 : 10;
    else
        frame->bit_depth = 8;

    if (frame->frameType != KEY_FRAME && frame->intraOnly && frame->profile == 0)
    {
        frame->subsamplingY = frame->subsamplingX = 1;
        return;
    }

    uint32_t const colorspace
        = bs->GetBits(3);

    if (colorspace != SRGB)
    {
        bs->GetBit(); // 0: [16, 235] (i.e. xvYCC), 1: [0, 255]
        if (1 == frame->profile || 3 == frame->profile)
        {
            frame->subsamplingX = bs->GetBit();
            frame->subsamplingY = bs->GetBit();
            bs->GetBit(); // reserved bit
        }
        else
            frame->subsamplingY = frame->subsamplingX = 1;
    }
    else
    {
        if (1 == frame->profile || 3 == frame->profile)
        {
            frame->subsamplingX = 0;
            frame->subsamplingY = 0;
            bs->GetBit(); // reserved bit
        }
        else
            throw vp9_exception(UMC::UMC_ERR_INVALID_STREAM);
    }
}

void GetFrameSizeWithRefs(VP9Bitstream* bs, VP9DecoderFrame* frame)
{
    bool bFound = false;
    for (uint8_t i = 0; i < REFS_PER_FRAME; ++i)
    {
        if (bs->GetBit())
        {
            bFound = true;
            frame->width = frame->sizesOfRefFrame[frame->activeRefIdx[i]].width;
            frame->height = frame->sizesOfRefFrame[frame->activeRefIdx[i]].height;
            break;
        }
    }

    if (!bFound)
    {
        frame->width = bs->GetBits(16) + 1;
        frame->height = bs->GetBits(16) + 1;
    }

    GetDisplaySize(bs, frame);
}

void SetupLoopFilter(VP9Bitstream* bs, Loopfilter* filter)
{
    filter->filterLevel = bs->GetBits(6);
    filter->sharpnessLevel = bs->GetBits(3);

    filter->modeRefDeltaUpdate = 0;

    mfxI8 value = 0;

    filter->modeRefDeltaEnabled = (uint8_t)bs->GetBit();
    if (filter->modeRefDeltaEnabled)
    {
        filter->modeRefDeltaUpdate = (uint8_t)bs->GetBit();

        if (filter->modeRefDeltaUpdate)
        {
            for (uint32_t i = 0; i < MAX_REF_LF_DELTAS; i++)
            {
                if (bs->GetBit())
                {
                    value = (int8_t)bs->GetBits(6);
                    filter->refDeltas[i] = bs->GetBit() ? -value : value;
                }
            }

            for (uint32_t i = 0; i < MAX_MODE_LF_DELTAS; i++)
            {
                if (bs->GetBit())
                {
                    value = (int8_t)bs->GetBits(6);
                    filter->modeDeltas[i] = bs->GetBit() ? -value : value;
                }
            }
        }
    }
}

} // namespace UMC_VP9_DECODER
#endif
