#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"

using namespace std;

// Global variables

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

extern functionUser *fun;

void CleanAll();

/**
 * Simulate by compiling
 **/
int EvalCompile(){
	vector<variable *> &outputs = fun->GetOutputs();
	vector<variable *> &inputs = fun->GetInputs();

	if(verbose)cout << endl << "---------- Create a C program : sim.c ----------" << endl << endl;

	ofstream f("sim.cpp",ios::out);

	f << "#include <stdio.h>" << endl;

	if(watch2){f << "#include \"watch2.h\"" << endl;}

	if(period){
        f << "#include <time.h>" << endl;
        f << "#include <sys/time.h>" << endl;

		f << "inline double timeF()" << endl;
		f << "{" << endl;
		f << "  struct timeval t;" << endl;
		f << "  if( gettimeofday( &t, NULL ) )" << endl;
		f << "    printf(\"\tFATAL ERROR : Can\'t use gettimeofday.\\n\");" << endl;
		f << "  return t.tv_sec + 1E-6 * t.tv_usec;" << endl;
		f << "}" << endl;
	}

    fun->CPutDeclarations(f,"v");

	f << "long i;" << endl;
	f << "unsigned short hour=0, minute=0, second=0, hourP, minuteP, secondP;" << endl;
    f << "short value;" << endl;
    if(micro)f << "short lastChangeOut=0;" << endl;
	f << "unsigned long mem_val=0, mem_addr=0;" << endl;
	f << "void print_time(int x, int y, int z);" << endl;

    if(!watch2){
        f << "int main(){" << endl;
    }else{
        f << "void Eval(){" << endl;
    }

	if(period){
		f << "	double period=" << period << ";" << endl;
		f << "	double prevTime = timeF()-period;" << endl;
		f << "" << endl;
#ifndef DO_NOT_USE_NANOSLEEP
		f << "	long long nanosecsTimer = (long long)(1000000000*period);" << endl;
		f << "	struct timespec dt;" << endl;
		f << "	dt.tv_sec  = nanosecsTimer / 1000000000;" << endl;
		f << "	dt.tv_nsec = nanosecsTimer % 1000000000;" << endl;
#endif
	}

	if(NB_CYCLES)f << "\tfor(i=0;i<"<<NB_CYCLES<<";i++){" << endl;
	else f<< "\twhile(1){" << endl;

	if(period){
		f << "		double curTime = timeF();" << endl;
		f << "		if(prevTime + period > curTime){" << endl;
#ifndef DO_NO_USE_NANOSLEEP
		f << "			nanosleep( &dt, NULL );" << endl;
#endif
		f << "			i--;" << endl;
		f << "			continue;" << endl;
		f << "		}" << endl;
		f << "		prevTime += period;" << endl;
	}

	fun->CPutCommands(f,"v");

	if(!quiet){
		f << "// Print" << endl;

        if(watch){
            f << "" << endl;
            f << "		hourP = hour;" << endl;
            f << "		minuteP = minute;" << endl;
            f << "		secondP = second;" << endl;
            f << "		hour = 0;" << endl;
            f << "		minute = 0;" << endl;
            f << "		second = 0;" << endl;
            f << "" << endl;
            if(micro){
                for(short i=91;i<98;i++){
                    f << "		hour<<=1;" << endl;
                    f << "		hour|=v_" << outputs[i]->id << " & 1;" << endl;
                }
                for(short i=75;i<82;i++){
                    f << "		hour<<=1;" << endl;
                    f << "		hour|=v_" << outputs[i]->id << " & 1;" << endl;
                }
                for(short i=59;i<66;i++){
                    f << "		minute<<=1;" << endl;
                    f << "		minute|=v_" << outputs[i]->id << " & 1;" << endl;
                }
                for(short i=43;i<50;i++){
                    f << "		minute<<=1;" << endl;
                    f << "		minute|=v_" << outputs[i]->id << " & 1;" << endl;
                }
                for(short i=27;i<34;i++){
                    f << "		second<<=1;" << endl;
                    f << "		second|=v_" << outputs[i]->id << " & 1;" << endl;
                }
                for(short i=11;i<18;i++){
                    f << "		second<<=1;" << endl;
                    f << "		second|=v_" << outputs[i]->id << " & 1;" << endl;
                }
            }else{
                for(short i=0;i<14;i++){
                    f << "		hour<<=1;" << endl;
                    f << "		hour|=v_" << outputs[i]->id << " & 1;" << endl;
                }
                for(short i=14;i<28;i++){
                    f << "		minute<<=1;" << endl;
                    f << "		minute|=v_" << outputs[i]->id << " & 1;" << endl;
                }
                for(short i=28;i<42;i++){
                    f << "		second<<=1;" << endl;
                    f << "		second|=v_" << outputs[i]->id << " & 1;" << endl;
                }
            }
            f << "" << endl;
            f << "		if(second!=secondP || minute!=minuteP || hour!=hourP){" << endl;
            f << "			print_time(hour,minute,second);" << endl;
            f << "		}" << endl;
            f << "" << endl;
        }else if(micro){
            f << endl;
            f << "\t\tif((lastChangeOut & 1)!=v_" << outputs[0]->id << "){" << endl;
            if(!watch2 && !quiet)f << "\t\t\tprintf(\"%6ld - output :      \",i);" << endl;
            for(short j=0;j<8;j++){
                if(!watch2){
                    f << "\t\t\tvalue = 0;" << endl;
                    for(short k=0;k<16;k++){
                        f << "\t\t\tvalue<<=1;" << endl;
                        f << "\t\t\tvalue|=v_" << outputs[2+16*j+k]->id << " & 1;" << endl;
                    }
                    f << "\t\t\tprintf(\"%7d\",value);" << endl;
                }else{
                    if(j>=6)break;
                    for(short k=0;k<7;k++){
                        f << "\t\t\tWatch2ChangeSeg(" << j << "," << k << ",v_" << outputs[2+(5-j)*16+15-k]->id << ");" << endl;
                    }
                }
            }
            if(watch2)f << "\t\t\tif(Watch2Update())return;" << endl;
            f << "\t\t\tlastChangeOut = v_" << outputs[0]->id << " & 1;" << endl;
            if(!watch2 && !quiet)f << "\t\t\tprintf(\"\\n\");" << endl;
            f << "\t\t}" << endl;
        }else if(watch2){
            f << "\t\tbool change=false;" << endl;
            for(short j=0;j<6;j++){
                for(short k=0;k<7;k++){
                    f << "\t\tchange |= Watch2ChangeSeg(" << j << "," << k << ",v_" << outputs[j*7+6-k]->id << ");" << endl;
                }
            }
            f << "\t\tif(change && Watch2Update())return;" << endl;
        }else{
            f << "\t\tprintf(\"%"<<3<<"ld";
            for(vector<variable *>::iterator it=inputs.begin() ; it!=inputs.end() ; it++){
                f << "%"<<SIZE_COLUMN<<"d";
            }
            for(vector<variable *>::iterator it=outputs.begin() ; it!=outputs.end() ; it++){
                f << "%"<<SIZE_COLUMN<<"d";
            }
            f << "\\n\",i";
            for(vector<variable *>::iterator it=inputs.begin() ; it!=inputs.end() ; it++){
                f << ",1&v_" << (*it)->id;
            }
            for(vector<variable *>::iterator it=outputs.begin() ; it!=outputs.end() ; it++){
                f << ",1&v_" << (*it)->id;
            }
            f << ");" << endl;
        }
	}

	f << "// Registers" << endl;

	fun->CPutRegistersSave(f,"v");
	fun->CPutRegistersNext(f,"v");

	f << "\t}" << endl;

    if(!quiet)f << "\tprintf(\"\\n\");" << endl;
    if(!watch2)f << "\treturn 0;" << endl;
	f << "}" << endl;

	f.close();

	if(verbose)cout << "Compile sim.c (need print_time.c/watch2.cpp/watch2.h)... ";
	string command=ccBin +
        (!watch2 ? string(" -x c -o sim src/print_time.cpp sim.cpp") : string(" `wx-config --cflags --libs` -DUSE_WX -DSIM_C -Iinclude -o sim src/watch2.cpp sim.cpp"));
	if(system(command.c_str())){
		if(verbose)cout << "ERR" << endl;
		else cout << "Compile sim.c... ERR" << endl;
		CleanAll();
		return -1;
	}else{
		if(verbose)cout << "OK" << endl;
	}

	if(verbose)cout << "Results : " << endl << endl;
	if(!watch && !quiet && !watch2 && !micro){
        cout << setw(3) << "";
        for(vector<variable *>::iterator it=inputs.begin() ; it!=inputs.end() ; it++){
            cout << setw(SIZE_COLUMN) << (*it)->GetName().substr(0,SIZE_COLUMN-1);
        }
        for(vector<variable *>::iterator it=outputs.begin() ; it!=outputs.end() ; it++){
            cout << setw(SIZE_COLUMN) << (*it)->GetName().substr(0,SIZE_COLUMN-1);
        }
	}else if(micro && !watch && !watch2 && !quiet){
        cout << "                       ";
        for(short i=0;i<8;i++){
            cout << setw(7) << i;
        }
        cout << endl;
    }

    if(!quiet){
        cout << endl;
        cout.flush();
    }
	return system("./sim");
}
