#pragma once

#include "UnBitArrayBase.h"

namespace APE
{

#pragma pack(push, 1)

class IAPEDecompress;

struct RANGE_CODER_STRUCT_DECOMPRESS
{
    unsigned int low;       // low end of interval
    unsigned int range;     // length of interval
    unsigned int buffer;    // buffer for input/output
    unsigned int padding;   // for 64-bit alignment
};

/**************************************************************************************************
RangeOverflowTable
**************************************************************************************************/
class RangeOverflowTable
{
public:
    RangeOverflowTable(const uint32 * RANGE_TOTAL);
    ~RangeOverflowTable();
#ifdef APE_ADD_GET_TO_RANGE_OVERFLOW_TABLE // only include if this define is set or else Clang will warn when UnBitArray.h is included in places this isn't called
    __forceinline uint8 Get(uint32 nIndex) const { return m_aryTable[nIndex]; }
#endif

private:
    uint8 m_aryTable[65536];
};

/**************************************************************************************************
CUnBitArray
**************************************************************************************************/
class CUnBitArray : public CUnBitArrayBase
{
public:
    // construction/destruction
    CUnBitArray(APE::CIO * pIO, intn nVersion, int64 nFurthestReadByte);
    ~CUnBitArray();

    uint32 DecodeValue(DECODE_VALUE_METHOD DecodeMethod, int nParam1 = 0, int nParam2 = 0) APE_OVERRIDE;
    void GenerateArray(int * pOutputArray, int nElements, intn nBytesRequired) APE_OVERRIDE;
    int64 DecodeValueRange(UNBIT_ARRAY_STATE & BitArrayState) APE_OVERRIDE;
    void FlushState(UNBIT_ARRAY_STATE & BitArrayState) APE_OVERRIDE;
    void FlushBitArray() APE_OVERRIDE;
    void Finalize() APE_OVERRIDE;

private:
    // data
    int m_nFlushCounter;
    int m_nFinalizeCounter;
    CSmartPtr<RangeOverflowTable> m_spRangeTable1;
    CSmartPtr<RangeOverflowTable> m_spRangeTable2;
    RANGE_CODER_STRUCT_DECOMPRESS m_RangeCoderInfo;

    // functions
    inline uint32 DecodeByte();
    uint32 DecodeOverflow(uint32 & nPivotValue);
    uint32 RangeDecodeFast(int nShift);
    uint32 RangeDecodeFastWithUpdate(int nShift);
    void GenerateArrayRange(int * pOutputArray, int nElements);
};

#pragma pack(pop)

}
