package com.colloquial.io;

import java.io.InputStream;
import java.io.IOException;

/** Reads input from an underlying input stream a bit at a time.  Bits
 * are returned as booleans, with <code>true=1</code> and
 * <code>false=0</code>.
 *
 * @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a>
 * @version 1.2
 * @since 1.0
 */
public final class BitInput {

    /** Constructs a bit input from an underlying input stream.
     * @param in Input stream backing this bit input.
     * @throws IOException If there is an exception reading from the specified input stream.
     */
    public BitInput(InputStream in) throws IOException {
        _in = in;
        readAhead();
    }

    /** Returns number of bits available for reading.  Will always be
     * <code>0</code> or <code>1</code>.
     * @return Number of bits available for reading.
     * @throws IOException If there is an exception checking available bytes in the underlying input stream.
     */
    public long available() throws IOException {
        return endOfStream() ? 0 : 1;
    }

    /** Closes the underlying input stream.
     * @throws IOException If there is an exception closing the underlying input stream.
     */
    public void close() throws IOException { 
        _in.close(); 
    }

    /** Returns <code>true</code> if all of the available bits have been read.
     * @return <code>true</code> if all of the available bits have been read.
     */
    public boolean endOfStream() { return _endOfStream; }

    /** Reads the next bit from the input stream.  Returns garbage if reading
     * while available() is false.
     * @return The boolean value of the next bit, <code>true</code>=1, <code>false</code>=0.
     * @throws IOException If there is an exception reading a byte from the underlying stream.
     */
    public boolean readBit() throws IOException {
        if (_nextBitIndex > 0) 
            return ((_nextByte & (1 << _nextBitIndex--)) != 0); // inspects bit in buffered byte
        boolean result = ((_nextByte & 1) != 0); // on last bit in byte; buffer new byte
        readAhead();
        return result;
    }

    /** Underlying input stream.
     */
    private final InputStream _in;
    
    /** Buffered byte from which bits are read.
     */
    private int _nextByte; // implied = 0;
    
    /** Position of next bit in the buffered byte.
     */
    private int _nextBitIndex;

    /** Set to true when all bits have been read.
     */
    private boolean _endOfStream = false;

    /** Reads the next byte from the input stream into <code>_nextByte</code>.
     * @throws IOException If there is an IOException reading from the stream.
     */
    private void readAhead() throws IOException {
        if (_endOfStream) return;
        _nextByte = _in.read(); 
        if (_nextByte == -1) { _endOfStream = true; return; }
        _nextBitIndex = 7;
    }

}
