// LZMA/Decoder.h



#ifndef __LZMA_DECODER_H

#define __LZMA_DECODER_H



#include "../../../Common/MyCom.h"

#include "../../ICoder.h"

#include "../LZ/LZOutWindow.h"

#include "../RangeCoder/RangeCoderBitTree.h"



extern "C"

{

  #include "../../../../C/Alloc.h"

}



#include "LZMA.h"



namespace NCompress {

namespace NLZMA {



typedef NRangeCoder::CBitDecoder<kNumMoveBits> CMyBitDecoder;



class CLiteralDecoder2

{

  CMyBitDecoder _decoders[0x300];

public:

  void Init()

  {

    for (int i = 0; i < 0x300; i++)

      _decoders[i].Init();

  }

  Byte DecodeNormal(NRangeCoder::CDecoder *rangeDecoder)

  {

    UInt32 symbol = 1;

    RC_INIT_VAR

    do

    {

      // symbol = (symbol << 1) | _decoders[0][symbol].Decode(rangeDecoder);

      RC_GETBIT(kNumMoveBits, _decoders[symbol].Prob, symbol)

    }

    while (symbol < 0x100);

    RC_FLUSH_VAR

    return (Byte)symbol;

  }

  Byte DecodeWithMatchByte(NRangeCoder::CDecoder *rangeDecoder, Byte matchByte)

  {

    UInt32 symbol = 1;

    RC_INIT_VAR

    do

    {

      UInt32 matchBit = (matchByte >> 7) & 1;

      matchByte <<= 1;

      // UInt32 bit = _decoders[1 + matchBit][symbol].Decode(rangeDecoder);

      // symbol = (symbol << 1) | bit;

      UInt32 bit;

      RC_GETBIT2(kNumMoveBits, _decoders[0x100 + (matchBit << 8) + symbol].Prob, symbol, 

          bit = 0, bit = 1)

      if (matchBit != bit)

      {

        while (symbol < 0x100)

        {

          // symbol = (symbol << 1) | _decoders[0][symbol].Decode(rangeDecoder);

          RC_GETBIT(kNumMoveBits, _decoders[symbol].Prob, symbol)

        }

        break;

      }

    }

    while (symbol < 0x100);

    RC_FLUSH_VAR

    return (Byte)symbol;

  }

};



class CLiteralDecoder

{

  CLiteralDecoder2 *_coders;

  int _numPrevBits;

  int _numPosBits;

  UInt32 _posMask;

public:

  CLiteralDecoder(): _coders(0) {}

  ~CLiteralDecoder()  { Free(); }

  void Free()

  { 

    MyFree(_coders);

    _coders = 0;

  }

  bool Create(int numPosBits, int numPrevBits)

  {

    if (_coders == 0 || (numPosBits + numPrevBits) != 

        (_numPrevBits + _numPosBits) )

    {

      Free();

      UInt32 numStates = 1 << (numPosBits + numPrevBits);

      _coders = (CLiteralDecoder2 *)MyAlloc(numStates * sizeof(CLiteralDecoder2));

    }

    _numPosBits = numPosBits;

    _posMask = (1 << numPosBits) - 1;

    _numPrevBits = numPrevBits;

    return (_coders != 0);

  }

  void Init()

  {

    UInt32 numStates = 1 << (_numPrevBits + _numPosBits);

    for (UInt32 i = 0; i < numStates; i++)

      _coders[i].Init();

  }

  UInt32 GetState(UInt32 pos, Byte prevByte) const

    { return ((pos & _posMask) << _numPrevBits) + (prevByte >> (8 - _numPrevBits)); }

  Byte DecodeNormal(NRangeCoder::CDecoder *rangeDecoder, UInt32 pos, Byte prevByte)

    { return _coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); }

  Byte DecodeWithMatchByte(NRangeCoder::CDecoder *rangeDecoder, UInt32 pos, Byte prevByte, Byte matchByte)

    { return _coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); }

};



namespace NLength {



class CDecoder

{

  CMyBitDecoder _choice;

  CMyBitDecoder _choice2;

  NRangeCoder::CBitTreeDecoder<kNumMoveBits, kNumLowBits>  _lowCoder[kNumPosStatesMax];

  NRangeCoder::CBitTreeDecoder<kNumMoveBits, kNumMidBits>  _midCoder[kNumPosStatesMax];

  NRangeCoder::CBitTreeDecoder<kNumMoveBits, kNumHighBits> _highCoder; 

public:

  void Init(UInt32 numPosStates)

  {

    _choice.Init();

    _choice2.Init();

    for (UInt32 posState = 0; posState < numPosStates; posState++)

    {

      _lowCoder[posState].Init();

      _midCoder[posState].Init();

    }

    _highCoder.Init();

  }

  UInt32 Decode(NRangeCoder::CDecoder *rangeDecoder, UInt32 posState)

  {

    if(_choice.Decode(rangeDecoder) == 0)

      return _lowCoder[posState].Decode(rangeDecoder);

    if(_choice2.Decode(rangeDecoder) == 0)

      return kNumLowSymbols + _midCoder[posState].Decode(rangeDecoder);

    return kNumLowSymbols + kNumMidSymbols + _highCoder.Decode(rangeDecoder);

  }

};



}



class CDecoder: 

  public ICompressCoder,

  public ICompressSetDecoderProperties2,

  public ICompressGetInStreamProcessedSize,

  #ifndef NO_READ_FROM_CODER

  public ICompressSetInStream,

  public ICompressSetOutStreamSize,

  public ISequentialInStream,

  #endif

  public CMyUnknownImp

{

  CLZOutWindow _outWindowStream;

  NRangeCoder::CDecoder _rangeDecoder;



  CMyBitDecoder _isMatch[kNumStates][NLength::kNumPosStatesMax];

  CMyBitDecoder _isRep[kNumStates];

  CMyBitDecoder _isRepG0[kNumStates];

  CMyBitDecoder _isRepG1[kNumStates];

  CMyBitDecoder _isRepG2[kNumStates];

  CMyBitDecoder _isRep0Long[kNumStates][NLength::kNumPosStatesMax];



  NRangeCoder::CBitTreeDecoder<kNumMoveBits, kNumPosSlotBits> _posSlotDecoder[kNumLenToPosStates];



  CMyBitDecoder _posDecoders[kNumFullDistances - kEndPosModelIndex];

  NRangeCoder::CBitTreeDecoder<kNumMoveBits, kNumAlignBits> _posAlignDecoder;

  

  NLength::CDecoder _lenDecoder;

  NLength::CDecoder _repMatchLenDecoder;



  CLiteralDecoder _literalDecoder;



  UInt32 _posStateMask;



  ///////////////////

  // State

  UInt32 _reps[4];

  CState _state;

  Int32 _remainLen; // -1 means end of stream. // -2 means need Init

  UInt64 _outSize;

  bool _outSizeDefined;



  void Init();

  HRESULT CodeSpec(UInt32 size);

public:



  #ifndef NO_READ_FROM_CODER

  MY_UNKNOWN_IMP5(

      ICompressSetDecoderProperties2, 

      ICompressGetInStreamProcessedSize,

      ICompressSetInStream, 

      ICompressSetOutStreamSize, 

      ISequentialInStream)

  #else

  MY_UNKNOWN_IMP2(

      ICompressSetDecoderProperties2,

      ICompressGetInStreamProcessedSize)

  #endif



  void ReleaseStreams()

  {

    _outWindowStream.ReleaseStream();

    ReleaseInStream();

  }



  class CDecoderFlusher

  {

    CDecoder *_decoder;

  public:

    bool NeedFlush;

    CDecoderFlusher(CDecoder *decoder): _decoder(decoder), NeedFlush(true) {}

    ~CDecoderFlusher() 

    { 

      if (NeedFlush)

        _decoder->Flush();

      _decoder->ReleaseStreams(); 

    }

  };



  HRESULT Flush() {  return _outWindowStream.Flush(); }  



  STDMETHOD(CodeReal)(ISequentialInStream *inStream,

      ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,

      ICompressProgressInfo *progress);

  

  STDMETHOD(Code)(ISequentialInStream *inStream,

      ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,

      ICompressProgressInfo *progress);



  STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size);



  STDMETHOD(GetInStreamProcessedSize)(UInt64 *value);



  STDMETHOD(SetInStream)(ISequentialInStream *inStream);

  STDMETHOD(ReleaseInStream)();

  STDMETHOD(SetOutStreamSize)(const UInt64 *outSize);



  #ifndef NO_READ_FROM_CODER

  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);

  #endif



  CDecoder(): _outSizeDefined(false) {}

  virtual ~CDecoder() {}

};



}}



#endif

