#include "Generator.hpp"
#include <iostream>
#include <math.h>

Generator::Generator (QString name,
                      int depth, int procNbr, int nbrOfState, int nbrOfLine,
                      int countNbr, int eventNbr, int linkNbr,
                      int eventTypeNbr, int linkTypeNbr,
                      bool paje, bool otf, bool tau)
{
    _paje          = paje;
    _otf           = otf;
    _tau           = tau;

    _name          = name;
    _depth         = depth;
    _procNbr       = procNbr;
    _numberOfState = nbrOfState;
    _numberOfLine  = nbrOfLine;

    _counter       = false;
    _event         = false;
    _link          = false;

    _linkNbr       = linkNbr;
    _linkTypeNbr   = linkTypeNbr;
    _eventNbr      = eventNbr;
    _eventTypeNbr  = eventTypeNbr;
    _counterNbr    = countNbr;
    _size          = 0;


    _arrayOfValues = new int[TYPEACTION];
    int pos = 0;
    _arrayOfValues[pos] = CHANGESTATE;
    pos ++;
    if (linkNbr){
        _arrayOfValues[pos] = STARTLINK;
        pos ++;
        _arrayOfValues[pos] = ENDLINK;
        pos ++;
    }
    if (countNbr){
        _arrayOfValues[pos] = INCCPT;
        pos ++;
        _arrayOfValues[pos] = DECCPT;
        pos ++;
    }
    if (eventNbr){
        _arrayOfValues[pos] = EVENT;
        pos ++;
    }
    _numberAction = pos;
    fprintf (stderr, "Valeur de nb action = %d \n", _numberAction);


    if (paje)
        _pajeGen = new PajeGenerator ();
    if (otf)
        _otfGen  = new OTFGenerator  ();
    if (tau)
        _tauGen  = new TauGenerator  ();

}

Generator::~Generator (){
    if (_pajeGen)
        delete _pajeGen;
    _pajeGen = NULL;
    if (_tauGen)
        delete _tauGen;
    _tauGen = NULL;
    if (_otfGen)
        delete _otfGen;
    _otfGen = NULL;
}

void Generator::initTrace (QString name, int depth, int procNbr, int stateTypeNbr, int eventTypeNbr, int linkTypeNbr, int varNbr){
    if (_paje)
        _pajeGen->initTrace (name, depth, procNbr, stateTypeNbr, eventTypeNbr, linkTypeNbr, varNbr);
    if (_tau)
        _tauGen->initTrace  (name, depth, procNbr, stateTypeNbr, eventTypeNbr, linkTypeNbr, varNbr);
    if (_otf)
        _otfGen->initTrace  (name, depth, procNbr, stateTypeNbr, eventTypeNbr, linkTypeNbr, varNbr);

    time_t t;
    t = time(NULL);
    srand ((int)t);
}

void Generator::endTrace (){
    int i;
    my_link lnk;
    fpritnf (stderr, "Ending the trace, nettoyage des liens morts\n");
    for (i=0; i<_size; i++){
        lnk = getLink (1);
        // Ending the link
        endLink (lnk.proc, lnk.type, lnk.time);
    }
    fprintf (stderr, "Nettoyee \n");
    if (_paje)
        _pajeGen->endTrace ();
    if (_tau)
        _tauGen->endTrace  ();
    if (_otf)
        _otfGen->endTrace  ();
}


void Generator::addState (int proc, int state, double time){
    if (_paje)
        _pajeGen->addState (proc, state, time);
    if (_tau)
        _tauGen->addState  (proc, state, time);
    if (_otf)
        _otfGen->addState  (proc, state, time);
}



void Generator::startLink (int proc, int type, double time){
    if (_paje)
        _pajeGen->startLink (proc, type, time);
    if (_tau)
        _tauGen->startLink  (proc, type, time);
    if (_otf)
        _otfGen->startLink  (proc, type, time);
}



void Generator::endLink (int proc, int type, double time){
    if (_paje)
        _pajeGen->endLink (proc, type, time);
    if (_tau)
        _tauGen->endLink  (proc, type, time);
    if (_otf)
        _otfGen->endLink  (proc, type, time);
}



void Generator::addEvent (int proc, int type, double time){
    if (_paje)
        _pajeGen->addEvent (proc, type, time);
    if (_tau)
        _tauGen->addEvent  (proc, type, time);
    if (_otf)
        _otfGen->addEvent  (proc, type, time);
}


void Generator::incCpt (int proc, int var, double time){
    if (_paje)
        _pajeGen->incCpt (proc, var, time);
    if (_tau)
        _tauGen->incCpt  (proc, var, time);
    if (_otf)
        _otfGen->incCpt  (proc, var, time);
}


void Generator::decCpt (int proc, int var, double time){
    if (_paje)
        _pajeGen->decCpt (proc, var, time);
    if (_tau)
        _tauGen->decCpt  (proc,var, time);
    if (_otf)
        _otfGen->decCpt  (proc, var, time);
}

void Generator::generate (){
    int i;
    int val = 0;

    initTrace (_name, _depth, _procNbr, _numberOfState, _eventTypeNbr,  _linkTypeNbr, _counterNbr);
    for (i=0; i<_numberOfLine; i++){
        do{
            val = getRand (_numberAction);
            if (_arrayOfValues[val]==STARTLINK)
                _size ++;
        }
        while (_arrayOfValues[val]==ENDLINK && _size<1);
        launchAction (_arrayOfValues[val], i);
    }
    endTrace ();
}

void Generator::launchAction (int val, int time){
    int  newVal;
    int  newVal2;
    my_link lnk;

    switch (val){
    case CHANGESTATE :
        // Getting the corresponding proc
        newVal = getRand (_procNbr);
        // Getting the corresponding state
        newVal2 = getRand (_numberOfState);
        addState (newVal, newVal2, time);
        break;
    case STARTLINK :
        // Getting the corresponding proc
        newVal = getRand (_linkNbr);
        // Getting the corresponding link type
        newVal2 = getRand (_linkTypeNbr);

        lnk.time  = time;
        lnk.proc  = newVal;
        lnk.type  = newVal2;

        // Ajouter un lien a la liste
        _startedLink.append (lnk);
        startLink (newVal, newVal2, time);
        break;
    case ENDLINK :
        // Choosing the link to end
        newVal = getRand (_size);
        _size--;
        lnk = getLink (newVal);
        // Ending the link
        endLink (lnk.proc, lnk.type, time);
        break;
    case EVENT :
        // Getting the proc to add the event
        newVal = getRand (_eventNbr);
        // Getting the type of the event
        newVal2 = getRand (_eventTypeNbr);
        addEvent (newVal, newVal2, time);
        break;
    case INCCPT :
        newVal = getRand (_counterNbr);
        // Always generate 1 counter per proc
        incCpt (newVal, 1, time);
        break;
    case DECCPT :
        newVal = getRand (_counterNbr);
        // Always generate 1 counter per proc
        decCpt (newVal, 1, time);
        break;
    default :
        break;
    }

}

my_link Generator::getLink (int pos){
    my_link ret;
    QList<my_link>::iterator it = _startedLink.begin ();
    it += pos;
    ret = *it;
    _startedLink.erase (it);
    return ret;
}


int Generator::getRand (int max){
    int ret = 10.0 * rand () / (RAND_MAX + 1.0);
    return ret%max;
}
