#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 "watch2.h"

#include "eval.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 setting_file *sett;

extern functionUser *fun;

vector< vector<bool> > simulationDiag;

void CleanAll();
void print_time(int x, int y, int z);

/**
 * Return current time is us
 **/
inline double timeF()
{
  struct timeval t;
  if( gettimeofday( &t, NULL ) )
    cerr << "\tFATAL ERROR : Can't use gettimeofday" << endl;
    //exit( -1 );
  return t.tv_sec + 1E-6 * t.tv_usec;
}

/**
 * Simulate by interpreting
 **/
int Eval(){
	double prevTime = timeF()-period;

#ifndef DO_NOT_USE_NANOSLEEP
	long long nanosecsTimer = (long long)(1000000000*period);
	struct timespec dt;
	dt.tv_sec  = nanosecsTimer / 1000000000;
	dt.tv_nsec = nanosecsTimer % 1000000000;
#endif

	if(verbose)cout << endl << "---------- Evaluation ----------" << endl << endl;

	vector<variable *> &outputs = fun->GetOutputs();
	vector<variable *> &inputs = fun->GetInputs();

	vector<string> names;

    if(!watch && !watch2){
        if(diag){
            for(vector<variable *>::iterator it=inputs.begin() ; it!=inputs.end() ; it++){
                names.push_back((*it)->GetName().substr(0,SIZE_COLUMN-1));
                simulationDiag.push_back( vector<bool> ());
            }
            for(vector<variable *>::iterator it=outputs.begin() ; it!=outputs.end() ; it++){
                names.push_back((*it)->GetName().substr(0,SIZE_COLUMN-1));
                simulationDiag.push_back( vector<bool> ());
            }
        }else if(micro){
            cout << "                       ";
            for(short i=0;i<8;i++){
                cout << setw(7) << i;
            }
            cout << endl;
        }else if(!quiet){
            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);
            }
        }
    }

	cout << endl;

	short hour=0,minute=0,second=0;
	short hourP,minuteP,secondP;    // P for "previuous", value at previous cycle
	bool lastChangeOut=false;
	//bool lastChangeIn=false;

	for(unsigned long i=0; i<NB_CYCLES || customSettings || !NB_CYCLES ; i++){
		if(period){
			double curTime = timeF();
			if(prevTime + period > curTime){
#ifndef DO_NOT_USE_NANOSLEEP
                nanosleep( &dt, NULL );
#endif
                i--;
                continue;
            }
			prevTime += period;
		}

		if(customSettings){
			if(!(sett->forced_var())){
				break;
			}// assuming nothing overwrites the forced variables inputs
		}
		if(!quiet && !diag && !watch && !micro && !watch2){
			cout << setw(3) << i;
		}

		fun->Eval();

		if(!quiet){
			if(diag){
				short j=0;
				for(vector<variable *>::iterator it=inputs.begin() ; it!=inputs.end() ; it++){
					simulationDiag[j].push_back((*it)->GetValue());
					j++;
				}
				for(vector<variable *>::iterator it=outputs.begin() ; it!=outputs.end() ; it++){
					simulationDiag[j].push_back((*it)->GetValue());
					j++;
				}
			}else if(watch){
			    hourP = hour;
			    minuteP = minute;
			    secondP = second;
			    hour = 0;
			    minute = 0;
			    second = 0;

                if(micro){
                    for(short j=27;j<34;j++){
                        second<<=1;
                        second|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                    for(short j=11;j<18;j++){
                        second<<=1;
                        second|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                    for(short j=59;j<66;j++){
                        minute<<=1;
                        minute|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                    for(short j=43;j<50;j++){
                        minute<<=1;
                        minute|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                    for(short j=91;j<98;j++){
                        hour<<=1;
                        hour|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                    for(short j=75;j<82;j++){
                        hour<<=1;
                        hour|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                }else{
                    for(short j=0;j<14;j++){
                        hour<<=1;
                        hour|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                    for(short j=14;j<28;j++){
                        minute<<=1;
                        minute|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                    for(short j=28;j<42;j++){
                        second<<=1;
                        second|=(outputs[j]->GetValue() ? 1 : 0);
                    }
                }

			    if(second!=secondP || minute!=minuteP || hour!=hourP){
			        print_time(hour,minute,second);
			    }
			}else if(watch2){
			    if(!micro){
			        bool change = false;
                    for(short j=0;j<6;j++){
                        for(short k=0;k<7;k++){
                            change |= Watch2ChangeSeg(j,k,outputs[j*7+6-k]->GetValue());
                        }
                    }
                    if(change && Watch2Update())return 0;
			    }else{
			        if(lastChangeOut!=outputs[0]->GetValue()){
                        for(short j=0;j<6;j++){
                            for(short k=0;k<7;k++){
                                Watch2ChangeSeg(j,k,outputs[2+(5-j)*16+15-k]->GetValue());
                            }
                        }
                        if(Watch2Update())return 0;
                        lastChangeOut=outputs[0]->GetValue();
			        }
			    }
			}else if (micro){
                if(lastChangeOut!=outputs[0]->GetValue()){
                    cout << setw(6) << i << " - output :      ";
                    for(short j=0;j<8;j++){
                        short value = 0;
                        for(short k=0;k<16;k++){
                            value<<=1;
                            value|=(outputs[2+16*j+k]->GetValue() ? 1 : 0);
                        }
                        cout << setw(7) << value;
                    }
                    cout << endl;
                    lastChangeOut=outputs[0]->GetValue();
                }
			}else{
				for(vector<variable *>::iterator it=inputs.begin() ; it!=inputs.end() ; it++){
					cout << setw(SIZE_COLUMN) << (*it)->GetValue();
				}
				for(vector<variable *>::iterator it=outputs.begin() ; it!=outputs.end() ; it++){
					cout << setw(SIZE_COLUMN) << (*it)->GetValue();
				}
			}
		}

		fun->MainNextClockEdge();

		if(!quiet && !diag && !watch && !micro && !watch2){
			cout << endl;
		}
	}

	if(diag){
		PrintDiag(simulationDiag,names);
	}

	cout << endl;

	return 0;
}
