/******************************************************************************
*
* Copyright (C) 2012 Ittiam Systems Pvt Ltd, Bangalore
*
* 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.
*
******************************************************************************/
/**
*******************************************************************************
* @file
*  ihevcd_bitstream.c
*
* @brief
*  Contains functions for bitstream access
*
* @author
*  Harish
*
* @par List of Functions:
* - ihevcd_bits_init()
* - ihevcd_bits_flush()
* - ihevcd_bits_flush_to_byte_boundary()
* - ihevcd_bits_nxt()
* - ihevcd_bits_nxt32()
* - ihevcd_bits_get()
* - ihevcd_bits_num_bits_remaining()
* - ihevcd_bits_num_bits_consumed()
* - ihevcd_sev()
* - ihevcd_uev()
*
*
* @remarks
*  None
*
*******************************************************************************
*/
/*****************************************************************************/
/* File Includes                                                             */
/*****************************************************************************/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "ihevc_typedefs.h"
#include "iv.h"
#include "ivd.h"
#include "ihevcd_cxa.h"

#include "ihevc_defs.h"
#include "ihevc_debug.h"
#include "ihevc_structs.h"
#include "ihevc_macros.h"
#include "ihevc_platform_macros.h"
#include "ihevc_cabac_tables.h"

#include "ihevcd_defs.h"
#include "ihevcd_function_selector.h"
#include "ihevcd_structs.h"
#include "ihevcd_error.h"
#include "ihevcd_bitstream.h"

/*****************************************************************************/
/* Function Prototypes                                                       */
/*****************************************************************************/

/**
*******************************************************************************
*
* @brief
*  Function used for bitstream structure initialization
*
* @par Description:
*  Initialize bitstream structure elements
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @param[in] pu1_buf
*  Pointer to bitstream data
*
* @param[in] u4_numbytes
*  Number of bytes in bitstream
*
* @returns  none
*
* @remarks
*  Assumes pu1_buf is aligned to 4 bytes. If not aligned  then all bitstream
* accesses will be unaligned and hence  costlier. Since this is codec memory
* that holds emulation prevented data, assumption of aligned to 4 bytes is
* valid
*
*******************************************************************************
*/
void ihevcd_bits_init(bitstrm_t *ps_bitstrm,
                      UWORD8 *pu1_buf,
                      UWORD32 u4_numbytes)
{
    UWORD32 u4_cur_word;
    UWORD32 u4_nxt_word;
    UWORD32 u4_temp;
    UWORD32 *pu4_buf;

    pu4_buf     = (UWORD32 *)pu1_buf;
    u4_temp = *pu4_buf++;
    u4_cur_word = ITT_BIG_ENDIAN(u4_temp);
    u4_temp = *pu4_buf++;
    u4_nxt_word = ITT_BIG_ENDIAN(u4_temp);

    ps_bitstrm->u4_bit_ofst     = 0;
    ps_bitstrm->pu1_buf_base    = pu1_buf;
    ps_bitstrm->pu4_buf         = pu4_buf;
    ps_bitstrm->u4_cur_word     = u4_cur_word;
    ps_bitstrm->u4_nxt_word     = u4_nxt_word;

    ps_bitstrm->pu1_buf_max     = pu1_buf + u4_numbytes + 8;

    return;
}

/**
*******************************************************************************
*
* @brief
*  Flushes given number of bits. Bits consumed increases by  this number
*
* @par Description:
*  Increment bit offset by numbits. If bit offset increases  beyond 32, then
* move nxt_word to cur_word, read next  word32 to nxt_word after endian
* conversion
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @param[in] u4_numbits
*  Number of bits to be flushed
*
* @returns  None
*
* @remarks
*
*
*******************************************************************************
*/
void ihevcd_bits_flush(bitstrm_t *ps_bitstrm, UWORD32 u4_numbits)
{

    BITS_FLUSH(ps_bitstrm->pu4_buf,
               ps_bitstrm->u4_bit_ofst,
               ps_bitstrm->u4_cur_word,
               ps_bitstrm->u4_nxt_word,
               u4_numbits);

    return;
}

/**
*******************************************************************************
*
* @brief
*  Flushes to next byte boundary.Bits consumed increases by  this number
*
* @par Description:
*  Compute number of bits remaining in the current byte  then call
* ihevcd_bits_flush() bits with this number
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @returns  None
*
* @remarks
*
*
*******************************************************************************
*/
void ihevcd_bits_flush_to_byte_boundary(bitstrm_t *ps_bitstrm)
{
    UWORD32 u4_numbits;
    u4_numbits = (ps_bitstrm->u4_bit_ofst) & 7;

    u4_numbits = 8 - u4_numbits;

    BITS_FLUSH(ps_bitstrm->pu4_buf,
               ps_bitstrm->u4_bit_ofst,
               ps_bitstrm->u4_cur_word,
               ps_bitstrm->u4_nxt_word,
               u4_numbits);

    return;
}

/**
*******************************************************************************
*
* @brief
*  Seeks by given number of bits in the bitstream from current position
*
* @par Description:
*  Add given number of bits to bitstream offset and update pu4_buf, cur_word and
*  nxt_word accordingly
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @param[in] numbits
*  Number of bits to seek
*
* @returns  None
*
* @remarks
* Assumes emulation prevention has been done before and the buffer does not
* contain any emulation prevention bytes
*
*******************************************************************************
*/
void ihevcd_bits_seek(bitstrm_t *ps_bitstrm, WORD32 numbits)
{
    WORD32 val;
    ASSERT(numbits >= -32);
    ASSERT(numbits <= 32);
    /* Check if Seeking backwards*/
    if(numbits < 0)
    {
        UWORD32 abs_numbits = -numbits;
        if(ps_bitstrm->u4_bit_ofst >= abs_numbits)
        {
            /* If the current offset is greater than number of bits to seek back,
             * then subtract abs_numbits from offset and return.
             */
            ps_bitstrm->u4_bit_ofst -= abs_numbits;
            return;
        }
        else
        {
            /* If the current offset is lesser than number of bits to seek back,
             * then subtract abs_numbits from offset and add 32 and move cur_word to nxt_word
             * and load cur_word appropriately and decrement pu4_buf
             */
            ps_bitstrm->u4_bit_ofst -= abs_numbits;
            ps_bitstrm->u4_bit_ofst += 32;
            ps_bitstrm->pu4_buf--;

            val = *(ps_bitstrm->pu4_buf - 2);
            ps_bitstrm->u4_nxt_word = ps_bitstrm->u4_cur_word;
            ps_bitstrm->u4_cur_word = ITT_BIG_ENDIAN(val);
            return;
        }
    }
    else
    {
        /* Not supported/tested currently */
        ASSERT(1);
        BITS_FLUSH(ps_bitstrm->pu4_buf,
                   ps_bitstrm->u4_bit_ofst,
                   ps_bitstrm->u4_cur_word,
                   ps_bitstrm->u4_nxt_word,
                   numbits);


    }
    return;
}
/**
*******************************************************************************
*
* @brief
*  Snoops for next numbits number of bits from the bitstream this does not
* update the bitstream offset and does not  consume the bits
*
* @par Description:
*  Extract required number of bits from cur_word & nxt_word  return these
* bits
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @param[in] u4_numbits
*  Number of bits
*
* @returns  Next u4_numbits number of bits
*
* @remarks
*
*
*******************************************************************************
*/
UWORD32 ihevcd_bits_nxt(bitstrm_t *ps_bitstrm, UWORD32 u4_numbits)
{
    UWORD32 u4_bits_read;

    BITS_NXT(u4_bits_read,
             ps_bitstrm->pu4_buf,
             ps_bitstrm->u4_bit_ofst,
             ps_bitstrm->u4_cur_word,
             ps_bitstrm->u4_nxt_word,
             u4_numbits);
    return u4_bits_read;
}
/**
*******************************************************************************
*
* @brief
*  Snoops for next 32 bits from the bitstream  this does not update the
* bitstream offset and does not  consume the bits
*
* @par Description:
*  Extract required number of bits from cur_word & nxt_word  return these
* bits
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @param[in] u4_numbits
*  Number of bits
*
* @returns  Next 32 bits
*
* @remarks
*
*
*******************************************************************************
*/
UWORD32 ihevcd_bits_nxt32(bitstrm_t *ps_bitstrm, UWORD32 u4_numbits)
{
    UWORD32 u4_bits_read;
    UNUSED(u4_numbits);
    BITS_NXT32(u4_bits_read,
               ps_bitstrm->pu4_buf,
               ps_bitstrm->u4_bit_ofst,
               ps_bitstrm->u4_cur_word,
               ps_bitstrm->u4_nxt_word);
    return u4_bits_read;
}

/**
*******************************************************************************
*
* @brief
*  Reads next numbits number of bits from the bitstream  this updates the
* bitstream offset and consumes the bits
*
* @par Description:
*  Extract required number of bits from cur_word & nxt_word  return these
* bits
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @param[in] u4_numbits
*  Number of bits
*
* @returns  Bits read
*
* @remarks
*
*
*******************************************************************************
*/
UWORD32 ihevcd_bits_get(bitstrm_t *ps_bitstrm, UWORD32 u4_numbits)
{
    UWORD32 u4_bits_read;

    BITS_GET(u4_bits_read,
             ps_bitstrm->pu4_buf,
             ps_bitstrm->u4_bit_ofst,
             ps_bitstrm->u4_cur_word,
             ps_bitstrm->u4_nxt_word,
             u4_numbits);
    return u4_bits_read;

}

/**
*******************************************************************************
*
* @brief
*  Returns the number of bits remaining in the bitstream
*
* @par Description:
*  Compute number of bits remaining based on current pointer and buffer base
* and current offset. Since 8 bytes are  read at the start into cur_word and
* nxt_word and are not  consumed, 8 has to be subtracted
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @returns  Total number of bits remaining
*
* @remarks
*
*
*******************************************************************************
*/
UWORD32  ihevcd_bits_num_bits_remaining(bitstrm_t *ps_bitstrm)
{
    UWORD32 u4_bits_consumed;
    UWORD32 u4_size_in_bits;

    /* 8 bytes are read in cur_word and nxt_word at the start. Hence */
    /* subtract 8 bytes */
    u4_bits_consumed = (UWORD32)(((UWORD8 *)ps_bitstrm->pu4_buf -
                                  (UWORD8 *)ps_bitstrm->pu1_buf_base - 8) <<
                                 3) + ps_bitstrm->u4_bit_ofst;

    u4_size_in_bits = (UWORD32)(ps_bitstrm->pu1_buf_max -
                    ps_bitstrm->pu1_buf_base) - 8;
    u4_size_in_bits <<= 3;
    if(u4_size_in_bits > u4_bits_consumed)
    {
        return (u4_size_in_bits - u4_bits_consumed);
    }
    else
    {
        return 0;
    }
}

/**
*******************************************************************************
*
* @brief
*  Returns the number of bits consumed in the bitstream
*
* @par Description:
*  Compute number of bits consumed based on current pointer  and buffer base
* and current offset. Since 8 bytes are  read at the start into cur_word and
* nxt_word and are not  consumed, 8 has to be subtracted
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @returns  Total number of bits bits consumed
*
* @remarks
*
*
*******************************************************************************
*/
UWORD32  ihevcd_bits_num_bits_consumed(bitstrm_t *ps_bitstrm)
{
    UWORD32 u4_bits_consumed;
    /* 8 bytes are read in cur_word and nxt_word at the start. Hence */
    /* subtract 8 bytes */

    u4_bits_consumed = (UWORD32)(((UWORD8 *)ps_bitstrm->pu4_buf -
                                  (UWORD8 *)ps_bitstrm->pu1_buf_base - 8) <<
                                 3) + ps_bitstrm->u4_bit_ofst;
    return u4_bits_consumed;
}

/**
*******************************************************************************
*
* @brief
*  Reads unsigned integer 0-th order exp-golomb-coded syntax element from
* the bitstream  Section: 9.2
*
* @par Description:
*  Extract required number of bits from cur_word & nxt_word  return these
* bits
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @returns  UEV decoded syntax element
*
* @remarks
*
*
*******************************************************************************
*/
UWORD32 ihevcd_uev(bitstrm_t *ps_bitstrm)
{
    UWORD32 u4_bits_read;
    UWORD32 u4_clz;


    /***************************************************************/
    /* Find leading zeros in next 32 bits                          */
    /***************************************************************/
    BITS_NXT32(u4_bits_read,
               ps_bitstrm->pu4_buf,
               ps_bitstrm->u4_bit_ofst,
               ps_bitstrm->u4_cur_word,
               ps_bitstrm->u4_nxt_word);


    u4_clz = CLZ(u4_bits_read);

    BITS_FLUSH(ps_bitstrm->pu4_buf,
               ps_bitstrm->u4_bit_ofst,
               ps_bitstrm->u4_cur_word,
               ps_bitstrm->u4_nxt_word,
               (u4_clz + 1));

    u4_bits_read = 0;
    if(u4_clz)
    {
        BITS_GET(u4_bits_read,
                 ps_bitstrm->pu4_buf,
                 ps_bitstrm->u4_bit_ofst,
                 ps_bitstrm->u4_cur_word,
                 ps_bitstrm->u4_nxt_word,
                 u4_clz);
    }
    return ((1 << u4_clz) + u4_bits_read - 1);

}

/**
*******************************************************************************
*
* @brief
*  Reads signed integer 0-th order exp-golomb-coded syntax  element from the
* bitstream. Function similar to get_uev  Section: 9.2.1
*
* @par Description:
*  Extract required number of bits from cur_word & nxt_word  return these
* bits
*
* @param[in] ps_bitstrm
*  Pointer to bitstream structure
*
* @returns  UEV decoded syntax element
*
* @remarks
*
*
*******************************************************************************
*/
WORD32 ihevcd_sev(bitstrm_t *ps_bitstrm)
{
    UWORD32 u4_bits_read;
    UWORD32 u4_clz;
    UWORD32 u4_abs_val;


    /***************************************************************/
    /* Find leading zeros in next 32 bits                          */
    /***************************************************************/
    BITS_NXT32(u4_bits_read,
               ps_bitstrm->pu4_buf,
               ps_bitstrm->u4_bit_ofst,
               ps_bitstrm->u4_cur_word,
               ps_bitstrm->u4_nxt_word);


    u4_clz = CLZ(u4_bits_read);

    BITS_FLUSH(ps_bitstrm->pu4_buf,
               ps_bitstrm->u4_bit_ofst,
               ps_bitstrm->u4_cur_word,
               ps_bitstrm->u4_nxt_word,
               (u4_clz + 1));

    u4_bits_read = 0;
    if(u4_clz)
    {
        BITS_GET(u4_bits_read,
                 ps_bitstrm->pu4_buf,
                 ps_bitstrm->u4_bit_ofst,
                 ps_bitstrm->u4_cur_word,
                 ps_bitstrm->u4_nxt_word,
                 u4_clz);
    }
    u4_abs_val = ((1 << u4_clz) + u4_bits_read) >> 1;
    if(u4_bits_read & 0x1)
        return (-(WORD32)u4_abs_val);
    else
        return (u4_abs_val);
}