///////////
//
//  puff.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: Puff <input fname> <output fname> 
//
// Description: Decompresses huff files back into ascii
//
//     Methods: 
//
// 				    Puff() - constructor
// 			 clearMemory() - clear memory
// 				openFile() - open file inFile
// 		  frequencyTable() - read frequency table in from file,
//							 tell utils to create key
// 		decompressToFile() - Read the file bit by bit,
//							 comparing to Huffman tree
// 					main() - decompress using open, table, and decompress 
//
//

#include "puff.hpp"								// include header


//
//  constructor
//
Puff :: Puff (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 - ensure puff resolves cleanly
//  
void Puff :: clearMemory()
{
	inStream.close();							// ensure streams closed
	outStream.close();
	delete forestKey;							// clear forest
}


//
//  openFile - ensure inStream opens cleanly
//
void Puff :: openFile()
{
	// open file		
	inStream.open(inFile, ios::binary | ios::in );
	
	// ensure file exists
	if ( ! inStream) {							// check for error
	
		cerr << "Can't open file " << inFile << "." << endl;
		inStream.close();
		exit (1);								// end.
	}
	
	// look for huffpuff tag at beginning of file
	int tag = 0;
	inStream.read( (char*) &tag, sizeof(int) );
	
	if ( tag != TAGNAME ) {						// check for tag
		cerr << inFile << " is not a huff encoded file." << endl;
		inStream.close();
		exit (1);								// end.
	}
}


//
//  frequencyTable - read frequency table in from file,
//  then tells utils to create key tree
//
void Puff :: frequencyTable()
{
	// read table in from file
	inStream.read( (char*) &table, TOTLETTERS * sizeof(int) );
	
	forestKey = new Forest( table );			// tell utils to build tree
	forestKey->createKey();
}


//
//  decompressToFile
//
//  open the outstream, open a bit_reader, and get the Huffman (key) tree.
//	Read the file bit by bit, comparing to key tree and
//  outputting leaves when reached.
//
void Puff :: decompressToFile()
{
	outStream.open(outFile);					// open out stream
	if ( ! outStream) {							// check for error
	
		cerr << "Can't write file " << outFile << "." << endl;
		outStream.close();
		exit (1);								// end.
	}
	
	bit_reader	bRead(inStream);				// init reader
	int			aBit;							// bit holder
	
	Tree*		kTree = forestKey->getTree();	// keep roof (k) tree
	Tree*		ptr   = kTree;					// ptr to ktree top
		
	int totFreq = kTree->getFrequency();		// find total frequency
	int counter = 1;

	// limit reads = totFreq, so not to read spare bits at
	// end of compressed file.
	while ( bRead.get_bit( aBit ) && counter < totFreq ) {
		
		if (ptr->getLetter() != -1 ) {			// ptr at a leaf node?	
			outStream << ptr->getLetter();		// output char
			counter++;							// inc counter
			ptr = kTree;						// reset ptr 
		} else if ( aBit == 0 ) {
			ptr = ptr->getLeft();				// traverse left
		} else {
			ptr = ptr->getRight();				// traverse right
		}
	}
	
	outStream.close();
}


//
//	main
//
//	test for three args passed to main
//  open and decompress first file via open, table, and decompress
//  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: puff <input fname> <output fname>" << endl;
        exit(0);
		
	} else {
	
		Puff expander( av[1], av[2] );			// construct Puff
		expander.openFile();
		expander.frequencyTable();				// reads frequency table
		expander.decompressToFile();			// save out
		expander.clearMemory();					// resolve
	}
	
	exit(0);
}






