package cs211.examples;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
/**
 * This is a sample class to show you how to do some simple bit level reading 
 * and writing from and to files.
 * 
 * The basic strategy for both reading and writing is to work with a byte
 * array and using bit-wide operators to access the individual bits in the array.
 * 
 * @author C. Andrews
 *
 */
public class BinaryDataExample {
	private byte[] buffer; // an array of bytes that we will use as our buffer
	private int currentBit; // the address of our current location in the buffer
	
	/**
	 * This function adds a single bit to the buffer.
	 * 
	 * The function takes in a single number, which should be a 1 or 0 and 
	 * adds it to the end of the buffer (the location of currentBit). This 
	 * currently does no error checking to make sure the number is actually a
	 * 1 or a 0.
	 * 
	 * @param b the "bit"
	 */
	private void addBit(int b){
		// figure out which byte in the array we want
		int currentByte = currentBit / 8; 
		
		// use mod and shifting to set the precise bit in that byte
		buffer[currentByte] |= b << (7 - currentBit % 8);
		
		// increment currentBit to point to the next open space in the buffer
		currentBit ++;
	}

	/**
	 * This is a simple example that uses the above function to write data out 
	 * to a file.
	 * 
	 * In this example, I set the buffer to a very small 10 bytes. You will 
	 * probably want something larger. That said, no matter how large you make
	 * it, you will probably need more room. There are two ways to handle this.
	 * You could write an array expansion method and copy to a longer array, or
	 * you could write the buffer to the file, empty it, and start over.
	 * 
	 * @throws IOException
	 */
	private void writeExample() throws IOException {
		File f = new File("writeExample");
		FileOutputStream stream = new FileOutputStream(f);
		
		// create the buffer and initialize the pointer to the end of the buffer
		buffer = new byte[10];
		currentBit = 0;
		
		// write a bunch of bits into the buffer
		addBit(1);
		addBit(0);
		addBit(0);
		addBit(1);
		addBit(1);
		addBit(1);
		addBit(1);
		addBit(1);
		addBit(0);
		addBit(0);
		addBit(0);
		addBit(1);
		addBit(1);
		addBit(1);
		
		// write the buffer into the file
		stream.write(buffer, 0, currentBit/8 + 1);
		// close the file
		stream.close();
	}

	/**
	 * Return the next bit available in the buffer.
	 * 
	 * This is just the inverse of the addBit function. We find the right byte.
	 * Shift the byte to put the bit we are interesting in the rightmost position,
	 * and then AND it with 1 to remove all of the other bits.
	 * 
	 * @return the next bit
	 */
	private int nextBit(){
		int bit = buffer[currentBit/8] >> (7 - currentBit % 8) & 1;
		currentBit++;
		return bit;
	}
	
	
	/**
	 * Example of reading bits from a file.
	 * 
	 * The general approach we will take is to read the entire file into the buffer.
	 * We can then use the inverse of the addBit() function to read each bit out
	 * individually.
	 * @throws IOException 
	 */
	private void readExample() throws IOException {
		
		// we will just open the file we wrote above and read it back
		File f = new File("writeExample");
		FileInputStream stream = new FileInputStream(f);
		
		// ask how many bytes are in the file
		int numBytes = stream.available();
		
		// create a buffer big enough to hold the file
		buffer = new byte[numBytes];
		currentBit = 0;
		
		// read in the file
		stream.read(buffer);
		
		// close the stream
		stream.close();
		
		// print out all of the bits in the file
		for (int i = 0; i < buffer.length * 8; i ++){
			System.out.println(nextBit());
		}
		
		
	}

	
	/**
	 * This runs the two examples in sequence, write and then read (so read has 
	 * something to work with.
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		BinaryDataExample bde = new BinaryDataExample();
		bde.writeExample();
		bde.readExample();
		
	
		
	}

	
}