#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <sstream>

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

#include "instructions.h"
#include "parser.h"
#include "map.h"

using namespace std;

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

bool errorOccured=false;

vector<instruction> program;

/**
 * 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;
}


/**
 * 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:    asm [options] source" << endl;
	cout << "       or asm -h" << endl;
	cout << endl;
	cout << "Options:" << endl;
	cout << "      -v          verbose" << endl;
	cout << endl;
	cout << "      --sim       simulate after compiling" << endl;
	cout << "      --rc        print content of register at each cycle of simulation" << endl;
	cout << endl;
    cout << "      -p period   period of one cycle     (default : 0 - no limit)" << endl;
    cout << "      -n cycles   max number of cycles    (default : 0 - no limit)" << endl;
    cout << endl;
    cout << "      --watch     print watch (see manual)" << endl;
	cout << endl;
	cout << "      -h          print this help" << endl;
	cout << endl;
}

int main(int argc, char **argv)
{
    bool rc = false;        // print register content & instructions
    bool sim = false;   // simulate
    bool verbose = false;
    bool watch = false;
    unsigned int maxCycles=0;

    double period = 0;

    for(short i=1;i<argc-1;i++){
		if(string(argv[i])=="--rc"){
			rc = true;
		}else if(string(argv[i])=="--sim"){
			sim = true;
		}else if(string(argv[i])=="--watch"){
			watch = true;
		}else if(string(argv[i])=="-v"){
			verbose = true;
		}else if(string(argv[i])=="-p" && i<argc+1){
			i++;
			if(i>=argc-1){
			    cerr << "Missing argument to -p." << endl;
			    Help(true);
			    return -1;
			}
			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])=="-n" && i<argc+1){
			i++;
			if(i>=argc-1){
			    cerr << "Missing argument to -n." << endl;
			    Help(true);
			    return -1;
			}
			istringstream str(argv[i]);
			str >> maxCycles;
		}else if(string(argv[i])=="-h"){
            Help(false);
            return 0;
		}else{
		    Help(true);
		    return -1;
		}
    }

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

    string src = string(argv[argc-1]);

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


    if(verbose)     cout << endl << endl << "---------------- Initialization ------------------" << endl ;
    static int ram[65536] = {0};
    unsigned short inputs[NB_IO] = {0};
    unsigned short outputs[NB_IO] = {0};

        // CARRY AND ZERO ARE REGISTERS
    int registers[18] = {0}; // register 16 is carry, register 17 is zero
    table_label labels;

    if(verbose)     cout << endl << endl << "---------------- Parsing of " << src << " ------------------" << endl << endl;
    src_file tests;
    tests.parsing(src,program, labels,verbose);
    vector<instruction>::iterator it;
    if(errorOccured){
        cerr << endl << "An error has occured -> we can't continue." << endl;
        return -1;
    }

    if(verbose)     cout << endl << endl << "---------------- Instructions verification ------------------" << endl << "Replaces pseudoinstructions and fixes other details" << endl << endl;
    for(it=program.begin();it!=program.end();it++)     (*it).verif(&labels,&program,&it,verbose);
    if(errorOccured){
        cerr << endl << "An error has occured -> we can't continue." << endl;
        return -1;
    }

    vector<instruction>::iterator begin = program.begin();

    if(!sim){
        if(verbose)     cout << endl << endl << "---------------- Program code ------------------" << endl << endl;
        for(it=begin;it!=program.end();it++)      (*it).printc();
    }
    if(errorOccured){
        cerr << endl << "An error has occured -> we can't continue." << endl;
        return -1;
    }

    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 (sim) {
        bool sleep1=false;
        unsigned int cycles = 0;

        if(verbose)     cout << "---------------- Simulation results ------------------" << endl << endl;
        for(it=begin;it!=program.end();){
            // Wait for next cycle
            if(period){
                double curTime = timeF();
                if(prevTime + period > curTime){
#ifndef DO_NOT_USE_NANOSLEEP
                    nanosleep( &dt, NULL );
#endif
                    continue;
                }
                prevTime += period;
            }

            cycles++;
            if(maxCycles && cycles>=maxCycles)break;

            outputs[9]++;   // Timer value
            if(outputs[9]==outputs[8]){
                // Counter modulo outputs[8]
                outputs[9]=0;
                // Wake up the microprocessor
                sleep1 = false;
            }
            inputs[9]=outputs[9];

            // Execute instruction if not in sleep mode
            if(sleep1)continue;
            if (rc) {
                cout << it - begin << " >>> ";
                (*it).print();
                sleep1 = (*it).execute(registers,&it,begin,ram,inputs,outputs,watch);
                int i = 0;
                cout << "[";
                for (i=0;i<16;i++)     cout << i << ":" << registers[i] << " | ";
                cout << "C" << ":" << registers[16] << " | ";
                cout << "Z" << ":" << registers[17] << "]" << endl << endl;
            } else {
                sleep1 = (*it).execute(registers,&it,begin,ram,inputs,outputs,watch);
            }

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

            // Save timer period/value if needed;
            inputs[8]=outputs[8];
            inputs[9]=outputs[9];
        }
    }

    return 0;
}

