
#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();
        
        }
