///////////
//
//  huff.cpp
//
//      AUTHOR: Aaron Lenard Hatcher (hatcher@fas.harvard.edu)
//          and Russell James Lowke  (lowke@fas.harvard.edu)
//
//		  T.Fs: Francis Crick & Bryan Kim
//
//		  Date: 8/12/2002
//
//  CSCIE-s111: Assignment Nine, Huffman Encoding
//
//       Usage: huff <input fname> <output fname> 
//
// Description: Uses Huffman encoding to compress a file
//
//     Methods: 
// 				Huff()			 - constructor
// 				clearMemory()	 - cleanup after use
// 				openFile()  	 - open file inFile
// 				frequencyTable() - build frequency table, then tell Forest
//								   utils to create the Huffman key.
// 				compressToFile() - compress the file, saving bits to outStream.
// 				main() 			 - test args passed to main, then process
//
//

#include "huff.hpp"								// include header


//
//  constructor for Huff class
//
Huff :: Huff (char *iFilename, char *oFilename)
{
	inFile  = iFilename;						// set streams
	outFile = oFilename;
	
	for (int i = 0; i < TOTLETTERS; i++) {		// init table with 0
		table[i] = 0;
	}
}


//
//  clearMemory - cleanup after use
//
void Huff :: clearMemory()
{
	inStream.close();							// ensure streams closed
	outStream.close();
	delete forestKey;							// clear forest
}


//
//  openFile - ensure inStream opens cleanly
//
void Huff :: openFile()
{
	// check if file is ASCII or Binary!
	
	// open file		
	inStream.open(inFile);
	
	// ensure file exists
	if ( ! inStream) {							// check for error
	
		cerr << "Can't open file " << inFile << "." << endl;
		inStream.close();
		exit (1);								// end.
	}
}


//
//  frequencyTable - build frequency table, then tell Forest
//  utils to create the Huffman key.
//
void Huff :: frequencyTable()
{
	char c;
	
	inStream.unsetf(ios::skipws);				// unset skip whitespace	
			
	while (! inStream.eof() ) {
	
		inStream >> c;							// read file
		table[ (int) c ]++;						// build table
	}

	forestKey = new Forest( table );			// utils to build the tree
	forestKey->createKey();
	forestKey->encodeKey();

}


//
//  compressToFile
//
//  once frequency table is created, rewind inStream and
//  start compressing the file, saving bits to outStream.
//  note: the freqency table is saved first as a header
//
void Huff :: compressToFile()
{
	char c;										// read char
	code mCode;
	bit_writer bWrite(outStream);				// init bit writer
		
	// ensure file exists
	if ( ! outStream) {							// check for error
	
		cerr << "Can't write file " << outFile << "." << endl;
		outStream.close();
		exit (1);								// end.
	}
	
	// declare out file as binary
	outStream.open(outFile, ios::out | ios::binary );
	
	// save huffpuff tag
	int tag = TAGNAME;
	outStream.write( (const char*) &tag, sizeof(int) );
	
	// save out table header
	outStream.write( (const char*) &table, TOTLETTERS * sizeof(int) );
	
	inStream.seekg(0);							// rewind infile
	inStream.clear();							// clear eof
	
	// read file
	while ( ! inStream.eof() ) {
		inStream.get(c);						// collect next char
		mCode = forestKey->getEncoding(c);		// get code from forest
		bWrite.write_code(mCode);				// write code to out stream
	}
	
	bWrite.flush_bits();
	
	inStream.close();
	outStream.close();
	
	// make outFile read only
	char tempStr[MAXSTR];
	sprintf(tempStr, "chmod -w %s", outFile);
	system(tempStr);
	
}


//
//	main
//
//	test for three args passed to main
//  then process the first file via open, table, and compress
//  finishup with clear memory to resolve.
//
int main(int ac, char **av)
{
    if (ac != 3) {								// Check for the file names
    											// on the command line
		cerr << "Usage: huff <input fname> <output fname>" << endl;
        exit(0);
		
	} else {
		
		Huff reader( av[1], av[2] );			// construct Huff
		reader.openFile();
		reader.frequencyTable();				// builds frequency table
		reader.compressToFile();
		reader.clearMemory();
	}
	
	exit(0);
}
