#include "trace/SerializerDispatcher.hpp"
#include "trace/SerializerWriter.hpp"
#include "trace/IntervalOfContainer.hpp"
#include <boost/thread/thread.hpp>
#include <QObject>
#include <QThread>
#include <QWaitCondition>
#include <QMutex>
#include <QSemaphore>


    SerializerDispatcher::SerializerDispatcher(){
        //we launch all threads just now


        _nb_threads=boost::thread::hardware_concurrency();
       // SerializerWriter* evt=NULL;

       launch_threads();


        //evt=&(evt_array[_uid%nb_cpus]);//&(SerializerWriter::Instance());
        /*_cond = evt->get_cond();
                _freeSlots= evt->get_sem_free();
                _itcProduced= evt->get_sem_produced();
                _thread= evt->get_thread();*/
//printf("récupérons %s %d %p %p\n",_name.to_string().c_str(),_uid%boost::thread::hardware_concurrency(), _thread, _cond);



        }
    SerializerDispatcher::~SerializerDispatcher(){}


void SerializerDispatcher::launch_threads(){

    _mutex= new QMutex();
        _killed=false;
        _previous_call=0;
        _evt_array=new SerializerWriter[_nb_threads];
                for(unsigned int i=0; i<_nb_threads; i++){

            QWaitCondition* _cond = new QWaitCondition();
            QThread* _thread= new QThread();

                    QSemaphore* _freeSlots= new QSemaphore(1);
                    QSemaphore* _itcProduced= new QSemaphore(1);

        //TODO : try to add dynamic signal/slots creation and use to avoid constant rebinding
            if(i==0){
                    /*_evt_array[i].connect((const QObject*)this,
                         SIGNAL(build_finish()),
                         SLOT(finish_build()));*/

                    _evt_array[i].connect((const QObject*)this,
                         SIGNAL(dump_on_disk(IntervalOfContainer*,  char*)),
                         SLOT(dump_on_disk(IntervalOfContainer*,  char*)));

                  _evt_array[i].connect((const QObject*)this,
                         SIGNAL(load_data(IntervalOfContainer*,  char*)),
                         SLOT(load(IntervalOfContainer*,  char*)));

                }

            new(&_evt_array[i]) SerializerWriter (_cond, _freeSlots, _itcProduced, _thread, _mutex);
                    // _evt_array[i].set_values(_cond, _freeSlots, _itcProduced, _thread);
                     _evt_array[i].moveToThread(_thread);
             _thread->start();
        }

    }
void SerializerDispatcher::kill_all_threads(){
    if(!_killed){
         for(unsigned int i=0;i<_nb_threads; i++){

            if(_evt_array[i].get_thread()!=NULL && _evt_array[i].get_thread()->isRunning()){

                _evt_array[i].connect((const QObject*)this,
                        SIGNAL(build_finish()),
                        SLOT(finish_build()));
                //locks the mutex and automatically unlocks it when going out of scope
                                QMutexLocker locker(_mutex);
                emit build_finish();
                QWaitCondition* _cond=_evt_array[i].get_cond();
                _cond->wait(_mutex);
                locker.unlock();
                _evt_array[i].get_thread()->quit();

            }
        }
        //delete _evt_array;
        _killed=true;
    }
}

void SerializerDispatcher::dump(IntervalOfContainer* itc, char* name){
        bool changed=false;
    SerializerWriter* evt=&(_evt_array[_previous_call]);
    QSemaphore* _itcProduced= evt->get_sem_produced();
    while (_itcProduced->tryAcquire(1,10)==false){
            //the connected working thread is not free
        changed=true;
        _previous_call++;
        _previous_call=_previous_call%_nb_threads;
        _itcProduced=_evt_array[_previous_call].get_sem_produced();

    }

    if(changed!=false){
            //the acquired semaphore isn't the same as the initial one, disconnect the signl and connect to the right thread

            this->disconnect(SIGNAL(dump_on_disk(IntervalOfContainer*,  char*)));
            _evt_array[_previous_call].connect((const QObject*)this,
                SIGNAL(dump_on_disk(IntervalOfContainer*, char*)),
                SLOT(dump_on_disk(IntervalOfContainer*, char*)));
                                  //  printf("changing at the end %s %d\n",_name.to_string().c_str(),slot_number);
                    //_thread=evt_array[slot_number].get_thread();
                   // _cond=evt_array[slot_number].get_cond();


    }
    itc->_loaded=false;
    emit dump_on_disk(itc, name);
    _evt_array[_previous_call].get_sem_free()->release();
}

void SerializerDispatcher::load(IntervalOfContainer* itc, char* name){

        bool changed=false;
    SerializerWriter* evt=&(_evt_array[_previous_call]);
    QSemaphore* _itcProduced= evt->get_sem_produced();
    while (_itcProduced->tryAcquire(1,10)==false){
            //the connected working thread is not free
        changed=true;
        _previous_call++;
        _previous_call=_previous_call%_nb_threads;

        _itcProduced=_evt_array[_previous_call].get_sem_produced();

    }

    if(changed!=false){
            //the acquired semaphore isn't the same as the initial one, disconnect the signl and connect to the right thread

            this->disconnect(SIGNAL(load_data(IntervalOfContainer*,  char*)));
            _evt_array[_previous_call].connect((const QObject*)this,
                SIGNAL(load_data(IntervalOfContainer*, char*)),
                SLOT(load(IntervalOfContainer*, char*)));
                    //_thread=evt_array[slot_number].get_thread();
                   // _cond=evt_array[slot_number].get_cond();


    }
    itc->_loaded=true;
    emit load_data(itc, name);
    _evt_array[_previous_call].get_sem_free()->release();
}


    void SerializerDispatcher::init(){
        _killed=false;
        launch_threads();

        }
