///////////////////////////////////////////////////////////////
//                                                           //
// File bits.cpp                                             //
//                                                           //
// I converted the bbDMux utility to a class with a simple   //
// interface.                                                //
// This class was created using bbdmux version 1.9 of bits.c //
//                                                           //
///////////////////////////////////////////////////////////////
/* bits.c, bit-level input */
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>

#include "bits.h"

namespace Mpeg2
{

Bits::Bits ( )
  : BufferSize ( 1024 * 1024 )
{
  m_pBuffer = new unsigned char [ BufferSize ];
}

Bits::~Bits ( )
{
  delete []m_pBuffer;
}

void Bits::debugOut ( const char *pString, ... )
{
  // For the library version we are only interested in the generated structures
  // and do not want to print to stdout.
#ifdef MAKE_EXE
  // at this point m_cBuffer should have file/func/line or should be empty.
  char cBuffer [ 1024 ];
  va_list theList;
  va_start ( theList, pString );
  vsprintf((char *)&cBuffer, pString, theList);
  //  vsprintf((char *)&(cBuffer[ strlen(cBuffer) ]), pString, theList);
  va_end ( theList );
  printf ( cBuffer );
#endif
}

/* return total number of generated bits */
double Bits::bitCount()
{
  return m_fTotalBits;
}

bool Bits::refillBuffer ( )
{
  int i;

  i = fread(m_pBuffer, sizeof(unsigned char), BufferSize, m_pFile);
  if (i <= 0)  {
    m_bEndOfBitStream = true;
    return false;
  }
  m_iBufferCount = i;
  return true;
}

/* open the device to read the bit stream from it */
bool Bits::initGetbits(char *pFileName)
{
  if ( (m_pFile = fopen ( pFileName, "rb" ) ) == NULL)  {
    debugOut("Unable to open file %s for reading.\n", pFileName);
    return false;
  }
  m_iByteIdx        = 0;
  m_iBitIdx         = 8;
  m_fTotalBits      = 0.0;
  m_iBufferCount    = 0;
  m_bEndOfBitStream = false;
  if ( ! refillBuffer ( ) )  {
    if ( m_bEndOfBitStream )  {
      debugOut ( "Unable to read from file %s.\n", pFileName );
      return false;
    }
  }
  return true;
}

/*close the device containing the bit stream after a read process*/
void Bits::finishGetbits()
{
  if (m_pFile)
    fclose(m_pFile);
  m_pFile = NULL;
}

/*read 1 bit from the bit stream */
uint Bits::get1Bit()
{
  int masks[8]={0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
  unsigned int bit;

  if ( m_bEndOfBitStream )
    return 0;

  bit = (m_pBuffer[m_iByteIdx] & masks[m_iBitIdx - 1]) >> (m_iBitIdx - 1);
  m_fTotalBits++;
  m_iBitIdx--;
  if (!m_iBitIdx)
  {
    m_iBitIdx = 8;
    m_iByteIdx++;
    if (m_iByteIdx == m_iBufferCount)
    {
      if (m_iBufferCount == BufferSize)
        refillBuffer();
      else
        m_bEndOfBitStream = true;
      m_iByteIdx = 0;
    }
  }
  return bit;
}

/*read N bit from the bit stream */
uint Bits::getBits(int N)
{
 unsigned int val = 0;
 int i = N;
 unsigned int j;

 // Optimize: we are on byte boundary and want to read multiple of bytes!
 if ((m_iBitIdx == 8) && ((N & 7) == 0))
 {
   i = N >> 3;
   while (i > 0)
   {
     val = (val << 8) | m_pBuffer[m_iByteIdx];
     m_iByteIdx++;
     m_fTotalBits += 8;
     if (m_iByteIdx == m_iBufferCount)
     {
       if (m_iBufferCount == BufferSize)
         refillBuffer();
       else
         m_bEndOfBitStream = true;
       m_iByteIdx = 0;
     }
     i--;
   }
   return val;
 }

 while (i > 0)
 {
   j = get1Bit();
   val = (val << 1) | j;
   i--;
 }
 return val;
}

/* return the status of the bit stream            */
/* returns 1 if end of bit stream was reached     */
/* returns 0 if end of bit stream was not reached */
int Bits::endBitStream ( )
{
  return m_bEndOfBitStream;
}

/*this function seeks for a byte aligned sync word (max 32 bits) in the bit stream and
  places the bit stream pointer right after the sync.
  This function returns 1 if the sync was found otherwise it returns 0  */
int Bits::seekSync ( uint sync, int N )
{
  unsigned int val;
  unsigned int maxi = (int)pow(2.0, (double)N) - 1;

  while (m_iBitIdx != 8)
    get1Bit();

  val = getBits(N);

  while ((val & maxi) != sync)
  {
    if (m_bEndOfBitStream)
      return 0;
    val <<= 8;
    val |= getBits(8);
  }
  return 1;
}

/*look ahead for the next N bits from the bit stream */
uint Bits::lookAhead ( int N )
{
  int masks[8]={0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
  unsigned long val = 0;
  unsigned char *tmp_bfr = m_pBuffer;
  unsigned char tmp_bfr1[4];
  int j = N;
  int eo_bs     = m_bEndOfBitStream;
  int buf_count = m_iBufferCount;
  int bit_idx   = m_iBitIdx;
  int byte_idx  = m_iByteIdx;

  while (j > 0)
  {
    if (eo_bs)
      return 0;
    val <<= 1;
    val |= (tmp_bfr[byte_idx] & masks[bit_idx - 1]) >> (bit_idx - 1);
    bit_idx--;
    j--;
    if (!bit_idx)
    {
      bit_idx = 8;
      byte_idx++;
      if (byte_idx == buf_count)
      {
        if (buf_count == BufferSize)
        {
          if (fread(tmp_bfr1, sizeof(unsigned char), 4, m_pFile) != 4)
            eo_bs = true;
          else
            tmp_bfr = &tmp_bfr1[0];
          if (fseek(m_pFile, -4, SEEK_CUR))
          {
            debugOut ("Unable to set file position.\n");
            exit ( 1 );
          }
        }
        else
          eo_bs = true;
        byte_idx = 0;
      }
    }
  }
  return val;
}

}; // End of namepace Mpeg2 ...
