// ShrinkDecoder.cpp

#include "StdAfx.h"

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

#include "../Common/InBuffer.h"
#include "../Common/OutBuffer.h"

#include "BitlDecoder.h"
#include "ShrinkDecoder.h"

namespace NCompress {
namespace NShrink {

static const UInt32 kEmpty = 256; // kNumItems;
static const UInt32 kBufferSize = (1 << 18);
static const unsigned kNumMinBits = 9;

HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
    const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)
{
  NBitl::CBaseDecoder<CInBuffer> inBuffer;
  COutBuffer outBuffer;

  if (!inBuffer.Create(kBufferSize))
    return E_OUTOFMEMORY;
  if (!outBuffer.Create(kBufferSize))
    return E_OUTOFMEMORY;

  inBuffer.SetStream(inStream);
  inBuffer.Init();

  outBuffer.SetStream(outStream);
  outBuffer.Init();

  {
    for (unsigned i = 0; i < kNumItems; i++)
      _parents[i] = kEmpty;
  }

  UInt64 outPrev = 0, inPrev = 0;
  unsigned numBits = kNumMinBits;
  unsigned head = 257;
  int lastSym = -1;
  Byte lastChar = 0;
  bool moreOut = false;

  HRESULT res = S_FALSE;

  for (;;)
  {
    _inProcessed = inBuffer.GetProcessedSize();
    const UInt64 nowPos = outBuffer.GetProcessedSize();

    bool eofCheck = false;

    if (outSize && nowPos >= *outSize)
    {
      if (!_fullStreamMode || moreOut)
      {
        res = S_OK;
        break;
      }
      eofCheck = true;
      // Is specSym(=256) allowed after end of stream ?
      // Do we need to read it here ?
    }

    if (progress)
    {
      if (nowPos - outPrev >= (1 << 20) || _inProcessed - inPrev >= (1 << 20))
      {
        outPrev = nowPos;
        inPrev = _inProcessed;
        res = progress->SetRatioInfo(&_inProcessed, &nowPos);
        if (res != SZ_OK)
        {
          // break;
          return res;
        }
      }
    }

    UInt32 sym = inBuffer.ReadBits(numBits);

    if (inBuffer.ExtraBitsWereRead())
    {
      res = S_OK;
      break;
    }
    
    if (sym == 256)
    {
      sym = inBuffer.ReadBits(numBits);

      if (inBuffer.ExtraBitsWereRead())
        break;

      if (sym == 1)
      {
        if (numBits >= kNumMaxBits)
          break;
        numBits++;
        continue;
      }
      if (sym != 2)
      {
        break;
        // continue; // info-zip just ignores such code
      }
      {
        /*
        ---------- Free leaf nodes ----------
        Note : that code can mark _parents[lastSym] as free, and next
        inserted node will be Orphan in that case.
        */

        unsigned i;
        for (i = 256; i < kNumItems; i++)
          _stack[i] = 0;
        for (i = 257; i < kNumItems; i++)
        {
          unsigned par = _parents[i];
          if (par != kEmpty)
            _stack[par] = 1;
        }
        for (i = 257; i < kNumItems; i++)
          if (_stack[i] == 0)
            _parents[i] = kEmpty;
        head = 257;
        continue;
      }
    }

    if (eofCheck)
    {
      // It's can be error case.
      // That error can be detected later in (*inSize != _inProcessed) check.
      res = S_OK;
      break;
    }

    bool needPrev = false;
    if (head < kNumItems && lastSym >= 0)
    {
      while (head < kNumItems && _parents[head] != kEmpty)
        head++;
      if (head < kNumItems)
      {
        /*
        if (head == lastSym), it updates Orphan to self-linked Orphan and creates two problems:
            1) we must check _stack[i++] overflow in code that walks tree nodes.
            2) self-linked node can not be removed. So such self-linked nodes can occupy all _parents items.
        */
        needPrev = true;
        _parents[head] = (UInt16)lastSym;
        _suffixes[head] = (Byte)lastChar;
        head++;
      }
    }

    lastSym = (int)sym;
    unsigned cur = sym;
    unsigned i = 0;
    
    while (cur >= 256)
    {
      _stack[i++] = _suffixes[cur];
      cur = _parents[cur];
      // don't change that code:
      // Orphan Check and self-linked Orphan check (_stack overflow check);
      if (cur == kEmpty || i >= kNumItems)
        break;
    }
    
    if (cur == kEmpty || i >= kNumItems)
      break;

    _stack[i++] = (Byte)cur;
    lastChar = (Byte)cur;

    if (needPrev)
      _suffixes[(size_t)head - 1] = (Byte)cur;

    if (outSize)
    {
      const UInt64 limit = *outSize - nowPos;
      if (i > limit)
      {
        moreOut = true;
        i = (unsigned)limit;
      }
    }

    do
      outBuffer.WriteByte(_stack[--i]);
    while (i);
  }
  
  RINOK(outBuffer.Flush());

  if (res == S_OK)
    if (_fullStreamMode)
    {
      if (moreOut)
        res = S_FALSE;
      const UInt64 nowPos = outBuffer.GetProcessedSize();
      if (outSize && *outSize != nowPos)
        res = S_FALSE;
      if (inSize && *inSize != _inProcessed)
        res = S_FALSE;
    }
  
  return res;
}


STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
    const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)
{
  try { return CodeReal(inStream, outStream, inSize, outSize, progress); }
  // catch(const CInBufferException &e) { return e.ErrorCode; }
  // catch(const COutBufferException &e) { return e.ErrorCode; }
  catch(const CSystemException &e) { return e.ErrorCode; }
  catch(...) { return S_FALSE; }
}


STDMETHODIMP CDecoder::SetFinishMode(UInt32 finishMode)
{
  _fullStreamMode = (finishMode != 0);
  return S_OK;
}


STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value)
{
  *value = _inProcessed;
  return S_OK;
}


}}
