#include <modules\import\pla_import.h>
#include <modules\export\vhdl_export.h>
#include <modules\export\blif_export.h>
#include "Decomposer.h"

int printHelp()
{
	printf("\nDECOMPOSE by Jakub Zahradnik, CTU FEE (2007)\n");
	printf("Valid options:\n");
	printf("\t-i fileName\treq\t... input file in PLA format\n");
	printf("\t-b\t\topt\t... output in 2-input gates\n");
	printf("\t-d\t\topt\t... generate debug information (-s required)\n");
	printf("\t-g\t\topt\t... compute classic kernel gain\n");
	printf("\t-h\t\topt\t... display help\n");
	printf("\t-n\t\topt\t... output in NAND gates\n");
	printf("\t-o %%d\t\topt\t... kernel count optimalization (-1 disabled)\n");
	printf("\t-s\t\topt\t... generate statistic file\n");
	printf("--------------------------------------------------\n");
	printf("req\t... required option\n");
	printf("opt\t... optional option\n");
	printf("--------------------------------------------------\n");
	printf("EXAMPLE: decompose -i adder.pla -b -n -s -o 100\n");
	return -1;
}

int main(int argc,char* argv[])
{
	//Inicializace - defaultni nastaveni
	hub::Hub theHub;
	bool convertTo2Input=false;
	bool convertToNAND=false;
	std::string statsFileName;
	bool printDebugInfo=false;
	bool multiGain=true;
	int optimalizationKS=100;
	std::string fileName;


	//Nastaveni dle prepinacu
	if (argc<2)
		return printHelp();
	else for (int arg=1;arg<argc;arg++)
	{
		if (!std::string("-b").compare(argv[arg]))
			convertTo2Input=true;
		else if (!std::string("-n").compare(argv[arg]))
			convertToNAND=true;
		else if (!std::string("-h").compare(argv[arg]))
			return printHelp();
		else if (!std::string("-i").compare(argv[arg]))
		{
			if (++arg>=argc)
				return printHelp();
			fileName=argv[arg];
		}
		else if (!std::string("-s").compare(argv[arg]))
			statsFileName=".txt";
		else if (!std::string("-d").compare(argv[arg]))
			printDebugInfo=true;
		else if (!std::string("-o").compare(argv[arg]))
		{
			if (++arg>=argc)
				return printHelp();
			optimalizationKS=atoi(argv[arg]);
		}
		else if (!std::string("-g").compare(argv[arg]))
			multiGain=false;
		else
		{
			printf("Unknown option \"%s\"\n\n",argv[arg]);
			return printHelp();
		}
	}

	//Chybi vstupni soubor
	if (fileName.empty())
	{
		printf("Missing required option \"-i filename\", or corrupted\n\n");
		return printHelp();
	}

	//Uprava nazvu statistickeho souboru
	if (!statsFileName.empty())
		statsFileName=ImportLang::bareName(fileName)+statsFileName;

	//Kontrola -s pri -d
	if (printDebugInfo && statsFileName.empty())
	{
		printf("Debug info must be used with an -s option\n");
		printDebugInfo=false;
	}

	//Definujeme lokaci, kam se PLA importuje
	hub::Location loc(&theHub);
	loc[hub::Location::lib].content("pla_library");
	loc[hub::Location::arch].content("pla");

	//Vytvorime knihovnu
	loc.FindOrCreate();

	//Pridame do struktury pro import
	Import::Destinations dest;
	dest.push_back(loc);

	//Import PLA
	try
	{
		//Otevreni souboru
		std::filebuf fIn;
		fIn.open(fileName.c_str(),std::ios::in);
		std::istream* istr=new std::istream(&fIn);
		//Vlastni import
		PlaImport* plaImport=new PlaImport(&theHub);
		(*plaImport)(istr,fileName,dest);
		//Uvolneni z pameti
		delete istr;
		fIn.close();
		delete plaImport;
		//V lokaci nastavime nazev a nastavime promenne
		loc[hub::Location::ent].content(ImportLang::bareName(fileName));
		loc.Find();
	}
	catch (...)
	{
		throw;
		printf("An error has occured during loading a PLA file, please check the file\n");
		return -1;
	}

	//Dekompozice
	try
	{
		Decomposer decomposer(&theHub);

		int ret=decomposer(loc,std::string("gates"),convertTo2Input,convertToNAND,statsFileName,printDebugInfo,optimalizationKS,multiGain);
		if (ret)
		{
			printf("An error has occured during a decomposition\n");
			return -1;
		}
	}
	catch (...)
	{
		printf("An error has occured during a decomposition\n");
		return -1;
	}
	
	//Export
	try
	{
		//Nastaveni lokace - vyber jine architektury
		loc.Reset();
		loc[hub::Location::arch].content("gatenet");
		loc.Find();

		//VHDL
		//Otevreni souboru
		std::filebuf fOut;
		fOut.open(std::string(ImportLang::bareName(fileName)+".vhdl").c_str(),std::ios::out);
		std::ostream* ostr=new std::ostream(&fOut);
		//Vlastni export
		VhdlExport* vhdlExport=new VhdlExport(&theHub);
		(*vhdlExport)(ostr,&loc);
		//Uvolneni z pameti
		delete ostr;
		fOut.close();
		delete vhdlExport;

		//BLIF
		//Otevreni souboru
		fOut.open(std::string(ImportLang::bareName(fileName)+".blif").c_str(),std::ios::out);
		ostr=new std::ostream(&fOut);
		//Vlastni export
		BlifExport* blifExport=new BlifExport(&theHub);
		(*blifExport)(ostr,&loc);
		//Uvolneni z pameti
		delete ostr;
		fOut.close();
		delete blifExport;
	}
	catch (...)
	{
		printf("An error has occured during exporting to vhdl\n");
		return -1;
	}

	//Vsechno hotovo
	printf("Decomposition done\n");
	return 0;
}