package coding;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;

/**
 * Read in a code file and use it to encode a text file
 */
public class Encoder {

	/*
	 * Array that maps each 8 bit character to its code. 
	 * To access a particular code for a character index the code array with that character
	 * e.g. code['a'] returns a string with the code for the character a
	 * There is a code for all 256 possible bytes.   
	 */
	private String[] code;

	/**
	 * The main function reads command line arguments, creates an encoder, sets up the code array and calls encode. 
	 * @param args Takes three parameters, <text file> <code file> <output file>
	 */
	public static void main(String[] args) throws IOException {

		if (args.length != 3) {
			System.out
					.println("Expecting 3 arguments <text file> <code file> <output file> but got "
							+ args.length);
			System.exit(1);
		}

		String textFile = args[0];
		String codeFile = args[1];
		String outputFile = args[2];

		Encoder he = new Encoder(codeFile);
		he.encode(textFile, outputFile);
	}

	/**
	 * Encodes textFile using the given code and produces the encoded outputFile 
	 * @param textFile file to encode
	 * @param outputFile encoded file location
	 * @throws IOException
	 */
	private void encode(String textFile, String outputFile) throws IOException {
		FileInputStream fr = new FileInputStream(textFile);
		DataInputStream br = new DataInputStream(fr);

		/*
		 * Initialize output stream here
		 */
		//Your code here
		FileOutputStream out = new FileOutputStream(outputFile);

		int nextByte, countBytes = 0, count = 0;
		byte nextWrite = 0;

		try {
			while (true) {
				//Read next byte from the file or throw an EOFException if end of file.
				nextByte = br.readUnsignedByte();
				//Keep count of how many bytes have been encoded (used later for decoding)
				countBytes++;
				
				/*
				 * Encode nextByte and if necessary write one or more bytes to outputFile
				 */
				//Your code here
				String encodedChar = code[nextByte];
				
				for (char nextChar : encodedChar.toCharArray()) {
					if (nextChar == '1')
						nextWrite |= 1;
					else
						nextWrite |= 0;
					count++;
				if (count == 8) {
						out.write(nextWrite);
						out.flush();
						nextWrite = 0;
						count = 0;
					} else
					nextWrite <<= 1;
				}
				
			}
		} 
		catch (EOFException e) { //Catch the end of file exception
		}
		System.out.println("Total number of bytes: " + countBytes);
		
		/*
		 * Don't forget to close the output file stream 
		 */
		//Your code here
		for (int j = 8-count+1; j == 8; j++){
			nextWrite <<= 1;
		}
		out.write(nextWrite);
		out.close();
	}

	/**
	 * Initialize an encoder using codeFile as the code
	 * @param codeFile the code to use
	 * @throws IOException
	 */
	public Encoder(String codeFile) throws IOException {
		code = new String[256];
		loadCode(codeFile);
	}

	/**
	 * Load the code from codeFile into the code array
	 * @param codeFile the code to use
	 * @throws IOException
	 */
	private void loadCode(String codeFile) throws IOException {
		FileReader fr = new FileReader(codeFile);
		BufferedReader br = new BufferedReader(fr);

		String nextLine;
		while ((nextLine = br.readLine()) != null) {
			String[] line = nextLine.split(" ");
			code[new Integer(line[0])] = line[1];
		}

	}
}

