//---------------------------
// Class BitStreamInputStream
// --------------------------
// Implements an enhanced InputStream which allows you to read a
// stream of bit fields ranging in size from 1 bit (a true bit
// stream) to 32 bits (a stream of integers). The size of the current
// bitfield can be changed at any point while reading the stream.
// (c) Laurence Vanhelsuwe 1996. E-Mail: LVA@telework.demon.co.uk
//------------------------------------------------------------------

package jlittle_ex9;

import java.io.*;

public class BitStreamInputStream extends FilterInputStream {
    final static int EIGHT = 8; // 8 bits per byte
    protected short buffer; // our BYTE bitstream read-ahead
    // buffer declared as short to cope with EOF
    protected int bitsInCache;  // how many unread bits left in our byte
    protected int fieldSize;    // current size of bitstream read fields

    //------------------------------------------------------------------
    public BitStreamInputStream(InputStream in) {

	this(in, EIGHT);       // default to a normal byte stream
    }
    //------------------------------------------------------------------
    public BitStreamInputStream(InputStream in, int bitFieldSize) {
	super(in);
	setBitFieldSize(bitFieldSize);
	bitsInCache = 0;        // we haven't got any cached bits
    }
    //------------------------------------------------------------------
    // Set the current bitfield size.
    //------------------------------------------------------------------
    public void setBitFieldSize(int bits) throws IllegalArgumentException {
	if (bits>32 || bits<1) throw new IllegalArgumentException
	    (
	     "BitField size ("+ bits + ") no good. Has to be between 1 and 32."
	     );
	this.fieldSize = bits;
    }
    //------------------------------------------------------------------
    public int getBitFieldSize() {
	return this.fieldSize;
    }
    //------------------------------------------------------------------
    // Read a bitfield from the input stream. The number of bits read is
    // the current bitfield length. Bitfield can be on arbitrary bit boundaries.
    //------------------------------------------------------------------
    public long readBitField() throws IOException {

	int bitField;               // what we're going to return to caller
	int bitsToRead;             // remaining bits to assemble into BF
	int availableNumberOfBits;
	int OR_position;
	int rightAlignedBFPartial;
	bitField    = 0;        // start with empty jigsaw
	bitsToRead  = fieldSize;
	OR_position = fieldSize;

	while (bitsToRead > 0) {
	    if (bitsInCache == 0) {
		if ( (buffer = (short) in.read()) == -1) {
		    return -1;          // reached EOF
		}
		bitsInCache = EIGHT;    // we've got a full byte again
	    }
	    availableNumberOfBits = Math.min( bitsToRead, bitsInCache );
	    rightAlignedBFPartial = buffer >> (EIGHT - availableNumberOfBits);
	    // always keep next partial left aligned and clean
	    buffer <<= availableNumberOfBits;
	    buffer &= 255;
	    OR_position -= availableNumberOfBits;
	    // add bitfield subfield
	    bitField |= rightAlignedBFPartial << OR_position;
	    // track # of cached bits
	    // track how much left to do

	    bitsInCache -= availableNumberOfBits;
	    bitsToRead  -= availableNumberOfBits;
	}
	return bitField;
    }
    //---------------------------------------------
    // The remaining methods are methods we override
    // from our parent class: FilterInputStream
    //---------------------------------------------
    //------------------------------------------------------------------
    // Overridden read() still reads a byte, but on any bit boundary.
    //------------------------------------------------------------------
    public int read() throws IOException {
	int previousBFSize;
	int theByte;
	previousBFSize = getBitFieldSize();
	setBitFieldSize( EIGHT );
	try {
	    theByte = (int) readBitField();
	}
	finally {
	    setBitFieldSize( previousBFSize );
	}
	return theByte;
    }
    //------------------------------------------------------------------
    // Override block read() methods to use basic read() as building block.
    // The implementation we want for this read() is the same as that
    // for class InputStream.
    // According to the Java language spec, two elegant (and short
    // solution should work:
    //   ((InputStream) this).read(..)      // i.e. a cast
    //   InputStream.read(..)               // i.e. fully specifiying
    // Unfortunately neither work, so I am forced to paste in the
    // original code for InputStream.read(byte b[], int off, int len).
    //------------------------------------------------------------------

    public int read(byte b[], int off, int len) throws IOException {

	if (len <= 0) {
	    return 0;
	}
	int c = read();
	if (c == -1) {
	    return -1;
	}
	b[off] = (byte)c;
                 
	int i = 1;
	try {
	    for (; i < len ; i++) {
		c = read();
		if (c == -1) {
		    break;
		}
		if (b != null) {
		    b[off + i] = (byte)c;
		}
	    }
	} catch (IOException ee) {}
	return i;
    }
    //------------------------------------------------------------------
    // Overridden FilterInputStream.read(byte b[])
    //------------------------------------------------------------------
    public int read(byte b[]) throws IOException {
	return read(b, 0, b.length);
    }
    //------------------------------------------------------------------
    // Overridden FilterInputStream.skip(long n)
    // If any client relies heavily on skipping multi-byte strings in
    // the bitstream, then this method has to be re-implemented to be more
    // efficient. Current implementation is functional but highly inefficient.
    //------------------------------------------------------------------
    public long skip(long n) throws IOException {
	long i;

	for(i=0; i < n; i++) {
	    if (read() == -1) break;
	}
	return i;
    }
} // End of Class BitStreamInputStream
