#include "Huffman.h"
#include "Node.h"
#include "Leaf.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <string>
#include <bitset>
using namespace std;

typedef Node<char> CharNode;

Huffman::Huffman(string Filename)
{
	cout << "Starting" << endl;
	priority_queue<CharNode, vector<CharNode>, greater<CharNode> > pq;
	int *totalChars = new int[256];
	byteCode = "";

	memset(totalChars, 0, sizeof(int)*256);

	// Read file here
	char input;
	ifstream in_stream(Filename.c_str(),ios::binary); 

	while ( in_stream.get(input) )
	{
		totalChars[input] += 1;
	}

	in_stream.close();

	char *InputString = new char[10];

	for ( int i = 0; i < 257; i++)
	{
		if ( totalChars[i] > 0 )
		{
			char c = i;
			CharNode currentNode = CharNode(c, totalChars[i]);
			
			// cout << "Inserting: " << currentNode.data << endl;

			pq.push(currentNode);
		}
	}

	cout << endl << endl << endl;
	
	for( int i = 0; pq.size() != 1; i ++ )
	{
		CharNode *left = new CharNode(pq.top());
		pq.pop();

		CharNode *right = new CharNode(pq.top());
		pq.pop();
	
		/* -- Debug
		cout 
			<< "Left Frequency: \t" << left->Frequency 
			<< "\t\tValue: " << left->data 
			<< "\nRight Frequency: \t" << right->Frequency  
			<< "\t\tValue: " << right->data 
			<< endl << endl;
		
		cout << "New Frequency: \t\t" << newNode.Frequency << endl << endl;
		*/
		CharNode newNode = CharNode();
		newNode.Merge(left, right);

		

		cout << "\rCreating tree: " << i;

		pq.push(newNode);
	}


	vector<Leaf*> *leafs = new vector<Leaf*>();

	CharNode *tree = new CharNode(pq.top());
	tree->Traverse("", leafs);
	
	/* --- Debug

	for ( int i = 0; i < leafs->size(); i ++ )
	{
		cout << "Code: " << leafs->at(i)->byteCode << "\nData: " << leafs->at(i)->Data << endl << endl;
	}

	*/

	cout << endl << endl << endl;

	Encode(leafs, Filename);
	
}

void Huffman::Encode(vector<Leaf*> *leafs, string filename)
{
	cout << "Encoding " << filename << endl;
	char c;
	ifstream in_stream(filename.c_str(),ios::binary); 

	filename += ".frrw";
	ofstream fileWriter (filename.c_str(), ios::app);

	bitset<8> bits;

	string fullBitSequence = "";

	// Reading all characters in the file, cross-examing it to the bytecodes and add the bytecode to the bit-sequence
	cout << "Start coding bytestream" << endl;
	while ( in_stream.get(c) )
	{		
		for( int a = 0 ; a < leafs->size(); a ++ )
		{
			if ( c == leafs->at(a)->Data )
			{
				fullBitSequence += leafs->at(a)->byteCode;
				break;
			}
		}
	}


	// Run the following loop as many times as the lenght is divided by 8
	int loopCount = (int)(fullBitSequence.length() / 8) ;

	for ( int i = 0 ; i < loopCount; i ++ )
	{		
		for(int a = 0; a < 8; a ++ )
		{
			bits.set(a, fullBitSequence.at(a) == '1');
		}

		fileWriter << (char)bits.to_ulong();

		fullBitSequence = fullBitSequence.substr(7, fullBitSequence.length());
	}

	cout << endl;

	// In case lenght % 8 returns anything else than 0,
	// fill the remaining bytes with 0.
	int charsLeft = fullBitSequence.length() % 8;
	if ( charsLeft > 0 )
	{
		int addChars = 8 - charsLeft;

		for ( int b = 0 ; b < addChars; b ++ )
		{
			fullBitSequence += "0";
		}
	
		cout << endl;
		for(int a = 0; a < 8; a ++ )
		{
			bits.set(a, fullBitSequence.at(a) == '1');
		}
		fileWriter << (char)bits.to_ulong();
	}
	in_stream.close();

 	fileWriter.close();
	
	cout << "Finished" << endl;
}

Huffman::~Huffman(void)
{
}

