#ifdef USE_WX
#include <wx/wx.h>
#include <wx/image.h>
#include <wx/sizer.h>
#endif

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <fstream>
#include <cstdlib>
#include <sstream>

#include <time.h>
#include <sys/time.h>

#include "parserNetlist.h"
#include "graph.h"
#include "parserVar.h"
#include "functionUser.h"
#include "diag.h"
#include "parserMem.h"

#include "eval.h"
#include "eval_compile.h"

// For Windows, define this
// For Linux, you can comment this
//#define DO_NOT_USE_NANOSLEEP

using namespace std;

// Global variables

bool verbose;
bool quiet,compile,customSettings,memFile,diag,noGraphCopy,noITS,watch,micro,watch2;
unsigned long NB_CYCLES=16,SIZE_COLUMN=5;
string settingsFileName;
string netlistFileName;
string memFileName;
string ccBin;
double period=0;

setting_file *sett;

functionUser *fun;

// Functions

void CleanAll();
void Help(bool=false);



/**
 * Print help of command line
 * @param error : true when command line is not correctly used
 **/
void Help(bool error){
	if(error){
		cerr << "Bad arguments -> Try again !" << endl << endl;
	}

	//cout << "   simulateur [-v] [-q] [-n n]    [-c] [--column s] [-m file] [--diag] [-p period] netlist" << endl;
	//cout << "or simulateur [-v] [-q] [-s file]      [--column s] [-m file] [--diag] [-p period] netlist" << endl;
	cout << "Usage:    simulateur [options] netlist" << endl;
	cout << "       or simulateur -h" << endl;
	cout << endl;
	cout << "Options:" << endl;
	cout << "      -v          verbose" << endl;
	cout << "      -q          do not print the simulation results" << endl;
	cout << "      -n n        number of cycles        (default : n=16)" << endl;
	cout << "      --column s  size of column          (default : s=5)" << endl;
	cout << endl;
	cout << "      --nogc      no graph copy WARNING : there are some limitations" << endl;
	cout << "      --noits     no intermediate topological sort (not with --nogc)" << endl;
	cout << endl;
	cout << "      -c          compile insteading interpreting" << endl;
	cout << "      --cc bin    use this C compiler     (default : gcc)" << endl;
	cout << endl;
	cout << "      -m file     use this memory file" << endl;
	cout << "      -s file     use this settings file  (not with -c)" << endl;
	cout << "      --diag      print timing diagram    (not with -c)" << endl;
	cout << "      --watch     print a watch           (not with --diag)" << endl;
#ifdef USE_WX
	cout << "      --watch2    watch with WxWidgets    (not with --watch/--diag)" << endl;
#endif
	cout << "      -p period   period of one cycle     (by default max speed)" << endl;
	cout << endl;
	cout << "      --micro     consider inputs/outputs as microprocessor ones" << endl;
	cout << endl;
	cout << "      -h          print this help" << endl;
	cout << endl;
}

int main(int argc, char **argv){

	// Parse command line arguments

	quiet = false;
	compile = false;
	customSettings = false;
	verbose = false;
	diag = false;
	memFile = false;
	noGraphCopy = false;
	noITS = false;
	watch = false;
	micro = false;
	watch2 = false;

	ccBin = "gcc";

	if(argc<2){
		Help(true);
		return -1;
	}

	for(short i=1;i<argc-1;i++){
		if(string(argv[i])=="-v"){
			verbose=true;
		}else if(string(argv[i])=="-c"){
			compile=true;
		}else if(string(argv[i])=="--cc" && i<argc+1){
			i++;
			ccBin = string(argv[i]);
		}else if(string(argv[i])=="-q"){
			quiet=true;
		}else if(string(argv[i])=="-n" && i<argc+1){
			i++;
			istringstream str(argv[i]);
			str >> NB_CYCLES;
		}else if(string(argv[i])=="--column" && i<argc+1){
			i++;
			istringstream str(argv[i]);
			str >> SIZE_COLUMN;
		}else if(string(argv[i])=="-s" && i<argc+1){
			i++;
			customSettings = true;
			settingsFileName = string(argv[i]);
		}else if(string(argv[i])=="-m" && i<argc+1){
			i++;
			memFile = true;
			memFileName = string(argv[i]);
		}else if(string(argv[i])=="--diag"){
			diag = true;
		}else if(string(argv[i])=="--watch"){
		    watch = true;
#ifdef USE_WX
		}else if(string(argv[i])=="--watch2"){
		    watch2 = true;
#endif
		}else if(string(argv[i])=="--nogc"){
		    noGraphCopy = true;
		}else if(string(argv[i])=="--noits"){
		    noITS = true;
		}else if(string(argv[i])=="--micro"){
		    micro = true;
        }else if(string(argv[i])=="-p" && i<argc+1){
			i++;
			istringstream str(argv[i]);
			str >> period;
			if(period<0){
				cerr << "Period has to be >0." << endl;
				Help(true);
				return -1;
			}
		}else if(string(argv[i])=="-h"){
			Help();
			return 0;
		}else{
			Help(true);
			return -1;
		}
	}

	if(noITS && noGraphCopy){
		cerr << "You can't use --noits and --nogc flags together." << endl;
		Help(true);
		return -1;
	}
	if(customSettings && compile){
		cerr << "You can't use -c and -s flags together." << endl;
		Help(true);
		return -1;
	}
	if(diag && compile){
		cerr << "You can't use -c and --diag flags together." << endl;
		Help(true);
		return -1;
	}
	if(diag && watch){
		cerr << "You can't use --diag and --watch flags together." << endl;
		Help(true);
		return -1;
	}
	if(diag && watch2){
		cerr << "You can't use --diag and --watch2 flags together." << endl;
		Help(true);
		return -1;
	}
	if(watch && watch2){
		cerr << "You can't use --watch and --watch2 flags together." << endl;
		Help(true);
		return -1;
	}

    if(argc<=1){
        cerr << "No netlist." << endl;
        Help(true);
        return -1;
    }

    netlistFileName = argv[argc-1];

	if(netlistFileName=="-h"){
        Help();
        return 0;
	}

	setting_file sett_(settingsFileName);
	sett = &sett_;

	// Read netlist file and parse it

	if(verbose)cout << endl << "********** Read NetList file \"" << netlistFileName << "\" **********" << endl;

	fun = ParserNetlist(netlistFileName);

	if(!fun){
		cerr << endl << "An error has occured -> we can't continue." << endl;
		CleanAll();
		return -1;
	}

	if(customSettings){
		if(verbose)cout << endl << endl << "********** Read Settings file \"" << settingsFileName << "\" **********" << endl;

		if(!sett->init_settings(fun)){
			cerr << endl << "An error has occured -> we can't continue." << endl;
			CleanAll();
			return -1;
		}
	}

	if(memFile){
		if(verbose)cout << endl << endl << "********** Read Memory file \"" << memFileName << "\" **********" << endl;

		if(!ParserMem(memFileName)){
			cerr << endl << "An error has occured -> we can't continue." << endl;
			CleanAll();
			return -1;
		}
	}

    if(!micro && watch && fun->GetNumberOfOutputs()!=42){
        cerr << "Watch ERROR : there are " << fun->GetNumberOfOutputs() << " instead of 42 outputs." << endl;
        cerr << endl << "An error has occured -> we can't continue." << endl;
        CleanAll();
        return -1;
    }

    if(micro && fun->GetNumberOfOutputs()!=2+8*16){
        cerr << "Micro ERROR : there are " << fun->GetNumberOfOutputs() << " instead of 130 outputs." << endl;
        cerr << endl << "An error has occured -> we can't continue." << endl;
        CleanAll();
        return -1;
    }

	// Simulate all that stuff !

	if(compile){
		int ret = EvalCompile();
		if(ret){
			if(customSettings)sett->close();
			return ret;
		}
	}else{
	    if(watch2){
#ifdef USE_WX
	        return wxEntry(argc,argv);
#endif
	    }else{
            int ret = Eval();
            if(ret){
                if(customSettings)sett->close();
                return ret;
            }
	    }
	}

	if(customSettings){
		sett->close();
	}

	CleanAll();

    return 0;
}

/**
 * Clean all graphs/functions
 **/
void CleanAll(){
	ParserNetlistFree();
}
