/*
 ** 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);
    const std::string filename = get_next_file_to_parse();
    if(filename == "")
        return ;
    /* OTF_MasterControl* mc;
     mc = OTF_MasterControl_new( manager );
     OTF_MasterControl_read( mc, filename.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(filename.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();
        }
        delete[] tb_structs;
        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();
}
