#include <stdlib.h>
#include <iostream>
#include <string>
#include <ShockGraph.h>
#include "matchingsystem.hpp"
#include <BasicUtils.h>

using namespace std;

const string error_msgs[] = {
	/*0*/"Not enough arguments", 
	/*1*/"Error creating the database", 
	/*2*/"Error occured during reading of the database",
	/*3*/"Parameters are incorrect. Use -h for help", 
	/*4*/"Could not open the command file",
	/*5*/"Error occured during writing of the database",
	/*6*/"Error reading the command file",
	/*7*/"Expect <DTCF_root>",
	/*8*/"Expect <types>",
	/*9*/"Expect </DTCF_root>",
	/*10*/"Expect <type>",
	/*11*/"Expect </type>",
	/*12*/"Expect <name>",
	/*13*/"Expect </name>",
	/*14*/"Expect <regions>",
	/*15*/"Expect <region>",
	/*16*/"Expect </region>",
	/*17*/"Expect <prototype>",
	/*18*/"Expect </prototype>",
	/*19*/"Expect <id>",
	/*20*/"Expect </id>",
	/*21*/"Expect <dag>",
	/*22*/"Expect </dag>",
	/*23*/"Expect <views>",
	/*24*/"Expect </DTCF root>",
	/*25*/"Cannot find type with the given name",
	/*26*/"Cannot open output file",
	/*27*/"Cannot write to output file",
	/*28*/"Cannot find a view with an id",
	/*29*/"Cannot find a dag with an id",
	/*30*/"Cannot find a prototype with a given name",
	/*31*/"Cannot find a prototype with a given id",
	/*32*/"Cannot find a type with a given id",
	/*33*/"The dimension of the database should be between one and fourty",
	/*34*/"Cannot open shockgraph file",
	/*35*/"Expect <view>",
	/*36*/"Expect </view>"
};

istream& read_command(istream& is, string& s)
{
	char p;
	char c[256];
	int i;

	do {
		is.read(&p, sizeof(p)); 
	} while(p != '<' && p != '>');

	i=-1;
	do {
		i++;
		is.read(&c[i], sizeof(char));
	}
	while((i < 255) && (c[i] != '<') && (c[i] != '>'));

	c[i] = '\0';
	s = string(c);
	return is;
}

ostream& PrintHelp(ostream& os)
{
	os << "Usage:	test {-c|-r|-h} [dimensions] [commandfile] [outputfile] [datafile] [file] ..." << endl << endl;
	os << "-c creates new database" << endl;
	os << "-r reads database from config file" << endl;
	os << "-h print out this help" << endl;

	return os;
}

void error_exit(int i)
{
	cerr << "Error " << i << ": " << error_msgs[i] << endl;
	exit(3);
}

int main(int argc, char *argv[])
{
	int i;
	bool q;
	list< pair<string, ID> > view_map;
	map<ID, string> prototype_map;
	int dim;
	string idstr;
	string outfile;
	string cfile;
	string param;
	string names[7];
	ID prid;
	
	names[0] = "database.cnf";
	names[1] = "dags.dat";
	names[2] = "index.dat";
	names[3] = "prototypes.dat";
	names[4] = "regions.dat";
	names[5] = "types.dat";
	names[6] = "views.dat";
	

	DAGMatchingDatabase<ShockGraph> dmd;
	DAGMatcher<ShockGraph> matcher(dmd);


	//Check whether program parameters where indicated correctly
	if(argc < 2) 
	{
		cerr << error_msgs[0] << endl;
		PrintHelp(cout);
		exit(3);
	}

	param = string(argv[1]);

	if(param == "-c" || param== "-r")
	{

		//Check whether program parameters where indicated correctly
		if(argc < 5) 
		{
			cerr << error_msgs[0] << endl;
			PrintHelp(cout);
			exit(3);
		}

		dim = atoi(argv[2]);
		cout << "Dim is " << dim << endl;
		if(dim < 1 || dim > 40) error_exit(33);

		//Read command file name
		cfile = string(argv[3]);

		//Read output file
		outfile = string(argv[4]);
	
		for(i=5; i < argc && i < 12; i++)
		{
			names[i-5] = string(argv[i]);
		}

		if(param == "-c")
		{
			q = dmd.DAGs.Create(names[1]);
//			if(q) cout << "good1" << endl; else cout << "bad1" << endl;
			q = q && dmd.prototypes.Create(names[3]);
//			if(q) cout << "good3" << endl; else cout << "bad3" << endl;
			q = q && dmd.regions.Create(names[4]);
//			if(q) cout << "good4" << endl; else cout << "bad4" << endl;
			q = q && dmd.types.Create(names[5]);
//			if(q) cout << "good5" << endl; else cout << "bad5" << endl;
			q = q && dmd.views.Create(names[6]); 
//			if(q) cout << "good6" << endl; else cout << "bad6" << endl;
			q = q && dmd.index.Create(names[2], dim);
//			if(q) cout << "good2" << endl; else cout << "bad2" << endl;

			if(q)
			{
				cout << "Databases was created successfuly" << endl;
			}
			else
			{
				error_exit(1);
			}
		}
		else
		{
			if(dmd.Read(names[0]) == false)
			{
				error_exit(2);
			}
			else
			{
				cout << "Reading was performed successfuly" << endl;
			}
		}
	}
	else if(param == "-h")
	{
		PrintHelp(cout);
		exit(3);
	}
	else
	{
		cerr << error_msgs[3] << endl;
		PrintHelp(cout);

		exit(3);
	}


	//If we got to here than database has been created successfuly
	//Now we start creating the database based on command file
	fstream f;
	string instrn;
	ID regid;
	ID tid;
	ID dagid = 0;
	ShockGraph dag;

	f.open(cfile.c_str(), ios::in | ios::nocreate);
	if(!f) error_exit(4);

	if(!read_command(f, instrn)) error_exit(6);
	if(instrn != "DTCF_root")	error_exit(7);
	
	if(!read_command(f, instrn)) error_exit(6);
	if(instrn != "types") error_exit(8);

	if(!read_command(f, instrn)) error_exit(6);
	do
	{
		if(instrn != "type") error_exit(10);

			if(!read_command(f, instrn)) error_exit(6);
			if(instrn != "name") error_exit(12);

				if(!read_command(f, instrn)) error_exit(6);
				dmd.types.Add(DAGType(instrn));

			if(!read_command(f, instrn)) error_exit(6);
			if(instrn != "/name") error_exit(13);

		if(!read_command(f, instrn)) error_exit(6);
		if(instrn != "/type") error_exit(11);

		if(!read_command(f, instrn)) error_exit(6);
	}
	while(instrn != "/types");

	if(!read_command(f, instrn)) error_exit(6);
	if(instrn != "regions") error_exit(14);

	if(!read_command(f, instrn)) error_exit(6);
	do
	{
		if(instrn != "region") error_exit(15);

		regid = dmd.regions.Add(DAGRegion());

			if(!read_command(f, instrn)) error_exit(6);
			if(instrn != "prototype") error_exit(17);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "id") error_exit(19);
				
					if(!read_command(f, idstr)) error_exit(6);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "/id") error_exit(20);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "type") error_exit(10);
				
					if(!read_command(f, instrn)) error_exit(6);
					tid = dmd.types.Find(instrn);
					if(tid == NULLID) error_exit(25);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "/type") error_exit(11);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "dag") error_exit(21);
				
					if(!read_command(f, instrn)) error_exit(6);
					if(!dag.ReadFromXMLFile(instrn.c_str())) error_exit(34);
					dagid = dmd.DAGs.Add(dag);
				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "/dag") error_exit(22);
			
				prid = dmd.prototypes.Add(DAGPrototype(DAGView(tid, regid, dagid)));
				dmd.index.Add(prid, dag);
				prototype_map.insert(pair<ID, string>(prid, idstr));

			if(!read_command(f, instrn)) error_exit(6);
			if(instrn != "/prototype") error_exit(18);

			if(!read_command(f, instrn)) error_exit(6);
			if(instrn != "views") error_exit(23);

			if(!read_command(f, instrn)) error_exit(6);
			do
			{
				if(instrn != "view") error_exit(35);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "id") error_exit(19);
				
					if(!read_command(f,idstr)) error_exit(6);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "/id") error_exit(20);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "type") error_exit(10);
				
					if(!read_command(f, instrn)) error_exit(6);
					tid = dmd.types.Find(instrn);
					if(tid == NULLID) error_exit(25);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "/type") error_exit(11);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "dag") error_exit(21);
				
					if(!read_command(f, instrn)) error_exit(6);
					if(!dag.ReadFromXMLFile(instrn.c_str())) error_exit(34);
					dagid = dmd.DAGs.Add(dag);

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "/dag") error_exit(22);

				view_map.push_back(pair<string, ID>(idstr, dmd.views.Add(DAGView(tid, regid, dagid))));

				if(!read_command(f, instrn)) error_exit(6);
				if(instrn != "/view") error_exit(36);

				if(!read_command(f, instrn)) error_exit(6);
			}
			while(instrn != "/views");

		if(!read_command(f, instrn)) error_exit(6);
		if(instrn != "/region") error_exit(16);

		if(!read_command(f, instrn)) error_exit(6);
	}
	while(instrn != "/regions");


	if(!read_command(f, instrn)) error_exit(6);
	if(instrn != "/DTCF_root")	error_exit(9);

	f.close();

	//By now we finished creating the database from command file
	//Here is where we match each view and record the results
	typename list< pair<string, ID> >::iterator p;
	vector< MatchData<ShockGraph> > results;
	int k = 2;
	pair<DAGView, bool> temp_a;
	pair<ShockGraph, bool> temp_b;
	pair<DAGType, bool> temp_c;
	pair<DAGPrototype, bool> temp_d;
	typename map<ID, string>::iterator mi;

	f.open(outfile.c_str(), ios::out);
	if(!f) error_exit(26);

	if(!(f << "<results>" << endl)) error_exit(27);

	for(p = view_map.begin(); p!=view_map.end(); p++)
	{

		if(!(f << "<view>" << endl)) error_exit(27);
		if(!(f << "<name>")) error_exit(27);
		if(!(f << p->first)) error_exit(27);
		if(!(f << "</name>" << endl)) error_exit(27);

		temp_a = dmd.views[p->second];
		if(!temp_a.second) error_exit(28);

		temp_b = dmd.DAGs[temp_a.first.graph];
		if(!temp_b.second) error_exit(29);
		
		matcher.GetSimilar(temp_b.first, results, k);

		if(!(f << "<index matches>" << endl)) error_exit(27);

		for(i = 0; i<results.size(); i++)
		{
			if(!(f << "<prototype>" << endl)) error_exit(27);
			
				if(!(f << "<name>")) error_exit(27);
					mi = prototype_map.find(results[i].prototype);
					if(mi == prototype_map.end()) error_exit(30);
					if(!(f << mi->second)) error_exit(27);
				if(!(f << "</name>" << endl)) error_exit(27);

				temp_d = dmd.prototypes[results[i].prototype];
				if(!temp_d.second) error_exit(31);

				temp_c = dmd.types[temp_d.first.view.type];
				if(!temp_c.second) error_exit(32);

				if(!(f << "<type>")) error_exit(27);
					if(!(f << temp_c.first.name)) error_exit(27);
				if(!(f << "</type>" << endl)) error_exit(27);

				if(!(f << "<distance>")) error_exit(27);
				if(!(f << results[i].distance)) error_exit(27);
				if(!(f << "</distance>" << endl)) error_exit(27);
			
			if(!(f << "</prototype>" << endl)) error_exit(27);
		}

		if(!(f << "</index matches>" << endl)) error_exit(27);

		matcher.SortByClosest(temp_b.first, results);

		if(!(f << "<final matches>" << endl)) error_exit(27);

		for(i = 0; i<results.size(); i++)
		{
			if(!(f << "<prototype>" << endl)) error_exit(27);
			
				if(!(f << "<name>")) error_exit(27);
					mi = prototype_map.find(results[i].prototype);
					if(mi == prototype_map.end()) error_exit(30);
					if(!(f << mi->second)) error_exit(27);
				if(!(f << "</name>" << endl)) error_exit(27);

				temp_d = dmd.prototypes[results[i].prototype];
				if(!temp_d.second) error_exit(31);

				temp_c = dmd.types[temp_d.first.view.type];
				if(!temp_c.second) error_exit(32);

				if(!(f << "<type>")) error_exit(27);
					if(!(f << temp_c.first.name)) error_exit(27);
				if(!(f << "</type>" << endl)) error_exit(27);

				if(!(f << "<distance>")) error_exit(27);
				if(!(f << results[i].distance)) error_exit(27);
				if(!(f << "</distance>" << endl)) error_exit(27);
			
			if(!(f << "</prototype>" << endl)) error_exit(27);
		}

		if(!(f << "</final matches>" << endl)) error_exit(27);
	}

	if(!(f << "</results>" << endl)) error_exit(27);

	//Here we save the database
	if(dmd.Write(names[0]) == false)
		error_exit(5);
	else
		cout << "Operation proceeded succesfuly" << endl;

	return 0;
}
