#include "Portable.h"
#include "stdio.h"
#include "stdlib.h"
#include "LZMADecoder.h"


//#define RETURN_E_OUTOFMEMORY_IF_FALSE(x) { if (!(x)) return E_OUTOFMEMORY; }


static UINT32    kDistStart[kDistTableSizeMax];
struct WindowOut out_window;

/*
 * BRCM modification: free all the allocated buffer by malloc
 *
 */
static void LzmaDecoderFreeBuffer(LzmaDecoder  *lzmaDecoder)
{
  int i,aPosState;
  
  //printf("free lzmaDecoder->m_LiteralDecoder\n");
  free((&lzmaDecoder->m_LiteralDecoder)->m_Coders);

  for (i = 0; i < kNumLenToPosStates; i++) {
      //printf("free lzmaDecoder->m_PosSlotDecoder\n");
      free((&lzmaDecoder->m_PosSlotDecoder[i])->m_Models);
  }
  // from LenDecoderInit(&lzmaDecoder->m_LenDecoder;
  for (aPosState = 0; aPosState < (&lzmaDecoder->m_LenDecoder)->m_NumPosStates; aPosState++) {
      //printf("free lzmaDecoder->m_PosSlotDecoder\n");
      free( (&(&lzmaDecoder->m_LenDecoder)->m_LowCoder[aPosState])->m_Models );
      //printf("free lzmaDecoder->m_PosSlotDecoder\n");
      free( (&(&lzmaDecoder->m_LenDecoder)->m_MidCoder[aPosState])->m_Models );
   }
  //printf("free lzmaDecoder->m_PosSlotDecoder\n");
  free( (&(&lzmaDecoder->m_LenDecoder)->m_HighCoder)->m_Models );


  // from LenDecoderInit(&lzmaDecoder->m_RepMatchLenDecoder);
  for (aPosState = 0; aPosState < (&lzmaDecoder->m_RepMatchLenDecoder)->m_NumPosStates; aPosState++) {
      //printf("free lzmaDecoder->m_PosSlotDecoder\n");
      free( (&(&lzmaDecoder->m_RepMatchLenDecoder)->m_LowCoder[aPosState])->m_Models );
      //printf("free lzmaDecoder->m_PosSlotDecoder\n");
      free( (&(&lzmaDecoder->m_RepMatchLenDecoder)->m_MidCoder[aPosState])->m_Models );
  }
  //printf("free lzmaDecoder->m_PosSlotDecoder\n");
  free( (&(&lzmaDecoder->m_RepMatchLenDecoder)->m_HighCoder)->m_Models );

    
  //printf("free lzmaDecoder->m_PosAlignDecoder\n");
  free((&lzmaDecoder->m_PosAlignDecoder)->m_Models);

  for(i = 0; i < kNumPosModels; i++) {
      //printf("free lzmaDecoder->m_PosDecoders\n");
      free((&lzmaDecoder->m_PosDecoders[i])->m_Models);
  }
  
}

HRESULT LzmaDecoderSetDictionarySize(
    LzmaDecoder  *lzmaDecoder, 
    UINT32        aDictionarySize)
{
  if (aDictionarySize > (1 << kDicLogSizeMax))
    return E_INVALIDARG;
  
//  UINT32 aWindowReservSize = MyMax(aDictionarySize, UINT32(1 << 21));

  if (lzmaDecoder->m_DictionarySize != aDictionarySize)
  {
    lzmaDecoder->m_DictionarySize = aDictionarySize;
  }
  return S_OK;
}

HRESULT LzmaDecoderSetLiteralProperties( 
    LzmaDecoder  *lzmaDecoder,
    UINT32        aLiteralPosStateBits, 
    UINT32        aLiteralContextBits)
{
  if (aLiteralPosStateBits > 8)
    return E_INVALIDARG;
  if (aLiteralContextBits > 8)
    return E_INVALIDARG;
  LitDecoderCreate(&lzmaDecoder->m_LiteralDecoder, aLiteralPosStateBits, aLiteralContextBits);
  return S_OK;
}

HRESULT LzmaDecoderSetPosBitsProperties(
    LzmaDecoder *lzmaDecoder, 
    UINT32       aNumPosStateBits)
{
  UINT32 aNumPosStates;
  if (aNumPosStateBits > (UINT32) kNumPosStatesBitsMax)
    return E_INVALIDARG;
  aNumPosStates = 1 << aNumPosStateBits;
  LenDecoderCreate(&lzmaDecoder->m_LenDecoder, aNumPosStates);
  LenDecoderCreate(&lzmaDecoder->m_RepMatchLenDecoder, aNumPosStates);
  lzmaDecoder->m_PosStateMask = aNumPosStates - 1;
  return S_OK;
}


void LzmaDecoderConstructor(LzmaDecoder *lzmaDecoder)
{
  lzmaDecoder->m_DictionarySize = ((UINT32)-1);
  LzmaDecoderCreate(lzmaDecoder);
}

HRESULT LzmaDecoderCreate(LzmaDecoder *lzmaDecoder)
{
  int i;
  for(i = 0; i < kNumPosModels; i++)
  {
    if (!(ReverseBitTreeDecoder2Create(&lzmaDecoder->m_PosDecoders[i],kDistDirectBits[kStartPosModelIndex + i])))
        return E_OUTOFMEMORY;;
  }
  return S_OK;
}


HRESULT LzmaDecoderInit(LzmaDecoder *lzmaDecoder)
{
  int    i;
  UINT32 j;

  RangeDecoderInit(&in_stream, &lzmaDecoder->m_RangeDecoder);

  OutWindowInit();

  for(i = 0; i < kNumStates; i++)
  {
    for (j = 0; j <= lzmaDecoder->m_PosStateMask; j++)
    {
      BitDecoderInit(&lzmaDecoder->m_MainChoiceDecoders[i][j]);
      BitDecoderInit(&lzmaDecoder->m_MatchRepShortChoiceDecoders[i][j]);
    }
    BitDecoderInit(&lzmaDecoder->m_MatchChoiceDecoders[i]);
    BitDecoderInit(&lzmaDecoder->m_MatchRepChoiceDecoders[i]);
    BitDecoderInit(&lzmaDecoder->m_MatchRep1ChoiceDecoders[i]);
    BitDecoderInit(&lzmaDecoder->m_MatchRep2ChoiceDecoders[i]);
  }
  
  LitDecoderInit(&lzmaDecoder->m_LiteralDecoder);
   
  for (i = 0; i < (int) kNumLenToPosStates; i++)
    BitTreeDecoderInit(&lzmaDecoder->m_PosSlotDecoder[i],kNumPosSlotBits);

  for(i = 0; i < kNumPosModels; i++)
    ReverseBitTreeDecoder2Init(&lzmaDecoder->m_PosDecoders[i]);
  
  LenDecoderInit(&lzmaDecoder->m_LenDecoder);
  LenDecoderInit(&lzmaDecoder->m_RepMatchLenDecoder);

  ReverseBitTreeDecoderInit(&lzmaDecoder->m_PosAlignDecoder, kNumAlignBits);
  return S_OK;

}

HRESULT LzmaDecoderCodeReal( 
    LzmaDecoder     *lzmaDecoder, 
    UINT64          *anInSize, 
    UINT64          *anOutSize)
{
  BOOL                  aPeviousIsMatch         = FALSE;
  BYTE                  aPreviousByte           = 0;
  UINT32                aRepDistances[kNumRepDistances];
  int                   i;
  UINT64                aNowPos64               = 0;
  UINT64                aSize                   = *anOutSize;
  ISequentialInStream   my_in_stream;
//  WindowOut             out_window;
  CState                aState;

  CStateInit(&aState);

  if (anOutSize == NULL)
  {
      printf("CodeReal: invalid argument %x\n", (UINT32) anOutSize );
      return E_INVALIDARG;
  }


  LzmaDecoderInit(lzmaDecoder);

  my_in_stream.data           = in_stream.data;
  my_in_stream.remainingBytes = in_stream.remainingBytes;

  for(i = 0 ; i < (int) kNumRepDistances; i++)
    aRepDistances[i] = 0;

  //while(aNowPos64 < aSize)
  while(my_in_stream.remainingBytes > 0)
  {
    UINT64 aNext = MyMin(aNowPos64 + (1 << 18), aSize);
    while(aNowPos64 < aNext)
    {
      UINT32 aPosState = (UINT32)(aNowPos64) & lzmaDecoder->m_PosStateMask;
      if (BitDecode(&my_in_stream, 
                    &lzmaDecoder->m_MainChoiceDecoders[aState][aPosState], 
                    &lzmaDecoder->m_RangeDecoder) == (UINT32) kMainChoiceLiteralIndex)
      {
        CStateUpdateChar(&aState);
        if(aPeviousIsMatch)
        {
          BYTE aMatchByte = OutWindowGetOneByte(0 - aRepDistances[0] - 1);
          aPreviousByte = LitDecodeWithMatchByte(&my_in_stream, 
                                                 &lzmaDecoder->m_LiteralDecoder, 
                                                 &lzmaDecoder->m_RangeDecoder, 
                                                 (UINT32)(aNowPos64), 
                                                 aPreviousByte, 
                                                 aMatchByte);
          aPeviousIsMatch = FALSE;
        }
        else
          aPreviousByte = LitDecodeNormal(&my_in_stream, 
                                          &lzmaDecoder->m_LiteralDecoder, 
                                          &lzmaDecoder->m_RangeDecoder, 
                                         (UINT32)(aNowPos64), 
                                          aPreviousByte);
        OutWindowPutOneByte(aPreviousByte);
        aNowPos64++;
      }
      else             
      {
        UINT32 aDistance, aLen;
        aPeviousIsMatch = TRUE;
        if(BitDecode(&my_in_stream, 
                     &lzmaDecoder->m_MatchChoiceDecoders[aState], 
                     &lzmaDecoder->m_RangeDecoder) == (UINT32) kMatchChoiceRepetitionIndex)
        {
          if(BitDecode(&my_in_stream, 
                       &lzmaDecoder->m_MatchRepChoiceDecoders[aState], 
                       &lzmaDecoder->m_RangeDecoder) == 0)
          {
            if(BitDecode(&my_in_stream, 
                         &lzmaDecoder->m_MatchRepShortChoiceDecoders[aState][aPosState], 
                         &lzmaDecoder->m_RangeDecoder) == 0)
            {
              CStateUpdateShortRep(&aState);
              aPreviousByte = OutWindowGetOneByte(0 - aRepDistances[0] - 1);
              OutWindowPutOneByte(aPreviousByte);
              aNowPos64++;
              continue;
            }
            aDistance = aRepDistances[0];
          }
          else
          {
            if(BitDecode(&my_in_stream, 
                         &lzmaDecoder->m_MatchRep1ChoiceDecoders[aState], 
                         &lzmaDecoder->m_RangeDecoder) == 0)
            {
              aDistance = aRepDistances[1];
              aRepDistances[1] = aRepDistances[0];
            }
            else 
            {
              if (BitDecode(&my_in_stream, 
                            &lzmaDecoder->m_MatchRep2ChoiceDecoders[aState], 
                            &lzmaDecoder->m_RangeDecoder) == 0)
              {
                aDistance = aRepDistances[2];
              }
              else
              {
                aDistance = aRepDistances[3];
                aRepDistances[3] = aRepDistances[2];
              }
              aRepDistances[2] = aRepDistances[1];
              aRepDistances[1] = aRepDistances[0];
            }
            aRepDistances[0] = aDistance;
          }
          aLen = LenDecode(&my_in_stream, 
                           &lzmaDecoder->m_RepMatchLenDecoder, 
                           &lzmaDecoder->m_RangeDecoder, 
                           aPosState) + kMatchMinLen;
          CStateUpdateRep(&aState);
        }
        else
        {
          UINT32 aPosSlot;
          aLen = kMatchMinLen + LenDecode(&my_in_stream, 
                                          &lzmaDecoder->m_LenDecoder, 
                                          &lzmaDecoder->m_RangeDecoder, 
                                          aPosState);
          CStateUpdateMatch(&aState);
          aPosSlot = BitTreeDecode(&my_in_stream, 
                                   &lzmaDecoder->m_PosSlotDecoder[GetLenToPosState(aLen)],
                                   &lzmaDecoder->m_RangeDecoder);
          if (aPosSlot >= (UINT32) kStartPosModelIndex)
          {
            aDistance = kDistStart[aPosSlot];
            if (aPosSlot < (UINT32) kEndPosModelIndex)
              aDistance += ReverseBitTreeDecoder2Decode(&my_in_stream, 
                                                        &lzmaDecoder->m_PosDecoders[aPosSlot - kStartPosModelIndex],
                                                        &lzmaDecoder->m_RangeDecoder);
            else
            {
              aDistance += (RangeDecodeDirectBits(&my_in_stream, 
                                                  &lzmaDecoder->m_RangeDecoder, 
                                                  kDistDirectBits[aPosSlot] - kNumAlignBits) << kNumAlignBits);
              aDistance += ReverseBitTreeDecoderDecode(&my_in_stream, 
                                                       &lzmaDecoder->m_PosAlignDecoder, 
                                                       &lzmaDecoder->m_RangeDecoder);
            }
          }
          else
            aDistance = aPosSlot;

          
          aRepDistances[3] = aRepDistances[2];
          aRepDistances[2] = aRepDistances[1];
          aRepDistances[1] = aRepDistances[0];
          
          aRepDistances[0] = aDistance;
        }
        if (aDistance >= aNowPos64)
        {
            printf("CodeReal: invalid data\n" );
            return E_INVALIDDATA;
        }
        OutWindowCopyBackBlock(aDistance, aLen);
        aNowPos64 += aLen;
        aPreviousByte = OutWindowGetOneByte(0 - 1);
      }
    }
  }
  
  //BRCM modification
  LzmaDecoderFreeBuffer(lzmaDecoder);
  
  OutWindowFlush();
  return S_OK;
}

HRESULT LzmaDecoderCode(
    LzmaDecoder *lzmaDecoder,
    UINT64 *anInSize, 
    UINT64 *anOutSize)
{

    UINT32 aStartValue = 0;
    int i;
    
    for (i = 0; i < kDistTableSizeMax; i++)
    {
        kDistStart[i] = aStartValue;
        aStartValue += (1 << kDistDirectBits[i]);
    }
    return LzmaDecoderCodeReal( 
        lzmaDecoder, 
        anInSize, 
        anOutSize);
}

HRESULT LzmaDecoderReadCoderProperties(LzmaDecoder *lzmaDecoder)
{
  UINT32 aNumPosStateBits;
  UINT32 aLiteralPosStateBits;
  UINT32 aLiteralContextBits;
  UINT32 aDictionarySize;
  BYTE   aRemainder;
  UINT32 aProcessesedSize;

  BYTE aByte;
  RETURN_IF_NOT_S_OK(InStreamRead(&aByte, 
                                  sizeof(aByte), 
                                  &aProcessesedSize));

  if (aProcessesedSize != sizeof(aByte))
    return E_INVALIDARG;

  aLiteralContextBits   = aByte % 9;
  aRemainder            = aByte / 9;
  aLiteralPosStateBits  = aRemainder % 5;
  aNumPosStateBits      = aRemainder / 5;

  RETURN_IF_NOT_S_OK(InStreamRead(&aDictionarySize, 
                                  sizeof(aDictionarySize), 
                                  &aProcessesedSize));

  if (aProcessesedSize != sizeof(aDictionarySize))
    return E_INVALIDARG;

  RETURN_IF_NOT_S_OK( LzmaDecoderSetDictionarySize(lzmaDecoder, 
                                                   aDictionarySize) );
  RETURN_IF_NOT_S_OK( LzmaDecoderSetLiteralProperties(lzmaDecoder, 
                                                      aLiteralPosStateBits, 
                                                      aLiteralContextBits) );
  RETURN_IF_NOT_S_OK( LzmaDecoderSetPosBitsProperties(lzmaDecoder, 
                                                      aNumPosStateBits) );

  return S_OK;
}

