/*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
 *
 *  Use of this source code is governed by a zlib-style license that can
 *  be found in the License.txt file in the root of the source tree.
 */

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Read a stream bit per bit, Little Endian version (rarely used!!!)
// Can read up to 32 bits at once
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
#ifndef ZenBitStream_LEH
#define ZenBitStream_LEH
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "ZenLib/BitStream.h"
//---------------------------------------------------------------------------

namespace ZenLib
{

class BitStream_LE : public BitStream
{
public:
    BitStream_LE()                                                              : BitStream(), 
                                                                                  endbyte(0), 
                                                                                  endbit(0), 
                                                                                  buffer(NULL), 
                                                                                  ptr(NULL), 
                                                                                  ptr_BeforeLastCall(NULL), 
                                                                                  storage(0) {
    };

    BitStream_LE (const int8u* Buffer_, size_t Size_)                           : BitStream(Buffer_, Size_),
                                                                                  ptr_BeforeLastCall(NULL) {
        Attach(Buffer_, Size_);
    };

    void Attach(const int8u* Buffer_, size_t Size_)
    {
        endbyte=0;
        endbit=0;
        buffer=Buffer_;
        ptr=Buffer_;
        storage=(long)Size_;
    };

    int32u Get (size_t HowMany)
    {
        ptr_BeforeLastCall=ptr;

        long ret;
        static const int32u Mask[33]={
          0x00000000,
          0x00000001, 0x00000003, 0x00000007, 0x0000000f,
          0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
          0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
          0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
          0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
          0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
          0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
          0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff,
        };
        unsigned long m=Mask[HowMany];

        HowMany+=endbit;

        if(endbyte+4>=storage){
            ret=-1L;
            if(endbyte*8+(long)HowMany>storage*8){
                Attach(NULL, 0);
                goto overflow;
            }
        }

        ret=ptr[0]>>endbit;
        if(HowMany>8){
        ret|=ptr[1]<<(8-endbit);
        if(HowMany>16){
          ret|=ptr[2]<<(16-endbit);
          if(HowMany>24){
        ret|=ptr[3]<<(24-endbit);
        if(HowMany>32 && endbit){
          ret|=ptr[4]<<(32-endbit);
        }
          }
        }
        }
        ret&=m;

        ptr+=HowMany/8;
        endbyte+=(long)HowMany/8;
        endbit=(long)HowMany&7;

        overflow:

        return(ret);
    };

    void Skip(size_t bits)
    {
        Get(bits);
    }

    int32u Remain () //How many bits remain?
    {
        return storage*8-(endbyte*8+endbit);
    };

    void Byte_Align()
    {
        if (endbit)
            Get(endbit);
    };

    size_t Offset_Get()
    {
        return ptr-buffer;
    };

    size_t BitOffset_Get()
    {
        return endbit;
    };

    size_t OffsetBeforeLastCall_Get()
    {
        return ptr_BeforeLastCall-buffer;
    };

private :
    long endbyte;
    int  endbit;

    const unsigned char *buffer;
    const unsigned char *ptr;
    const unsigned char *ptr_BeforeLastCall;
    long storage;
};

} //namespace ZenLib
#endif
