/*
** This file is part of the ViTE project.
**
** This software is governed by the CeCILL-A license under French law
** and abiding by the rules of distribution of free software. You can
** use, modify and/or redistribute the software under the terms of the
** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following
** URL: "http://www.cecill.info".
** 
** As a counterpart to the access to the source code and rights to copy,
** modify and redistribute granted by the license, users are provided
** only with a limited warranty and the software's author, the holder of
** the economic rights, and the successive licensors have only limited
** liability.
** 
** In this respect, the user's attention is drawn to the risks associated
** with loading, using, modifying and/or developing or reproducing the
** software by the user in light of its specific status of free software,
** that may mean that it is complicated to manipulate, and that also
** therefore means that it is reserved for developers and experienced
** professionals having in-depth computer knowledge. Users are therefore
** encouraged to load and test the software's suitability as regards
** their requirements in conditions enabling the security of their
** systems and/or data to be ensured and, more generally, to use and
** operate it in the same conditions as regards security.
** 
** The fact that you are presently reading this means that you have had
** knowledge of the CeCILL-A license and that you accept its terms.
**
**
** ViTE developers are (for version 0.* to 1.0):
**
**        - COULOMB Kevin
**        - FAVERGE Mathieu
**        - JAZEIX Johnny
**        - LAGRASSE Olivier
**        - MARCOUEILLE Jule
**        - NOISETTE Pascal
**        - REDONDY Arthur
**        - VUCHENER Clément 
**
*/

#include <iostream>
#include <fstream>
#include <string>
#include <map>
/* -- */
#include <otf.h>
/* -- */
#include <QMetaType>
#include <QThread>
#include <QObject>
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include "parser/Parser.hpp"
#include "parser/OTFParser/mt_ParserOTF.hpp"
#include "parser/OTFParser/ParserDefinitionOTF.hpp"
#include "parser/OTFParser/mt_ParserEventOTF.hpp"
#include "parser/OTFParser/OTFTraceBuilderThread.hpp"
/* -- */

using namespace std;

#define VITE_OTF_MAXFILES_OPEN 100

#define VITE_ERR_OTF_FILEMANAGER "Failed to open OTF File Manager\n"
#define VITE_ERR_OTF_OPENREADER  "Failed to create the OTF Reader\n"


mt_ParserOTF::mt_ParserOTF() {};
mt_ParserOTF::mt_ParserOTF(const string &filename) : Parser(filename) {}
mt_ParserOTF::~mt_ParserOTF() {};

void mt_ParserOTF::parse(Trace &trace, 
		      bool   finish_trace_after_parse) {


    ParserDefinitionOTF *parserdefinition;
    mt_ParserEventOTF      *parserevent;
    OTF_FileManager     *manager;
    QWaitCondition ended;
    QMutex mutex;
    QSemaphore freeSlots(5);
    QSemaphore linesProduced(5);
    OTFTraceBuilderThread      *parserevent2      = new OTFTraceBuilderThread(&ended, &freeSlots, &mutex);
   /* OTF_MasterControl* mc;
    mc = OTF_MasterControl_new( manager );
    OTF_MasterControl_read( mc, _file_to_parse.c_str());
    
    uint32_t streamcount = OTF_MasterControl_getCount( mc );
    
    printf("nb of streams %d\n", streamcount);*/

    OTF_Reader          *reader;
    int ret = 0;

    manager = OTF_FileManager_open(VITE_OTF_MAXFILES_OPEN);
    if(manager == NULL) {
        cerr << QObject::tr(VITE_ERR_OTF_FILEMANAGER).toStdString() << endl;
        return;
    }
    
    int n_events=10000;
    OTF_Trace_builder_struct* tb_structs= new OTF_Trace_builder_struct[n_events];
    parserevent2->connect((const QObject*)this,
                SIGNAL(build_trace(int, OTF_Trace_builder_struct*)),
                SLOT(build_trace(int, OTF_Trace_builder_struct*)));
    parserevent2->connect((const QObject*)this,
                     SIGNAL(build_finished()),
                     SLOT(build_finished()));
	QThread traceThread;
    parserevent2->moveToThread(&traceThread);
       traceThread.start();
    
    reader = OTF_Reader_open(_file_to_parse.c_str(), manager);
    if(reader == NULL) {
        cerr << QObject::tr(VITE_ERR_OTF_OPENREADER).toStdString() << endl;
        OTF_FileManager_close(manager);
        finish();

        if(finish_trace_after_parse) { // true by default
            trace.finish();
        }
        return;
    }
    
    parserdefinition = new ParserDefinitionOTF();
    parserdefinition->set_handlers(&trace);
    parserdefinition->read_definitions(reader);
    parserdefinition->create_container_types(&trace);

    //parserdefinition->print_definitions();
    
    parserevent = new mt_ParserEventOTF();
    parserevent->set_number_event_read_by_each_pass(reader, n_events);
    parserevent->set_handlers(&trace);
    int n_read=0;

    while ( ((ret = parserevent->read_events(reader, tb_structs, &n_read)) != 0) 
	    && (!_is_canceled)) {
        if(ret == -1) {
            cerr << QObject::tr("Error while reading events. Aborting").toStdString() << endl;
            break;
        }
        freeSlots.acquire(); //do not produce too fast (5 blocks max at a time)
        emit(build_trace(n_read, tb_structs));
        linesProduced.release();
        tb_structs= new OTF_Trace_builder_struct[n_events];
    }
    
    freeSlots.acquire(); //do not produce too fast (5 blocks max at a time)
    emit(build_trace(n_read, tb_structs));
    linesProduced.release();
    //tb_structs= NULL;
    
    //same thing for markers
    tb_structs= new OTF_Trace_builder_struct[n_events];
    while ( ((ret=parserevent->read_markers(reader, tb_structs,&n_read)) != 0) 
	    && (!_is_canceled)) {
	    
	    freeSlots.acquire(); //do not produce too fast (5 blocks max at a time)
         emit(build_trace(n_read, tb_structs));
        linesProduced.release();
        tb_structs= new OTF_Trace_builder_struct[n_events];
    }
    
    freeSlots.acquire(); //do not produce too fast (5 blocks max at a time)
    emit(build_trace(n_read, tb_structs));
    linesProduced.release();
    
    finish();

    if(finish_trace_after_parse) { // true by default
        trace.finish();
    }
    
    QMutexLocker locker(&mutex);
	emit build_finished();
	ended.wait(&mutex);
	locker.unlock();
    traceThread.quit();
    traceThread.wait();
    delete parserevent;
    delete parserevent2;
    delete parserdefinition;

    OTF_Reader_close(reader);
    OTF_FileManager_close(manager);
}


float mt_ParserOTF::get_percent_loaded() const {
    return mt_ParserEventOTF::get_percent_loaded();
}
