package ij.io;

/**
 * A class for reading arbitrary numbers of bits from a byte array.
 * @author Eric Kjellman egkjellman at wisc.edu
 */
public class BitBuffer {

    private int currentByte;
    private int currentBit;
    private byte[] byteBuffer;
    private int eofByte;
    private int[] backMask;
    private int[] frontMask;
    private boolean eofFlag;

    public BitBuffer(byte[] byteBuffer) {
        this.byteBuffer = byteBuffer;
        currentByte = 0;
        currentBit = 0;
        eofByte = byteBuffer.length;
        backMask = new int[] {0x0000, 0x0001, 0x0003, 0x0007,
            0x000F, 0x001F, 0x003F, 0x007F};
        frontMask = new int[] {0x0000, 0x0080, 0x00C0, 0x00E0,
            0x00F0, 0x00F8, 0x00FC, 0x00FE};
    }

    public int getBits(int bitsToRead) {
        if (bitsToRead == 0)
            return 0;
        if (eofFlag)
            return -1; // Already at end of file
        int toStore = 0;
        while(bitsToRead != 0  && !eofFlag) {
            if (bitsToRead >= 8 - currentBit) {
                if (currentBit == 0) { // special
                    toStore = toStore << 8;
                    int cb = ((int) byteBuffer[currentByte]);
                    toStore += (cb<0 ? (int) 256 + cb : (int) cb);
                    bitsToRead -= 8;
                    currentByte++;
                } else {
                    toStore = toStore << (8 - currentBit);
                    toStore += ((int) byteBuffer[currentByte]) & backMask[8 - currentBit];
                    bitsToRead -= (8 - currentBit);
                    currentBit = 0;
                    currentByte++;
                }
            } else {
                toStore = toStore << bitsToRead;
                int cb = ((int) byteBuffer[currentByte]);
                cb = (cb<0 ? (int) 256 + cb : (int) cb);
                toStore += ((cb) & (0x00FF - frontMask[currentBit])) >> (8 - (currentBit + bitsToRead));
                currentBit += bitsToRead;
                bitsToRead = 0;
            }
            if (currentByte == eofByte) {
                eofFlag = true;
                return toStore;
            }
        }
        return toStore;
    }

}