//---------------------------------------------------------------------------

#include <iostream>
#ifdef _BCPLUSPLUS_
#pragma hdrstop
#endif
#include "config.h"

#include <limits.h>
#include <map>


#ifdef HAVE_GETOPT
#include <unistd.h>
#else
#include "getopt.h"
#endif

#include "hub.h"
#include "edif_import.h"
#include "bench_import.h"
#include "vhdl_import.h"
#include "trav.h"
#include "import.h"
#include "export.h"
#include "dbg_export.h"
#include "vhdl_export.h"

//---------------------------------------------------------------------------
typedef enum Language {edif, blif, cir, bench, pla, vhdl, dbg} Language;
std::map <std::string, Language> types;
const char* langNames[] = { "EDIF", "BLIF", "CIR", "BENCH", "PLA", "VHDL", "DBG"};
//---------------------------------------------------------------------------
static std::string suffix (std::string fileName) {
    int kdot = fileName.find_last_of (suffixDelim);
    return fileName.substr (kdot+1, INT_MAX);
}
//---------------------------------------------------------------------------
static void mime (std::map<std::string, Language> & m) {
    m ["edf"] = m ["EDF"] = m ["edif"] = m ["EDIF"] = edif;
    m ["blf"] = m ["BLF"] = m ["blif"] = m ["BLIF"] = blif;
    m ["vhdl"] = m ["VHDL"] = m ["vhd"] = m ["VHD"]  = m ["v"]= vhdl;
    m ["cir"] = m ["CIR"] = cir;
    m ["bench"] = m ["BENCH"] = bench;
    m ["pla"] = m ["PLA"] = pla;
    m ["dbg"] = m ["DBG"] = dbg;
}
//---------------------------------------------------------------------------
bool canImport (Language l) {
    switch (l) {
 	case edif: 	return true;
 	case blif:  return false;
 	case pla:  	return false;
 	case bench:	return true; 
 	case cir:  	return false;
	case vhdl:	return true;
	case dbg:	return false;
	default:	return false;
    };
}
//---------------------------------------------------------------------------
bool canExport (Language l) {
    switch (l) {
 	case blif: 
 	case cir:  
 	case bench:
 	case edif: 
 	case pla:   return false;
	case vhdl:	return true;
	case dbg:	return true;
	default:	return false;
    };
}
//---------------------------------------------------------------------------
void doImport (Import* & importFunc, Language inLang) {
    try {
	(*importFunc) ();
    } 
    catch ( std::runtime_error e ) { cerr << e.what() << "\n"; terminate(); } 
    catch ( std :: logic_error e ) { cerr << e.what() << "\n"; terminate(); }
    cout << "Import from " << importFunc->fileName() << " language " << langNames[inLang] << " OK\n";
    delete importFunc;
    importFunc = NULL;
}
//---------------------------------------------------------------------------
void doExport (Export* & exportFunc, Language outLang) {
    try {
	//	(*exportFunc) ();
	exportFunc->operator()();
    } 
    catch ( std::runtime_error e ) { cerr << e.what() << "\n"; terminate(); } 
    catch ( std :: logic_error e ) { cerr << e.what() << "\n"; terminate(); }
    cout << "Export to " << exportFunc->fileName() << " language " << langNames[outLang] << " OK\n";
    delete exportFunc;
    exportFunc = NULL;
}
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
    Language inLang, outLang;
    std::map <std::string, Language> :: const_iterator it;

    hub::Hub theHub;

    Import* importFunc = NULL;
    Export* exportFunc = NULL;
    // GuessPrim* guessFunc = NULL;		// disabled in Structural Edition
    
    std::cout << "Logic Specification Formats Convertor Pre-release(Feb 2006)\n\n";

    mime (types);
    int c;
   while ((c = getopt (argc, argv, "I:O:Gl:e:a:")) > 0) {
	switch (c) {
	    case 'I':		// -------------------- import from indicated file ----------
		if (importFunc) doImport (importFunc, inLang);
		if (exportFunc) doExport (exportFunc, outLang);
		
		it = types.find (suffix (optarg));
		if (it == types.end()) {
		    cerr << "." << suffix (optarg) << ": unknown file suffix\n";
		    return -1;
		}
		inLang = it->second;
		if ( !canImport(inLang) ) {
		    cerr << "cannot import from language " << langNames[inLang] << "\n";
		    return -1;
		}
		switch (inLang) {
			case vhdl:
			importFunc = new VhdlImport(&theHub,std::string (optarg)); 
			break;
		    case edif:
			importFunc = new EdifImport (&theHub, std::string (optarg));
			break;
		    case bench:
			importFunc = new BenchImport (&theHub, std::string (optarg));
			break;
		}
		break;

	    case 'O':		// ------------------- export to indicated file ------------
		if (importFunc) doImport (importFunc, inLang);
		if (exportFunc) doExport (exportFunc, outLang);

		it = types.find (suffix (optarg));
		if (it == types.end()) {
		    cerr << "." << suffix (optarg) << ": unknown file suffix\n";
		    return -1;
		}
		outLang = it->second;
		if (!canExport(outLang)) {
		    cerr << "cannot export to language " << langNames[outLang] << "\n";
		    return -1;
		}
		switch (outLang) {
		    case vhdl:
			exportFunc = new VhdlExport (&theHub, std::string (optarg));
			break;
		    case dbg:
			exportFunc = new DbgExport (&theHub, std::string (optarg));
			break;
		    default:
			break;
		}
		break;
		
	    case 'G':		// ------------------ guess behaviour and clock/reset signals ---
		if (importFunc) doImport (importFunc, inLang);
		if (exportFunc) doExport (exportFunc, outLang);

		// guessFunc = new GuessPrim (&theHub);
		// (*guessFunc)();
		// delete guessFunc;
		cout << "Primitives behavior estimation disabled in Structural Edition\n";
		break;

	    case 'l':		// ------------------ specify library for imported entities -----
		if (importFunc) importFunc->libName (optarg);
		if (exportFunc) exportFunc->libName (optarg);
		break;

	    case 'e':		// ------------------ specify entity for the imported circuit -----
		if (importFunc) importFunc->entityName (optarg);
		if (exportFunc) exportFunc->entityName (optarg);
		break;

	    case 'a':		// ------------------ specify architecture name for imported entities -----
		if (importFunc) importFunc->archName (optarg);
		if (exportFunc) exportFunc->archName (optarg);
		break;
		
	    default:
		break;
	}
    }

    if (importFunc) doImport (importFunc, inLang);
    if (exportFunc) doExport (exportFunc, outLang);

    return 0;
}

