/*
** 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 <sstream>
#include <string>
#include <map>
#include <queue>
#include <list>
/* -- */
#include <otf.h>
/* -- */
#include "common/Errors.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include "parser/OTFParser/ParserDefinitionOTF.hpp"
#include "parser/OTFParser/mt_ParserEventOTF.hpp"
#include "parser/OTFParser/OTFTraceBuilderThread.hpp"
/* -- */
using namespace std;

#define VITE_OTF_UNKNOWN_NOOP          "NoOp event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_COLLOP        "CollOp event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_BEGINCOLLOP   "BeginCollOp event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_ENDCOLLOP     "EndCollOp event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_EVENTCOMMENT  "EventComment event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_FILEOPERATION "FileOperation event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_BEGINFILEOP   "BeginFileOp event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_ENDFILEOP     "EndFileOp event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_RMAPUT        "RMAPut event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_RMAPUTRE      "RMAPutRe event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_RMAGET        "RMAGet event is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_RMAEND        "RMAEnd event is actually not handled in ViTE"


uint64_t mt_ParserEventOTF::_cur_time = 0;
uint64_t mt_ParserEventOTF::_min_time = 0;
uint64_t mt_ParserEventOTF::_max_time = 0;

uint64_t mt_ParserEventOTF::_n_read=0;
OTF_Trace_builder_struct* mt_ParserEventOTF::_tb_struct=NULL;


static mt_ParserEventOTF* my_address=NULL;

mt_ParserEventOTF::mt_ParserEventOTF() : _handlers(OTF_HandlerArray_open()) {
my_address=this;
}

mt_ParserEventOTF::~mt_ParserEventOTF() {
    OTF_HandlerArray_close(_handlers);
   // _containers.clear();
}

void mt_ParserEventOTF::set_handlers(Trace *t) {
    // Handlers for OTF event records
    SET_HANDLER(_handlers, handler_NoOp, t, OTF_NOOP_RECORD);
    SET_HANDLER(_handlers, handler_Enter, t, OTF_ENTER_RECORD);
    SET_HANDLER(_handlers, handler_Leave, t, OTF_LEAVE_RECORD);
    SET_HANDLER(_handlers, handler_SendMsg, t, OTF_SEND_RECORD);
    SET_HANDLER(_handlers, handler_RecvMsg, t, OTF_RECEIVE_RECORD);
    SET_HANDLER(_handlers, handler_Counter, t, OTF_COUNTER_RECORD);
    SET_HANDLER(_handlers, handler_CollectiveOperation, t, OTF_COLLOP_RECORD);
    SET_HANDLER(_handlers, handler_BeginCollectiveOperation, t, OTF_BEGINCOLLOP_RECORD);
    SET_HANDLER(_handlers, handler_EndCollectiveOperation, t, OTF_ENDCOLLOP_RECORD);
    SET_HANDLER(_handlers, handler_EventComment, t, OTF_EVENTCOMMENT_RECORD);
    SET_HANDLER(_handlers, handler_BeginProcess, t, OTF_BEGINPROCESS_RECORD);
    SET_HANDLER(_handlers, handler_EndProcess, t, OTF_ENDPROCESS_RECORD);
    SET_HANDLER(_handlers, handler_FileOperation, t, OTF_FILEOPERATION_RECORD);
    SET_HANDLER(_handlers, handler_BeginFileOperation, t, OTF_BEGINFILEOP_RECORD);
    SET_HANDLER(_handlers, handler_EndFileOperation, t, OTF_ENDFILEOP_RECORD);
    SET_HANDLER(_handlers, handler_RMAPut, t, OTF_RMAPUT_RECORD);
    SET_HANDLER(_handlers, handler_RMAPutRemoteEnd, t, OTF_RMAPUTRE_RECORD);
    SET_HANDLER(_handlers, handler_RMAGet, t, OTF_RMAGET_RECORD);
    SET_HANDLER(_handlers, handler_RMAEnd, t, OTF_RMAEND_RECORD);

    // Handlers for OTF marker records
    SET_HANDLER(_handlers, handler_DefMarker, t, OTF_DEFMARKER_RECORD);
    SET_HANDLER(_handlers, handler_Marker,    t, OTF_MARKER_RECORD);
}


int mt_ParserEventOTF::handler_BeginProcess( void* trace,
                                          uint64_t time,
                                          uint32_t process,
                                          OTF_KeyValueList */*list*/ )
{
    _tb_struct[_n_read].trace=trace;
    _tb_struct[_n_read].time=time;
    _tb_struct[_n_read].process=process;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_begin_process;
    _n_read++;

   //emit my_address->signal_handler_begin_process(trace,time,process);

    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_EndProcess( void* userData,
                                        uint64_t time,
                                        uint32_t proc_id,
                                        OTF_KeyValueList */*list*/ )
{
    _tb_struct[_n_read].trace=userData;
    _tb_struct[_n_read].time=time;
    _tb_struct[_n_read].process=proc_id;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_end_process;
    _n_read++;

    //emit my_address->signal_handler_end_process (trace,time, proc_id);
    return OTF_RETURN_OK;
}


int mt_ParserEventOTF::handler_Enter( void* userData,
                                   uint64_t time,
                                   uint32_t func_id,
                                   uint32_t proc_id,
                                   uint32_t /*source*/,
                                   OTF_KeyValueList */*list*/ )
{
    _tb_struct[_n_read].trace=userData;
    _tb_struct[_n_read].time=time;
    _tb_struct[_n_read].process=func_id;
    _tb_struct[_n_read].process2=proc_id;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_enter;
    _n_read++;
  // emit my_address->signal_handler_enter(trace, time, func_id,proc_id,0);
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_Leave( void* /*userData*/,
                                   uint64_t /*time*/,
                                   uint32_t /*func_id*/,
                                   uint32_t /*proc_id*/,
                                   uint32_t /*source*/,
                                   OTF_KeyValueList * /*list*/ )
{
 _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_leave;
    return OTF_RETURN_OK;
}
int mt_ParserEventOTF::handler_Counter( void* userData,
                                     uint64_t time,
                                     uint32_t proc_id,
                                     uint32_t counter_id,
                                     uint64_t value,
                                     OTF_KeyValueList */*list */)
{
    _tb_struct[_n_read].trace=userData;
    _tb_struct[_n_read].time=time;
    _tb_struct[_n_read].process=proc_id;
    _tb_struct[_n_read].process2=counter_id;
    _tb_struct[_n_read].value=value;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_counter;
    _n_read++;
  // emit my_address->signal_handler_counter(trace, time, proc_id,counter_id,value);
    return OTF_RETURN_OK;
}


int mt_ParserEventOTF::handler_DefMarker(void *,
                                      uint32_t stream,
                                      uint32_t id,
                                      const char *name,
                                      uint32_t type,
                                      OTF_KeyValueList */*list*/ )
{
    _tb_struct[_n_read].process=stream;
    _tb_struct[_n_read].process2=id;
    _tb_struct[_n_read].text=name;
    _tb_struct[_n_read].type=type;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_defmarker;
    _n_read++;
   //emit my_address->signal_handler_defmarker(NULL, stream, id,name,type);
    return OTF_RETURN_OK;
}


int mt_ParserEventOTF::handler_Marker(void *userData,
                                   uint64_t time,
                                   uint32_t proc_id,
                                   uint32_t id,
                                   const char *text,
                                   OTF_KeyValueList */*list*/ )
{
    _tb_struct[_n_read].trace=userData;
    _tb_struct[_n_read].time=time;
    _tb_struct[_n_read].process=proc_id;
    _tb_struct[_n_read].process2=id;
    _tb_struct[_n_read].text=text;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_marker;
    _n_read++;
  // emit my_address->signal_handler_marker(trace, time, proc_id,id,text);
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_SendMsg( void* userData,
                                     uint64_t time,
                                     uint32_t sender,
                                     uint32_t receiver,
                                     uint32_t group,
                                     uint32_t type,
                                     uint32_t length,
                                     uint32_t source,
                                     OTF_KeyValueList */*list*/ )
{
    // We define the LinkType if not exist and we store the time and other fields
    _tb_struct[_n_read].trace=userData;
    _tb_struct[_n_read].time=time;
    _tb_struct[_n_read].process=sender;
    _tb_struct[_n_read].process2=receiver;
    _tb_struct[_n_read].group=group;
    _tb_struct[_n_read].type=type;
    _tb_struct[_n_read].length=length;
    _tb_struct[_n_read].source=source;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_send_message;
    _n_read++;
//emit my_address->signal_handler_send_message(trace, time, sender, receiver, group, type,length, source);
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_RecvMsg( void* userData,
                                     uint64_t time,
                                     uint32_t receiver,
                                     uint32_t sender,
                                     uint32_t group,
                                     uint32_t type,
                                     uint32_t length,
                                     uint32_t source,
                                     OTF_KeyValueList */*list*/ )
{
    _tb_struct[_n_read].trace=userData;
    _tb_struct[_n_read].time=time;
    _tb_struct[_n_read].process=receiver;
    _tb_struct[_n_read].process2=sender;
    _tb_struct[_n_read].group=group;
    _tb_struct[_n_read].type=type;
    _tb_struct[_n_read].length=length;
    _tb_struct[_n_read].source=source;
    _tb_struct[_n_read].func=OTFTraceBuilderThread::handler_receive_message;
    _n_read++;
//emit my_address->signal_handler_receive_message(trace, time, receiver, sender, group, type,length, source);
    return OTF_RETURN_OK;
}





//
// Start definition of handlers for OTF event records
//
int mt_ParserEventOTF::handler_NoOp( void* /*userData*/,
                                  uint64_t /*time*/,
                                  uint32_t /*process*/,
                                  OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_NOOP, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}



int mt_ParserEventOTF::handler_CollectiveOperation( void* /*userData*/,
                                                 uint64_t /*time*/,
                                                 uint32_t /*process*/,
                                                 uint32_t /*collective*/,
                                                 uint32_t /*procGroup*/,
                                                 uint32_t /*rootProc*/,
                                                 uint32_t /*sent*/,
                                                 uint32_t /*received*/,
                                                 uint64_t /*duration*/,
                                                 uint32_t /*source*/,
                                                 OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_COLLOP, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_BeginCollectiveOperation( void* /*userData*/,
                                                      uint64_t /*time*/,
                                                      uint32_t /*process*/,
                                                      uint32_t /*collOp*/,
                                                      uint64_t /*matchingId*/,
                                                      uint32_t /*procGroup*/,
                                                      uint32_t /*rootProc*/,
                                                      uint64_t /*sent*/,
                                                      uint64_t /*received*/,
                                                      uint32_t /*scltoken*/,
                                                      OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_BEGINCOLLOP, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_EndCollectiveOperation( void* /*userData*/,
                                                    uint64_t /*time*/,
                                                    uint32_t /*process*/,
                                                    uint64_t /*matchingId*/,
                                                    OTF_KeyValueList * /*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_ENDCOLLOP, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_EventComment( void* /*userData*/,
                                          uint64_t /*time*/,
                                          uint32_t /*process*/,
                                          const char* /*comment*/,
                                          OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_EVENTCOMMENT, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}



int mt_ParserEventOTF::handler_FileOperation( void* /*userData*/,
                                           uint64_t /*time*/,
                                           uint32_t /*fileid*/,
                                           uint32_t /*process*/,
                                           uint64_t /*handleid*/,
                                           uint32_t /*operation*/,
                                           uint64_t /*bytes*/,
                                           uint64_t /*duration*/,
                                           uint32_t /*source*/,
                                           OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_FILEOPERATION, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_BeginFileOperation( void* /*userData*/,
                                                uint64_t /*time*/,
                                                uint32_t /*process*/,
                                                uint64_t /*matchingId*/,
                                                uint32_t /*scltoken*/,
                                                OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_BEGINFILEOP, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_EndFileOperation( void* /*userData*/,
                                              uint64_t /*time*/,
                                              uint32_t /*process*/,
                                              uint32_t /*fileid*/,
                                              uint64_t /*matchingId*/,
                                              uint64_t /*handleId*/,
                                              uint32_t /*operation*/,
                                              uint64_t /*bytes*/,
                                              uint32_t /*scltoken*/,
                                              OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_ENDFILEOP, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_RMAPut( void* /*userData*/,
                                    uint64_t /*time*/,
                                    uint32_t /*process*/,
                                    uint32_t /*origin*/,
                                    uint32_t /*target*/,
                                    uint32_t /*communicator*/,
                                    uint32_t /*tag*/,
                                    uint64_t /*bytes*/,
                                    uint32_t /*source*/,
                                    OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_RMAPUT, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_RMAPutRemoteEnd( void* /*userData*/,
                                             uint64_t /*time*/,
                                             uint32_t /*process*/,
                                             uint32_t /*origin*/,
                                             uint32_t /*target*/,
                                             uint32_t /*communicator*/,
                                             uint32_t /*tag*/,
                                             uint64_t /*bytes*/,
                                             uint32_t /*source*/,
                                             OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_RMAPUTRE, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_RMAGet( void* /*userData*/,
                                    uint64_t /*time*/,
                                    uint32_t /*process*/,
                                    uint32_t /*origin*/,
                                    uint32_t /*target*/,
                                    uint32_t /*communicator*/,
                                    uint32_t /*tag*/,
                                    uint64_t /*bytes*/,
                                    uint32_t /*source*/,
                                    OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_RMAGET, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}

int mt_ParserEventOTF::handler_RMAEnd( void* /*userData*/,
                                    uint64_t /*time*/,
                                    uint32_t /*process*/,
                                    uint32_t /*remote*/,
                                    uint32_t /*communicator*/,
                                    uint32_t /*tag*/,
                                    uint32_t /*source*/,
                                    OTF_KeyValueList */*list*/ )
{
    Error::set( VITE_OTF_UNKNOWN_RMAEND, 0, Error::VITE_ERRCODE_WARNING );
    return OTF_RETURN_OK;
}


int mt_ParserEventOTF::read_events(OTF_Reader *reader, OTF_Trace_builder_struct* tb_struct, int* n_read) {
    _tb_struct=tb_struct;
    _n_read=0;
    int return_value = OTF_Reader_readEvents(reader, _handlers);
    OTF_Reader_eventTimeProgress(reader, &mt_ParserEventOTF::_min_time, &mt_ParserEventOTF::_cur_time, &mt_ParserEventOTF::_max_time);
    *n_read=_n_read;
    return return_value;
}

int mt_ParserEventOTF::read_markers(OTF_Reader *reader, OTF_Trace_builder_struct* tb_struct, int* n_read) {
 _tb_struct=tb_struct;
    _n_read=0;
    int return_value = OTF_Reader_readMarkers(reader, _handlers);
    *n_read=_n_read;
    return return_value;
}

void mt_ParserEventOTF::set_number_event_read_by_each_pass(OTF_Reader *reader, int number) {
    OTF_Reader_setRecordLimit(reader, number);
}

float mt_ParserEventOTF::get_percent_loaded() {
    if(mt_ParserEventOTF::_max_time != mt_ParserEventOTF::_min_time) {
        return (float)(mt_ParserEventOTF::_cur_time - mt_ParserEventOTF::_min_time)/(float)(mt_ParserEventOTF::_max_time-mt_ParserEventOTF::_min_time);
    }
    else {
        return 0.0f;
    }
}
