#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "BHeap.h"
#include "FHeap.h"
#include "BinHeap.h"
#include "PLList.h"
#include "Huffman.h"
#include "numbers.h"

void exception(int e, char *msg)
{
	cerr << msg << endl;
	exit(-1);
}

PriorityList *newlist(int method)
{
	switch (method) {
	case 0:
		return new BinHeap;
		break;
	case 1:
		return new BHeap;
		break;
	case 2:
		return new FHeap;
		break;
	case 3:
		return new PLList;
		break;
	default:
		cerr << _("Unknow priority list type: ") << method << endl;
		exit(-1);
	}
}

void Count(FILE * strm, PriorityList * P)
{
	int tab[256], i;
	char t[2];

	if (!strm) {
		cerr << _("Error opening file (") << strerror(errno) << ")\n";
		exit(-1);
	}

	for (i = 0; i < 256; i++) {
		tab[i] = 0;
	}

	while ((i = getc(strm)) != EOF) {
		tab[i]++;
	}

	for (i = 0; i < 256; i++) {
		if (tab[i]) {
			t[0] = i;
			t[1] = 0;
			HInsert(P, tab[i], t);
		}
	}
}

void ReadDic(FILE * strm, PriorityList * P)
{
	char t[1024], *f, *word, *p;
	int valid, freq;

	if (!strm) {
		cerr << _("Error opening file (") << strerror(errno) << ")\n";
		exit(-1);
	}

	while (fgets(t, 1024, strm)) {
		if (!(f = strchr(t, ':'))) {
			cerr << _("Bad dictionnary structure. See doc/README.en (missing : separator)") << endl;
			exit(-1);
		}
		*(f++) = '\0';
		word = t;
		while (*word == ' ') {
			word++;
		}
		p = word + strlen(word) - 1;
		while (*p == ' ') {
			*(p--) = '\0';
		}
		if (!strlen(word)) {
			cerr << _("Bad dictionnary structure. See doc/README.en (missing word)") << endl;
			exit(-1);
		}
		while (*f == ' ') {
			f++;
		}
		p = f + strlen(f) - 1;
		while ((*p == ' ') || (*p == '\n')) {
			*(p--) = '\0';
		}
		if (!strlen(f)) {
			cerr << _("Bad dictionnary structure. See doc/README.en (missing frequency)") << endl;
			exit(-1);
		}
		freq = char_to_number(f, &valid);
		if (!valid) {
			cerr << _("Error: \"") << f << _("\" is not a valid number.") << endl;
			exit(-1);
		}
		HInsert(P, freq, word);
	}
}

void Usage(void)
{
	cerr << _("Huffman [{-f|-i} file] {type}") << endl;
	cerr << _("Huffman -h") << endl;
	cerr << _("By Nicolas Noble (nicolas@nobis-crew.org).") << endl;
	cerr << _("This will encode the input file with the Huffman code") << endl;
	cerr << _("using the priority list defined by type.") << endl;
	cerr << _("Type is a number taken from this list:") << endl;
	cerr << _("  0 : Binary Heap (default)") << endl;
	cerr << _("  1 : Binomial Heap") << endl;
	cerr << _("  2 : Fibonacci Heap") << endl;
	cerr << _("  3 : Sorted chained list") << endl;
	cerr << _("-f file means that you specify a dictionnary file which is") << endl;
	cerr << _("        structured as described into the README file.") << endl;
	cerr << _("-i file means that you specify a file to encode. It will") << endl;
	cerr << _("        built a quiet dumb dictionnary.") << endl;
	cerr << _("By default, a dictionnary will be built from stdin.") << endl;
	cerr << _("-h prints this help and exit.") << endl;
	exit(0);
}

int main(int argc, char **argv)
{
	PriorityList *P;
	HTree *H;
	int method = -1, readm = -1;
	char *filename = NULL;

	while (--argc) {
		argv++;
		if ((*argv)[0] == '-') {
			if (strlen(*argv) != 2) {
				cerr << _("Unknow option: ") << *argv << endl;
				Usage();
			}
			switch ((*argv)[1]) {
			case 'h':
				Usage();
				break;
			case 'i':
				if (readm != -1) {
					cerr << _("-i and -f options are exclusive") << endl;
					Usage();
				}
				readm = 1;
				filename = *(++argv);
				argc--;
				break;
			case 'f':
				if (readm != -1) {
					cerr << _("-i and -f options are exclusive") << endl;
					Usage();
				}
				readm = 2;
				filename = *(++argv);
				argc--;
				break;
			}
		} else {
			if ((strlen(*argv) != 1) || (((*argv)[0] < '0' || (*argv)[0] > '3'))) {
				cerr << _("Unknow priority list type: ") << *argv << endl;
				Usage();
			}
			if (method != -1) {
				cerr << _("Extra command: ") << *argv << endl;
				Usage();
			}
			method = (*argv)[0] - '0';
		}
	}

	if (method == -1)
		method = 0;
	P = newlist(method);

	switch (readm) {
	case -1:
		Count(stdin, P);
		break;
	case 1:
		if (!filename)
			cerr << _("-i needs a filename") << endl;
		Count(fopen(filename, "r"), P);
		break;
	case 2:
		if (!filename)
			cerr << _("-f needs a filename") << endl;
		ReadDic(fopen(filename, "r"), P);
		break;
	default:
		cerr << _("Internal error.") << endl;
		exit(-1);
	}

	H = Coder(P);
	delete P;

	H->Trace(cout);
	return 0;
}