/*
** 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
**
*/
/**
 *  @file ParserDefinitionOTF.cpp
 *
 *  @author Lagrasse Olivier
 *  @author Johnny Jazeix
 *  @author Mathieu Faverge
 *
 */
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
/* -- */
#include <otf.h>
/* -- */
#include "common/common.hpp"
#include "common/Errors.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include "parser/OTFParser/ParserDefinitionOTF.hpp"
/* -- */
using namespace std;

#define VITE_OTF_UNKNOWN_DEFINITIONCOMMENT     "DefinitionComment definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFATTRIBUTELIST      "DefAttributeList definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFPROCESSORGROUPATTR "DefProcessorGroupAttr definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFCOLLOP             "DefCollOp definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFCREATOR            "Defcreator definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFVERSION            "DefVersion definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFFILE               "DefFile definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFFILEGROUP          "DefFileGroup definition is actually not handled in ViTE"
#define VITE_OTF_UNKNOWN_DEFKEYVALUE           "DefKeyValue definition is actually not handled in ViTE"

map<uint32_t, Process >       ParserDefinitionOTF::_process;
map<uint32_t, ProcessGroup >  ParserDefinitionOTF::_process_group;
map<uint32_t, Function >      ParserDefinitionOTF::_function;
map<uint32_t, FunctionGroup > ParserDefinitionOTF::_function_group;
map<uint32_t, Counter >       ParserDefinitionOTF::_counter;
map<uint32_t, CounterGroup >  ParserDefinitionOTF::_counter_group;
map<uint32_t, FileSource >    ParserDefinitionOTF::_file_source;
map<uint32_t, FileLine >      ParserDefinitionOTF::_file_line;

vector<Color *>               ParserDefinitionOTF::_default_colors;

uint32_t                      ParserDefinitionOTF::_ticks_per_second;

ParserDefinitionOTF::ParserDefinitionOTF() {
    _handlers = OTF_HandlerArray_open();

    // Creation of the empty process
    Process temp_proc = {"0", 0, 0};
    ParserDefinitionOTF::_process[0] = temp_proc;

    // Creation of the empty functionGroup
    FunctionGroup temp_funcgroup = {"no", 0};
    ParserDefinitionOTF::_function_group[0] = temp_funcgroup;

    // Creation of the empty counterGroup
    CounterGroup temp_countergroup = {"no", 0};
    ParserDefinitionOTF::_counter_group[0] = temp_countergroup;

    // Create the colors
    _default_colors.push_back(new Color(1, 0, 0)); // Red
    _default_colors.push_back(new Color(0, 1, 0)); // Green
    _default_colors.push_back(new Color(0, 0, 1)); // Blue
    _default_colors.push_back(new Color(1, 1, 0)); // Yellow
    _default_colors.push_back(new Color(0, 1, 1)); // Cyan
    _default_colors.push_back(new Color(1, 0, 1)); // Magenta
    _default_colors.push_back(new Color(1, 0.5, 0)); // Orange
    _default_colors.push_back(new Color(0.5, 0, 0.5)); // Purple
    _default_colors.push_back(new Color(1, 0, 0.5)); // Orange
    _default_colors.push_back(new Color(0, 0.5, 1)); // Clear blue
}

ParserDefinitionOTF::~ParserDefinitionOTF() {
    OTF_HandlerArray_close(_handlers);

    // Free the memory used by the colors
    const size_t nb_colors = _default_colors.size();
    for(int i = 0 ; i < nb_colors ; ++ i) {
        delete _default_colors[i];
        _default_colors[i] = NULL;
    }
    _default_colors.clear();

    // \todo check if we have to delete it?
    //delete ParserDefinitionOTF::_process_group[0]._procs;

    ParserDefinitionOTF::_process.erase(ParserDefinitionOTF::_process.begin(),
                                        ParserDefinitionOTF::_process.end() );
    ParserDefinitionOTF::_process_group.erase(ParserDefinitionOTF::_process_group.begin(),
                                              ParserDefinitionOTF::_process_group.end());
    ParserDefinitionOTF::_function.erase(ParserDefinitionOTF::_function.begin(),
                                         ParserDefinitionOTF::_function.end());
    ParserDefinitionOTF::_function_group.erase(ParserDefinitionOTF::_function_group.begin(),
                                               ParserDefinitionOTF::_function_group.end());
    ParserDefinitionOTF::_counter.erase(ParserDefinitionOTF::_counter.begin(),
                                        ParserDefinitionOTF::_counter.end());
    ParserDefinitionOTF::_counter_group.erase(ParserDefinitionOTF::_counter_group.begin(),
                                              ParserDefinitionOTF::_counter_group.end());
}

void ParserDefinitionOTF::set_handlers(Trace *t) {
    SET_HANDLER(_handlers, handler_DefinitionComment,          t, OTF_DEFINITIONCOMMENT_RECORD);
    SET_HANDLER(_handlers, handler_DefTimerResolution,         t, OTF_DEFTIMERRESOLUTION_RECORD);
    SET_HANDLER(_handlers, handler_DefProcess,                 t, OTF_DEFPROCESS_RECORD);
    SET_HANDLER(_handlers, handler_DefProcessGroup,            t, OTF_DEFPROCESSGROUP_RECORD);
    SET_HANDLER(_handlers, handler_DefAttributeList,           t, OTF_DEFATTRLIST_RECORD);
    SET_HANDLER(_handlers, handler_DefProcessOrGroupAttribute, t, OTF_DEFPROCESSORGROUPATTR_RECORD);
    SET_HANDLER(_handlers, handler_DefFunction,                t, OTF_DEFFUNCTION_RECORD);
    SET_HANDLER(_handlers, handler_DefFunctionGroup,           t, OTF_DEFFUNCTIONGROUP_RECORD);
    SET_HANDLER(_handlers, handler_DefCollectiveOperation,     t, OTF_DEFCOLLOP_RECORD);
    SET_HANDLER(_handlers, handler_DefCounter,                 t, OTF_DEFCOUNTER_RECORD);
    SET_HANDLER(_handlers, handler_DefCounterGroup,            t, OTF_DEFCOUNTERGROUP_RECORD);
    SET_HANDLER(_handlers, handler_DefScl,                     t, OTF_DEFSCL_RECORD);
    SET_HANDLER(_handlers, handler_DefSclFile,                 t, OTF_DEFSCLFILE_RECORD);
    SET_HANDLER(_handlers, handler_DefCreator,                 t, OTF_DEFCREATOR_RECORD);
    SET_HANDLER(_handlers, handler_DefVersion,                 t, OTF_DEFVERSION_RECORD);
    SET_HANDLER(_handlers, handler_DefFile,                    t, OTF_DEFFILE_RECORD);
    SET_HANDLER(_handlers, handler_DefFileGroup,               t, OTF_DEFFILEGROUP_RECORD);
    SET_HANDLER(_handlers, handler_DefKeyValue,                t, OTF_DEFKEYVALUE_RECORD);
}

/*
 * All the definition handlers
 */
int ParserDefinitionOTF::handler_DefinitionComment(void             */*user_data */,
                                                   uint32_t          /*stream    */,
                                                   const char       */*comment   */,
                                                   OTF_KeyValueList */*list      */)
{
    Error::set( VITE_OTF_UNKNOWN_DEFINITIONCOMMENT, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefTimerResolution(void             */*user_data*/,
                                                    uint32_t          /*stream   */,
                                                    uint32_t          ticks_per_sec,
                                                    OTF_KeyValueList */*list      */)
{
    ParserDefinitionOTF::_ticks_per_second = ticks_per_sec;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefProcess(void             */*userData  */,
                                            uint32_t          stream,
                                            uint32_t          proc_id,
                                            const char       *name,
                                            uint32_t          parent,
                                            OTF_KeyValueList */*list      */)
{
    Process temp = {name, parent, stream};
    ParserDefinitionOTF::_process[proc_id] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefProcessGroup(void             */*userData  */,
                                                 uint32_t          stream,
                                                 uint32_t          procGroup,
                                                 const char       *name,
                                                 uint32_t          numberOfProcs,
                                                 const uint32_t   *procs,
                                                 OTF_KeyValueList */*list      */)
{
    ProcessGroup temp = {name, numberOfProcs, stream, (uint32_t *)procs};
    ParserDefinitionOTF::_process_group[procGroup] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefAttributeList(void             */*userData  */,
                                                  uint32_t          /*stream    */,
                                                  uint32_t          /*attr_token*/,
                                                  uint32_t          /*num       */,
                                                  OTF_ATTR_TYPE    */*array     */,
                                                  OTF_KeyValueList */*list      */ )
{
    Error::set( VITE_OTF_UNKNOWN_DEFATTRIBUTELIST, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefProcessOrGroupAttribute(void             */*userData  */,
                                                            uint32_t          /*stream    */,
                                                            uint32_t          /*proc_token*/,
                                                            uint32_t          /*attr_token*/,
                                                            OTF_KeyValueList */*list      */)
{
    Error::set( VITE_OTF_UNKNOWN_DEFPROCESSORGROUPATTR, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefFunction(void             */*user_data*/,
                                             uint32_t          stream,
                                             uint32_t          func_id,
                                             const char       *name,
                                             uint32_t          funcGroup,
                                             uint32_t          source,
                                             OTF_KeyValueList */*list     */ )
{
    Function temp = {name, stream, funcGroup, source, false};
    ParserDefinitionOTF::_function[func_id] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefFunctionGroup(void             */*user_data*/,
                                                  uint32_t          stream,
                                                  uint32_t          func_group_id,
                                                  const char       *name,
                                                  OTF_KeyValueList */*list     */ )
{
    FunctionGroup temp = {name, stream};
    ParserDefinitionOTF::_function_group[func_group_id] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefCollectiveOperation(void             */*userData  */,
                                                        uint32_t          /*stream    */,
                                                        uint32_t          /*collOp    */,
                                                        const char       */*name      */,
                                                        uint32_t          /*type      */,
                                                        OTF_KeyValueList */*list      */)
{
    Error::set( VITE_OTF_UNKNOWN_DEFCOLLOP, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}


int ParserDefinitionOTF::handler_DefCounter(void             */*userData  */,
                                            uint32_t          stream,
                                            uint32_t          counter_id,
                                            const char       *name,
                                            uint32_t          properties,
                                            uint32_t          countergroup,
                                            const char       *unit,
                                            OTF_KeyValueList */*list      */)
{
    Counter temp = {name, stream, properties, countergroup, unit};
    ParserDefinitionOTF::_counter[counter_id] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefCounterGroup(void             */*userData  */,
                                                 uint32_t          stream,
                                                 uint32_t          counter_group_id,
                                                 const char       *name,
                                                 OTF_KeyValueList */*list      */)
{
    CounterGroup temp = {name, stream};
    ParserDefinitionOTF::_counter_group[counter_group_id] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefScl(void             */*userData  */,
                                        uint32_t          stream,
                                        uint32_t          line_id,
                                        uint32_t          file_id,
                                        uint32_t          line_number,
                                        OTF_KeyValueList */*list      */)
{
    FileLine temp = {file_id, line_number, stream};
    ParserDefinitionOTF::_file_line[line_id] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefSclFile(void             */*userData  */,
                                            uint32_t          stream,
                                            uint32_t          file_id,
                                            const char       *name,
                                            OTF_KeyValueList */*list      */)
{
    FileSource temp = {name, stream};
    ParserDefinitionOTF::_file_source[file_id] = temp;
    return OTF_RETURN_OK;
}

int ParserDefinitionOTF::handler_DefCreator( void* userData,
                                             uint32_t stream,
                                             const char* creator,
                                             OTF_KeyValueList *list )
{
    Error::set( VITE_OTF_UNKNOWN_DEFCREATOR, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}


int ParserDefinitionOTF::handler_DefVersion( void* userData,
                                             uint32_t stream,
                                             uint8_t major,
                                             uint8_t minor,
                                             uint8_t sub,
                                             const char* string,
                                             OTF_KeyValueList *list )
{
    Error::set( VITE_OTF_UNKNOWN_DEFVERSION, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}


int ParserDefinitionOTF::handler_DefFile( void* userData,
                                          uint32_t stream,
                                          uint32_t token,
                                          const char *name,
                                          uint32_t group,
                                          OTF_KeyValueList *list )
{
    Error::set( VITE_OTF_UNKNOWN_DEFFILE, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}


int ParserDefinitionOTF::handler_DefFileGroup( void* userData,
                                               uint32_t stream,
                                               uint32_t token,
                                               const char *name,
                                               OTF_KeyValueList *list )
{
    Error::set( VITE_OTF_UNKNOWN_DEFFILEGROUP, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}


int ParserDefinitionOTF::handler_DefKeyValue(  void* userData,
                                               uint32_t stream,
                                               uint32_t key,
                                               OTF_Type type,
                                               const char *name,
                                               const char *description,
                                               OTF_KeyValueList *list )
{
    Error::set( VITE_OTF_UNKNOWN_DEFKEYVALUE, 0, Error::VITE_ERRCODE_WARNING);
    return OTF_RETURN_OK;
}

//
// Accessors to the data structures
//
Function &ParserDefinitionOTF::get_function_by_id(const uint32_t id)
{
    return ParserDefinitionOTF::_function[id];
}

FunctionGroup ParserDefinitionOTF::get_function_group_by_id(const uint32_t id)
{
    return ParserDefinitionOTF::_function_group[id];
}

Counter ParserDefinitionOTF::get_counter_by_id(const uint32_t id)
{
    return ParserDefinitionOTF::_counter[id];
}

CounterGroup ParserDefinitionOTF::get_counter_group_by_id(const uint32_t id)
{
    return ParserDefinitionOTF::_counter_group[id];
}

Process ParserDefinitionOTF::get_process_by_id(const uint32_t id)
{
    return ParserDefinitionOTF::_process[id];
}

ProcessGroup ParserDefinitionOTF::get_processgroup_by_id(const uint32_t id)
{
    return ParserDefinitionOTF::_process_group[id];
}

//
// Remark: This is assuming that one process can be in only one group
// that's a mistake if we follow OTF description
// We should fine a way to base our group to the parent/child relation
// and have these groups to move the process around in the trace or just
// for the communication.
//
ProcessGroup ParserDefinitionOTF::get_processgroup_by_process(const unsigned int process_id)
{
    map<uint32_t, ProcessGroup>::const_iterator it, end;
    int i;
    end = _process_group.end();
    for ( it = _process_group.begin() ; it != end; ++ it) {
        ProcessGroup proc_group_temp = (*it).second;
        for(i=0; i < proc_group_temp._numberOfProcs; i++) {
            if(proc_group_temp._procs[i] == process_id) {
                return proc_group_temp;
            }
        }
    }
    return ParserDefinitionOTF::_process_group[0];
}

FileSource ParserDefinitionOTF::get_filesource_by_id(const uint32_t id) {
    return ParserDefinitionOTF::_file_source[id];
}

FileLine ParserDefinitionOTF::get_fileline_by_id(const uint32_t id) {
    return ParserDefinitionOTF::_file_line[id];
}

uint32_t ParserDefinitionOTF::get_ticks_per_second() {
    return ParserDefinitionOTF::_ticks_per_second;
}

Color *ParserDefinitionOTF::get_color(uint32_t func_id) {
    return new Color(*_default_colors[func_id % _default_colors.size()]);
}

//
// Other public functions
//
void ParserDefinitionOTF::read_definitions(OTF_Reader *reader) {
    OTF_Reader_readDefinitions(reader, _handlers);
}

void ParserDefinitionOTF::create_container_types(Trace *t) {
    map<uint32_t, Process>::const_iterator itp, endp;
    map<uint32_t, ProcessGroup>::const_iterator itpg, endpg;
    set <uint32_t> all_process;
    set <uint32_t>::const_iterator itap, endap;
    unsigned int i;

    // We add in the processGroup '0' all the process that don't have a processGroup
    // We add all the process
    endp =  _process.end();
    for ( itp = _process.begin() ; itp != endp ; ++ itp) {
        all_process.insert((*itp).first);
    }

    // We remove process which have a parent
    endpg =  _process_group.end();
    for ( itpg = _process_group.begin() ; itpg != endpg ; ++ itpg) {
        ProcessGroup proc_group_temp = (*itpg).second;
        for(i=0 ; i < proc_group_temp._numberOfProcs; i++) {
            all_process.erase(proc_group_temp._procs[i]);
        }
    }
    // Creation of the processGroup 0
    const size_t size = all_process.size();
    ProcessGroup temp;
    temp._name = "no";
    temp._stream = 0;
    temp._numberOfProcs = size;
    temp._procs = new uint32_t[size+1];

    endap = all_process.end();
    i = 0;
    for ( itap = all_process.begin() ; itap != endap ; ++ itap, ++ i) {
        temp._procs[i] = *itap;
    }
    ParserDefinitionOTF::_process_group[0] = temp;

    // Create the container types
    endpg = _process_group.end();
    for (itpg = _process_group.begin() ; itpg != endpg ; ++ itpg) {
        ProcessGroup proc_group_temp = (*itpg).second;
        Name name_temp(proc_group_temp._name);
        map<std::string, Value *> extra_fields;
        t->define_container_type(name_temp, NULL, extra_fields);
    }
}

// Debug purposes
void ParserDefinitionOTF::print_definitions() {
    cout << "Process:" << endl;
    for (map<uint32_t, Process>::const_iterator it = _process.begin() ; it != _process.end() ; ++ it) {
        cout << "#" << (*it).first;
        Process temp = (*it).second;
        cout << " name: '" << temp._name << "' parent:'" << temp._parent << "' stream: '" << temp._stream << "'" << endl;
    }

    cout << "ProcessGroup:" << endl;
    for (map<uint32_t, ProcessGroup>::const_iterator it = _process_group.begin() ; it != _process_group.end() ; ++ it) {
        cout << "#" << (*it).first;
        ProcessGroup temp = (*it).second;
        cout << " name: '" << temp._name << "' stream: '" << temp._stream << "'" << endl << " Procs: ";
        for(unsigned int i = 0 ; i < temp._numberOfProcs ; i ++) {
            cout << temp._procs[i] << " ";
        }
        cout << endl;
    }

    cout << "Functions:" << endl;
    for (map<uint32_t, Function>::const_iterator it = _function.begin() ; it != _function.end() ; ++ it) {
        cout << "#" << (*it).first;
        Function temp = (*it).second;
        cout << " name: '" << temp._name << "' stream: '" << temp._stream << "' parent:'" << temp._func_group << "' file_source:'" << temp._file_source << "'" << endl;
    }

    cout << "Function groups:" << endl;
    for (map<uint32_t, FunctionGroup>::const_iterator it = _function_group.begin() ; it != _function_group.end() ; ++ it) {
        cout << "#" << (*it).first;
        FunctionGroup temp = (*it).second;
        cout << " name: '" << temp._name << "' stream: '" << temp._stream << "'" << endl;
    }

    cout << "Counters:" << endl;
    for (map<uint32_t, Counter>::const_iterator it = _counter.begin() ; it != _counter.end() ; ++ it) {
        cout << "#" << (*it).first;
        Counter temp = (*it).second;
        cout << " name: '" << temp._name << "' stream: '" << temp._stream << "'" << "' unit: '" << temp._unit << "'" << endl;
    }

    cout << "Counter groups:" << endl;
    for (map<uint32_t, CounterGroup>::const_iterator it = _counter_group.begin() ; it != _counter_group.end() ; ++ it) {
        cout << "#" << (*it).first;
        CounterGroup temp = (*it).second;
        cout << " name: '" << temp._name << "' stream: '" << temp._stream << "'" << endl;
    }

}
