/**
 * This class was not made by me.  It can be found at:
 * http://www.cs.wisc.edu/~smethegj/imageprocessor/
 */

import java.io.*;
import java.lang.Exception;

/**
 *
 */
public class BitOutputStream extends ByteArrayOutputStream {
  private FileOutputStream myFileOS;
  private int bitsWritten;
  private int bitPosition;
  private byte[] myByte;
  private final static int NUM_BITS_IN_BYTE = 8;
  private final static int END_POSITION = -1;


  /**
   * Default constructor.
   */
  public BitOutputStream() {
	this(null);
  }


  /**
   * Constuct based upon a FileOutputSteam to write to
   */
  public BitOutputStream( FileOutputStream fos ) {
	super();

	myFileOS    = fos;
	bitsWritten = 0;
	myByte      = new byte[1];

	resetByte();
  }


  /**
   * Reset the byte and position marker.
   */
  private void resetByte() {
	bitPosition = NUM_BITS_IN_BYTE-1;
	myByte[0]   = (byte)0x00;
  }


  /**
   * Write to my super's buffer.
   */
  private void flushByte() {
	write( myByte, 0, 1 );
	bitsWritten = bitsWritten + 8;
	resetByte();
  }


  /**
   * Write a bit to the stream.
   * If bit == "0", writes a '0' bit.
   * If bit == "1", writes a '1' bit.
   */
  public void writeBit( int bit ) {
	if( bit != 0 && bit != 1 ) {
	  System.err.println( "<BitOutputStream>: only 1's and 0's can be written" );
	}

	if( bitPosition == END_POSITION ) {
	  flushByte();
	}

	if( bit == 0 ) {
	  bitPosition--;
	}
	else if( bit == 1 ) {
	  myByte[0] = (byte)(myByte[0] | (0x01 << bitPosition));
	  bitPosition--;
	}
  }


  /**
   * Writes to the file if a FileOutputStream is set.
   */
  public void flushToFile() {
	if( myFileOS == null ) {
	  System.err.println( "No FileOutputStream set" );
	}
	else {
	  try {
		// Flush any excess to my super's buffer
		if( bitPosition >= END_POSITION && bitPosition != NUM_BITS_IN_BYTE-1 ) {
		  flushByte();
		}

		// Have my super write to the File
		writeTo( (OutputStream)myFileOS );
	  }
	  catch( Exception e ) {
		System.err.println("<BitOutputStream>: error flushing to file");
		e.printStackTrace();
	  }
	}
  }


  /**
   * Close the stream.
   */
  public void closeStream() throws Exception {
	try {
	  super.close();
	}
	catch( Exception e ) {
	   System.err.println("<BitOutputStream>: error while closing");
	   e.printStackTrace();
	   throw( new Exception( "<BitOutputStream>: error closing stream" ));
	}
  }
}
