/**
 *
 * @file src/parser/OTF2Parser/ParserDefinitionOTF2.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Francois Trahay
 * @author Johnny Jazeix
 * @author Mathieu Faverge
 *
 * @date 2024-07-17
 */
/**
 *  @file ParserDefinitionOTF2.cpp
 *
 *  @author François Trahay
 *  @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 <cassert>
/* -- */
#include <otf2/otf2.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/OTF2Parser/ParserDefinitionOTF2.hpp"
/* -- */
using namespace std;

map<OTF2_SystemTreeNodeRef, OTF2_SystemTreeNode *> ParserDefinitionOTF2::_system_tree_node;
map<OTF2_LocationGroupRef, OTF2_LocationGroup *> ParserDefinitionOTF2::_location_group;
map<OTF2_LocationRef, OTF2_Location *> ParserDefinitionOTF2::_location;

map<OTF2_MetricMemberRef, OTF2_MetricMember> ParserDefinitionOTF2::_metric_member;
map<OTF2_MetricRef, OTF2_MetricClass> ParserDefinitionOTF2::_metric_class;

map<uint32_t, OTF2_Function> ParserDefinitionOTF2::_functions;

map<uint32_t, const char *> ParserDefinitionOTF2::_strings;

map<OTF2_GroupRef, OTF2_Group *> ParserDefinitionOTF2::_groups;
map<OTF2_CommRef, OTF2_Comm *> ParserDefinitionOTF2::_comms;
struct OTF2_Comm_Locations ParserDefinitionOTF2::_comm_locations;

vector<Color *> ParserDefinitionOTF2::_default_colors;

uint64_t ParserDefinitionOTF2::_ticks_per_second = 1;
double ParserDefinitionOTF2::_first_timestamp = -1;

ParserDefinitionOTF2::ParserDefinitionOTF2(OTF2_Reader *reader) {

    _global_def_reader = OTF2_Reader_GetGlobalDefReader(reader);
    _global_def_callbacks = OTF2_GlobalDefReaderCallbacks_New();

    // 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
}

ParserDefinitionOTF2::~ParserDefinitionOTF2() {

    OTF2_GlobalDefReaderCallbacks_Delete(_global_def_callbacks);

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

    _strings.clear();

    ParserDefinitionOTF2::_location.erase(ParserDefinitionOTF2::_location.begin(),
                                          ParserDefinitionOTF2::_location.end());
    ParserDefinitionOTF2::_location_group.erase(ParserDefinitionOTF2::_location_group.begin(),
                                                ParserDefinitionOTF2::_location_group.end());
    ParserDefinitionOTF2::_system_tree_node.erase(ParserDefinitionOTF2::_system_tree_node.begin(),
                                                  ParserDefinitionOTF2::_system_tree_node.end());
    ParserDefinitionOTF2::_functions.erase(ParserDefinitionOTF2::_functions.begin(),
                                           ParserDefinitionOTF2::_functions.end());
    ParserDefinitionOTF2::_groups.erase(ParserDefinitionOTF2::_groups.begin(),
                                        ParserDefinitionOTF2::_groups.end());
    ParserDefinitionOTF2::_comms.erase(ParserDefinitionOTF2::_comms.begin(),
                                       ParserDefinitionOTF2::_comms.end());
}

void ParserDefinitionOTF2::set_handlers(Trace *t) {

    OTF2_GlobalDefReaderCallbacks_SetSystemTreeNodeCallback(_global_def_callbacks, &handler_DefSystemTreeNode);
    OTF2_GlobalDefReaderCallbacks_SetLocationCallback(_global_def_callbacks, &handler_DefLocation);
    OTF2_GlobalDefReaderCallbacks_SetLocationGroupCallback(_global_def_callbacks, handler_DefLocationGroup);
    OTF2_GlobalDefReaderCallbacks_SetStringCallback(_global_def_callbacks, &handler_DefString);
    OTF2_GlobalDefReaderCallbacks_SetClockPropertiesCallback(_global_def_callbacks, &handler_DefTimerResolution);
    OTF2_GlobalDefReaderCallbacks_SetRegionCallback(_global_def_callbacks, &handler_DefState);
    // SetGroupCallback to get containerType, stateType... ?
    OTF2_GlobalDefReaderCallbacks_SetGroupCallback(_global_def_callbacks, &handler_DefGroup);
    OTF2_GlobalDefReaderCallbacks_SetCommCallback(_global_def_callbacks, &handler_DefComm);

    OTF2_GlobalDefReaderCallbacks_SetMetricMemberCallback(_global_def_callbacks, &handler_DefMetricMember);
    OTF2_GlobalDefReaderCallbacks_SetMetricClassCallback(_global_def_callbacks, &handler_DefMetricClass);
    OTF2_GlobalDefReaderCallbacks_SetMetricInstanceCallback(_global_def_callbacks, &handler_DefMetricInstance);
    OTF2_GlobalDefReaderCallbacks_SetMetricClassRecorderCallback(_global_def_callbacks, &handler_DefMetricClassRecorder);

    (void)t;
}

/*
 * All the definition handlers
 */

#if OTF2_VERSION_MAJOR >= 3
OTF2_CallbackCode ParserDefinitionOTF2::handler_DefTimerResolution(void *, uint64_t timer_resolution, uint64_t /*global_offset*/, uint64_t /*trace_length*/, uint64_t /*realtimeTimestamp*/) {
#else
OTF2_CallbackCode ParserDefinitionOTF2::handler_DefTimerResolution(void *, uint64_t timer_resolution, uint64_t /*global_offset*/, uint64_t /*trace_length*/) {
#endif
    ParserDefinitionOTF2::_ticks_per_second = timer_resolution;
    return OTF2_CALLBACK_SUCCESS;
}

double ParserDefinitionOTF2::get_timestamp(OTF2_TimeStamp ts) {
    if (_first_timestamp < 0) {
        _first_timestamp = (double)ts / (double)ParserDefinitionOTF2::get_ticks_per_second();
    }
    return (double)ts / (double)ParserDefinitionOTF2::get_ticks_per_second() - _first_timestamp;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefString(void * /*userData  */,
                                                          OTF2_StringRef id,
                                                          const char *value) {
    _strings[id] = value;
    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefSystemTreeNode(void * /*userData  */,
                                                                  OTF2_SystemTreeNodeRef tree_node_id,
                                                                  OTF2_StringRef name_id,
                                                                  OTF2_StringRef /*class_id*/,
                                                                  OTF2_SystemTreeNodeRef parent_node_id) {
    OTF2_SystemTreeNode *temp = new OTF2_SystemTreeNode();
    temp->_node_id = tree_node_id;
    temp->_name_id = name_id;
    temp->_parent = parent_node_id;
    generate_string_id(temp);

    struct OTF2_SystemTreeNode *tree_node = ParserDefinitionOTF2::get_system_tree_node_by_id(parent_node_id);
    if (tree_node != NULL) {
        tree_node->_child_nodes[tree_node_id] = temp;
    }
    _system_tree_node[tree_node_id] = temp;

#if defined(OTF2_DEBUG)
    cout << "DefSystemTreeNode(node_id=" << tree_node_id << ", name=" << temp->_id_string << ", parent=";
    if (tree_node) {
        cout << tree_node->_id_string;
    }
    else {
        cout << "(none)";
    }
    cout << "\n";
#endif

    return OTF2_CALLBACK_SUCCESS;
}

#if OTF2_VERSION_MAJOR >= 3
OTF2_CallbackCode ParserDefinitionOTF2::handler_DefLocationGroup(void * /*userdata*/,
                                                                 OTF2_LocationGroupRef location_group_identifier,
                                                                 OTF2_StringRef name,
                                                                 OTF2_LocationGroupType /*type*/,
                                                                 OTF2_SystemTreeNodeRef system_tree_parent,
                                                                 OTF2_LocationGroupRef creatingLocationGroup) {
#else
OTF2_CallbackCode ParserDefinitionOTF2::handler_DefLocationGroup(void * /*userdata*/,
                                                                 OTF2_LocationGroupRef location_group_identifier,
                                                                 OTF2_StringRef name,
                                                                 OTF2_LocationGroupType /*type*/,
                                                                 OTF2_SystemTreeNodeRef system_tree_parent) {
#endif
    OTF2_LocationGroup *temp = new OTF2_LocationGroup();
    temp->_group_id = location_group_identifier;
    temp->_name_id = name;
    temp->_node_id = system_tree_parent;
    generate_string_id(temp);

    struct OTF2_SystemTreeNode *tree_node = ParserDefinitionOTF2::get_system_tree_node_by_id(system_tree_parent);
    if (tree_node != NULL) {
        tree_node->_location_group[location_group_identifier] = temp;
    }
    _location_group[location_group_identifier] = temp;

#if defined(OTF2_DEBUG)
    cout << "DefLocationGroup(group_id=" << location_group_identifier << ", name=" << temp->_id_string << ", parent=";
    if (tree_node)
        cout << tree_node->_id_string;
    else
        cout << "(none)";
    cout << "\n";
#endif // defined(OTF2_DEBUG)
    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefLocation(void *userData,
                                                            OTF2_LocationRef locationIdentifier,
                                                            OTF2_StringRef name_id,
                                                            OTF2_LocationType /*location_type*/,
                                                            uint64_t numberOfEvents,
                                                            OTF2_LocationGroupRef locationGroup) {
    OTF2_Reader *reader = (OTF2_Reader *)userData;
    OTF2_EvtReader *evt_reader = OTF2_Reader_GetEvtReader(reader, locationIdentifier);
    OTF2_DefReader *def_reader = OTF2_Reader_GetDefReader(reader, locationIdentifier);
    uint64_t definitions_read = 0;
    OTF2_Reader_ReadAllLocalDefinitions(reader, def_reader, &definitions_read);

    OTF2_Location *temp = new OTF2_Location();
    temp->_location_id = locationIdentifier;
    temp->_name_id = name_id;
    temp->_group_id = locationGroup;
    temp->_number_of_events = numberOfEvents;
    generate_string_id(temp);

    struct OTF2_LocationGroup *location_group = ParserDefinitionOTF2::get_location_group_by_id(locationGroup);
    if (location_group != NULL) {
        location_group->_location[locationIdentifier] = temp;
    }
    _location[locationIdentifier] = temp;

#if defined(OTF2_DEBUG)
    cout << "DefLocation(id=" << locationIdentifier << ", name=" << temp->_id_string << ", parent=";
    if (location_group)
        cout << location_group->_id_string;
    else
        cout << "(none)";
    cout << "\n";
#endif // defined(OTF2_DEBUG)

    (void)evt_reader;
    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefState(void * /*userData*/,
                                                         OTF2_RegionRef self,
                                                         OTF2_StringRef name,
                                                         OTF2_StringRef canonicalName,
                                                         OTF2_StringRef description,
                                                         OTF2_RegionRole regionRole,
                                                         OTF2_Paradigm paradigm,
                                                         OTF2_RegionFlag regionFlags,
                                                         OTF2_StringRef sourceFile,
                                                         uint32_t beginLineNumber,
                                                         uint32_t endLineNumber) {

    OTF2_Function temp = { name, canonicalName, description, regionRole, paradigm, regionFlags, sourceFile, beginLineNumber, endLineNumber };
    ParserDefinitionOTF2::_functions[self] = temp;
    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefGroup(void * /*userData*/,
                                                         OTF2_GroupRef group_id,
                                                         OTF2_StringRef name_id,
                                                         OTF2_GroupType type,
                                                         OTF2_Paradigm paradigm,
                                                         OTF2_GroupFlag flags,
                                                         uint32_t numberOfMembers,
                                                         const uint64_t *members) {
    if (type == OTF2_GROUP_TYPE_COMM_LOCATIONS) {
        struct OTF2_Comm_Locations *g = &ParserDefinitionOTF2::_comm_locations;
        g->_id = group_id;
        g->_name = name_id;
        g->_type = type;
        g->_paradigm = paradigm;
        g->_flags = flags;
        g->_members.resize(numberOfMembers);
        for (uint32_t i = 0; i < numberOfMembers; i++) {
            g->_members[i] = members[i];
        }
    }
    else if (type == OTF2_GROUP_TYPE_COMM_GROUP) {
        struct OTF2_Group *g = new struct OTF2_Group();
        g->_id = group_id;
        g->_name = name_id;
        g->_type = type;
        g->_paradigm = paradigm;
        g->_flags = flags;
        g->_members.resize(numberOfMembers);
        for (uint32_t i = 0; i < numberOfMembers; i++) {
            g->_members[i] = members[i];
        }
        ParserDefinitionOTF2::_groups[group_id] = g;
    }
    else {
        switch (type) {
        case OTF2_GROUP_TYPE_UNKNOWN:
            break;
        case OTF2_GROUP_TYPE_LOCATIONS:
            cout << "New location group: group_id " << group_id << ", name_id" << name_id << endl;
            break;
        case OTF2_GROUP_TYPE_REGIONS:
            cout << "New region group: group_id " << group_id << ", name_id" << name_id << endl;
            break;
        case OTF2_GROUP_TYPE_METRIC:
            cout << "New metric group: group_id " << group_id << ", name_id" << name_id << endl;
            break;
        case OTF2_GROUP_TYPE_COMM_SELF:
            cout << "New comm self: group_id " << group_id << ", name_id" << name_id << endl;
            break;
        default:
            cout << "type " << type << " not yet handled" << endl;
        }
        cout << "\tNot yet implemented\n";
    }
    return OTF2_CALLBACK_SUCCESS;
}

#if OTF2_VERSION_MAJOR >= 3
OTF2_CallbackCode ParserDefinitionOTF2::handler_DefComm(void * /*userData*/,
                                                        OTF2_CommRef self,
                                                        OTF2_StringRef name,
                                                        OTF2_GroupRef group,
                                                        OTF2_CommRef parent,
                                                        OTF2_CommFlag flags) {
#else
OTF2_CallbackCode ParserDefinitionOTF2::handler_DefComm(void * /*userData*/,
                                                        OTF2_CommRef self,
                                                        OTF2_StringRef name,
                                                        OTF2_GroupRef group,
                                                        OTF2_CommRef parent) {
#endif
    // defines an MPI communicator
    struct OTF2_Comm *c = new struct OTF2_Comm();
    c->_id = self;
    c->_name = name;
    c->_group = group;
    c->_parent = parent;
    ParserDefinitionOTF2::_comms[self] = c;
    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefMetricMember(void * /*userData*/,
                                                                OTF2_MetricMemberRef self,
                                                                OTF2_StringRef name,
                                                                OTF2_StringRef description,
                                                                OTF2_MetricType metricType,
                                                                OTF2_MetricMode metricMode,
                                                                OTF2_Type valueType,
                                                                OTF2_Base base,
                                                                int64_t exponent,
                                                                OTF2_StringRef unit) {
    OTF2_MetricMember temp;
    temp._id = self;
    temp._name = name;
    temp._description = description;
    temp._metricType = metricType;
    temp._metricMode = metricMode;
    temp._valueType = valueType;
    temp._base = base;
    temp._exponent = exponent;
    temp._unit = unit;

    ParserDefinitionOTF2::_metric_member[self] = temp;

    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefMetricClass(void * /*userData*/,
                                                               OTF2_MetricRef self,
                                                               uint8_t numberOfMetrics,
                                                               const OTF2_MetricMemberRef *metricMembers,
                                                               OTF2_MetricOccurrence metricOccurrence,
                                                               OTF2_RecorderKind recorderKind) {
    OTF2_MetricClass temp;
    temp._id = self;
    temp._metricOccurrence = metricOccurrence;
    temp._recorderKind = recorderKind;

    for (int i = 0; i < numberOfMetrics; i++) {
        temp._metricMembers.push_back(metricMembers[i]);
    }

    ParserDefinitionOTF2::_metric_class[self] = temp;
    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefMetricInstance(void * /*userData*/,
                                                                  OTF2_MetricRef /*self*/,
                                                                  OTF2_MetricRef /*metricClass*/,
                                                                  OTF2_LocationRef /*recorder*/,
                                                                  OTF2_MetricScope /*metricScope*/,
                                                                  uint64_t /*scope*/) {
    cout << __FUNCTION__ << " not implemented\n";
    return OTF2_CALLBACK_SUCCESS;
}

OTF2_CallbackCode ParserDefinitionOTF2::handler_DefMetricClassRecorder(void * /*userData*/,
                                                                       OTF2_MetricRef /*metric*/,
                                                                       OTF2_LocationRef /*recorder*/) {
    cout << __FUNCTION__ << " not implemented\n";
    return OTF2_CALLBACK_SUCCESS;
}

//
// Accessors to the data structures
//

OTF2_MetricClass ParserDefinitionOTF2::get_metric_class(const OTF2_MetricRef id) {
    return ParserDefinitionOTF2::_metric_class[id];
}

OTF2_MetricMember ParserDefinitionOTF2::get_metric_member(const OTF2_MetricClass metric_class, int index) {
    OTF2_MetricMemberRef ref = metric_class._metricMembers[index];
    return ParserDefinitionOTF2::_metric_member[ref];
}

OTF2_SystemTreeNode *ParserDefinitionOTF2::get_system_tree_node_by_id(const OTF2_SystemTreeNodeRef id) {
    return ParserDefinitionOTF2::_system_tree_node[id];
}

OTF2_LocationGroup *ParserDefinitionOTF2::get_location_group_by_id(const OTF2_LocationGroupRef id) {
    return ParserDefinitionOTF2::_location_group[id];
}

OTF2_Location *ParserDefinitionOTF2::get_location_by_id(const OTF2_LocationRef id) {
    return ParserDefinitionOTF2::_location[id];
}

OTF2_Location *ParserDefinitionOTF2::get_location_in_communicator(const OTF2_CommRef comm,
                                                                  uint32_t rank) {
    struct OTF2_Comm *c = _comms[comm];
    if (!c) {
        cerr << "Warning: cannot find communicator " << comm << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_by_id(rank);
    }

    struct OTF2_Group *g = _groups[c->_group];
    if (!g) {
        cerr << "Warning: cannot find rank " << rank << " in communicator " << comm << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_by_id(rank);
    }

    if (rank > g->_members.size()) {
        cerr << "Warning: searching for rank " << rank << " in communicator " << comm << ", but comm_size=" << g->_members.size() << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_by_id(rank);
    }

    int mpi_rank = g->_members[rank];
    struct OTF2_LocationGroup *lg = _location_group[mpi_rank];
    if (!lg) {
        cerr << "Warning: cannot find location group " << mpi_rank << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_by_id(rank);
    }
    OTF2_LocationRef l = _comm_locations._members[mpi_rank];
    return get_location_by_id(l);
}

OTF2_LocationGroup *ParserDefinitionOTF2::get_location_group_in_communicator(const OTF2_CommRef comm,
                                                                             uint32_t rank) {
    struct OTF2_Comm *c = _comms[comm];
    if (!c) {
        cerr << "Warning: cannot find communicator " << comm << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_group_by_id(rank);
    }

    struct OTF2_Group *g = _groups[c->_group];
    if (!g) {
        cerr << "Warning: cannot find rank " << rank << " in communicator " << comm << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_group_by_id(rank);
    }

    if (rank > g->_members.size()) {
        cerr << "Warning: searching for rank " << rank << " in communicator " << comm << ", but comm_size=" << g->_members.size() << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_group_by_id(rank);
    }

    int mpi_rank = g->_members[rank];
    struct OTF2_LocationGroup *lg = _location_group[mpi_rank];
    if (!lg) {
        cerr << "Warning: cannot find location group " << mpi_rank << endl;
        // This is probably because the tracing tool did not support communicators
        // Let's assume that rank is actually a locationRef
        return get_location_group_by_id(rank);
    }
    return lg;
}

OTF2_Function ParserDefinitionOTF2::get_function_by_id(const uint32_t id) {
    return ParserDefinitionOTF2::_functions[id];
}

const char *ParserDefinitionOTF2::get_string_by_id(uint32_t id) {
    return ParserDefinitionOTF2::_strings[id];
}

uint64_t ParserDefinitionOTF2::get_ticks_per_second() {
    return ParserDefinitionOTF2::_ticks_per_second;
}

Color *ParserDefinitionOTF2::get_color(uint32_t func_id) {
    return new Color(*_default_colors[func_id % NB_COLORS]);
}

//
// Other public functions
//
void ParserDefinitionOTF2::read_definitions(OTF2_Reader *reader) {
    OTF2_Reader_RegisterGlobalDefCallbacks(reader, _global_def_reader, _global_def_callbacks, reader);
    uint64_t definitions_read = 0;
    OTF2_Reader_ReadAllGlobalDefinitions(reader, _global_def_reader, &definitions_read);
}

void ParserDefinitionOTF2::generate_string_id(OTF2_Location *l) {
    const char *s = get_string_by_id(l->_name_id);
    l->_id_string = string(s ? s : "") + "_" + std::to_string(l->_location_id);
}

void ParserDefinitionOTF2::generate_string_id(OTF2_LocationGroup *lg) {
    const char *s = get_string_by_id(lg->_name_id);
    lg->_id_string = string(s ? s : "") + "_" + std::to_string(lg->_group_id);
}

void ParserDefinitionOTF2::generate_string_id(OTF2_SystemTreeNode *n) {
    const char *s = get_string_by_id(n->_name_id);
    n->_id_string = string(s ? s : "") + "_" + std::to_string(n->_node_id);
}

string ParserDefinitionOTF2::get_string_id(OTF2_Location *l) {
    return l->_id_string;
}

string ParserDefinitionOTF2::get_string_id(OTF2_LocationGroup *lg) {
    return lg->_id_string;
}

string ParserDefinitionOTF2::get_string_id(OTF2_SystemTreeNode *n) {
    return n->_id_string;
}

ContainerType *define_container_type(Trace *t, const char *name, const char *parent_name) {
    ContainerType *container_type = t->search_container_type(String(name));
    if (container_type == NULL) {
        // first type we create this kind of container. Let's define a container_type
        Name name_temp(name);
        map<std::string, Value *> extra_fields;
        ContainerType *parent_container_type = NULL;
        if (parent_name) {
            parent_container_type = t->search_container_type(String(parent_name));
        }
        t->define_container_type(name_temp, parent_container_type, extra_fields);
        container_type = t->search_container_type(String(name));
        assert(container_type != NULL);
    }
    return container_type;
}

void ParserDefinitionOTF2::create_location(Trace *t, OTF2_Location *l) {
    Name name_temp(get_string_id(l));
    OTF2_LocationGroup *parent = get_location_group_by_id(l->_group_id);
    Container *parent_container = NULL;
    map<string, Value *> extra_fields;
    if (parent != NULL) {
        parent_container = t->search_container(get_string_id(parent));
    }
    const char *parent_name = NULL;
    if (parent) {
        parent_name = get_string_by_id(parent->_name_id);
    }
    ContainerType *container_type = define_container_type(t, get_string_by_id(l->_name_id), parent_name);

    Date d = 0;
    Container *c = t->search_container(name_temp.get_name());
    if (c) {
        cerr << "Warning: container " << name_temp.get_name() << " already exists!\n";
        return;
    }
    t->create_container(d, name_temp, container_type, parent_container, extra_fields);
    l->container = t->search_container(name_temp.get_name());
}

void ParserDefinitionOTF2::create_location_group(Trace *t, OTF2_LocationGroup *lg) {
    Name name_temp(get_string_id(lg));
    OTF2_SystemTreeNode *parent = get_system_tree_node_by_id(lg->_node_id);
    Container *parent_container = NULL;
    map<string, Value *> extra_fields;
    if (parent != NULL) {
        parent_container = t->search_container(get_string_id(parent));
    }

    const char *parent_name = NULL;
    if (parent) {
        parent_name = get_string_by_id(parent->_name_id);
    }
    ContainerType *container_type = define_container_type(t, get_string_by_id(lg->_name_id), parent_name);

    Date d = 0;
    t->create_container(d, name_temp, container_type, parent_container, extra_fields);
    lg->container = t->search_container(name_temp.to_string());
}

void ParserDefinitionOTF2::create_system_tree_node(Trace *t, OTF2_SystemTreeNode *node) {

    Name name_temp(get_string_id(node));
    OTF2_SystemTreeNode *parent = get_system_tree_node_by_id(node->_parent);
    Container *parent_container = NULL;

    const char *parent_name = NULL;
    if (parent) {
        parent_name = get_string_by_id(parent->_name_id);
    }
    ContainerType *container_type = define_container_type(t, get_string_by_id(node->_name_id), parent_name);

    map<string, Value *> extra_fields;
    if (parent != NULL) {
        parent_container = t->search_container(get_string_id(parent));
    }
    Date d = 0;
    t->create_container(d, name_temp, container_type, parent_container, extra_fields);
}

void ParserDefinitionOTF2::create_container_types(Trace *t) {

    std::map<OTF2_SystemTreeNodeRef, OTF2_SystemTreeNode *>::const_iterator it_tree, end_tree;
    end_tree = _system_tree_node.end();
    for (it_tree = _system_tree_node.begin(); it_tree != end_tree; ++it_tree) {
        OTF2_SystemTreeNode *n = (*it_tree).second;
        if (n) {
            // Create the containers
            create_system_tree_node(t, n);
        }
    }

    std::map<OTF2_LocationGroupRef, OTF2_LocationGroup *>::const_iterator it_lg, end_lg;
    end_lg = _location_group.end();
    for (it_lg = _location_group.begin(); it_lg != end_lg; ++it_lg) {
        OTF2_LocationGroup *n = (*it_lg).second;
        if (n) {
            // Create the containers
            create_location_group(t, n);
        }
    }

    std::map<OTF2_LocationRef, OTF2_Location *>::const_iterator it_l, end_l;
    end_l = _location.end();
    for (it_l = _location.begin(); it_l != end_l; ++it_l) {
        OTF2_Location *n = (*it_l).second;
        if (n) {
            // Create the containers
            create_location(t, n);
        }
    }
}

void ParserDefinitionOTF2::create_metric_member(Trace *t, OTF2_MetricMember m) {
    ContainerType *temp_container_type = NULL;
    std::map<OTF2_LocationRef, OTF2_Location *>::const_iterator it_l;
    it_l = _location.begin();
    if (it_l != _location.end()) {
        OTF2_Location *n = (*it_l).second;
        if (n) {
            temp_container_type = t->search_container_type(String(get_string_by_id(n->_name_id)));
        }
    }
    if (!temp_container_type)
        temp_container_type = t->search_container_type(String("0"));

    if (temp_container_type == NULL) {
        Error::set(Error::VITE_ERR_UNKNOWN_CONTAINER_TYPE, Error::VITE_ERRCODE_ERROR);
    }
    else {
        map<string, Value *> extra_fields;
        Name alias_name(get_string_by_id(m._name));
        t->define_variable_type(alias_name, temp_container_type, extra_fields);
    }
}

void ParserDefinitionOTF2::create_metric_class(Trace * /*t*/,
                                               OTF2_MetricClass /*m*/) {
    // Nothing to do ?
}

void ParserDefinitionOTF2::initialize_types(Trace *t) {

    std::map<uint32_t, OTF2_MetricMember>::const_iterator it_member, end_member;
    end_member = _metric_member.end();
    for (it_member = _metric_member.begin(); it_member != end_member; ++it_member) {
        OTF2_MetricMember n = (*it_member).second;
        // Create the metricMember
        create_metric_member(t, n);
    }

    std::map<uint32_t, OTF2_MetricClass>::const_iterator it_class, end_class;
    end_class = _metric_class.end();
    for (it_class = _metric_class.begin(); it_class != end_class; ++it_class) {
        OTF2_MetricClass n = (*it_class).second;
        // Create the metricClass
        create_metric_class(t, n);
    }
}

// Debug purposes
void ParserDefinitionOTF2::print_definitions() {
    cout << "Strings:" << endl;
    for (map<uint32_t, const char *>::const_iterator it = _strings.begin(); it != _strings.end(); ++it) {
        cout << "str#" << (*it).first << " " << (*it).second << endl;
    }

    cout << "SystemTreeNode:" << endl;
    for (map<OTF2_SystemTreeNodeRef, OTF2_SystemTreeNode *>::const_iterator it = _system_tree_node.begin(); it != _system_tree_node.end(); ++it) {
        cout << "#" << (*it).first;
        OTF2_SystemTreeNode *temp = (*it).second;
        if (temp) {
            cout << " string_id: '" << temp->_id_string << "' parent:'" << temp->_parent << "'" << endl;
        }
    }

    cout << "LocationGroup:" << endl;
    for (map<OTF2_LocationGroupRef, OTF2_LocationGroup *>::const_iterator it = _location_group.begin(); it != _location_group.end(); ++it) {
        cout << "#" << (*it).first;
        OTF2_LocationGroup *temp = (*it).second;
        if (temp) {
            cout << " name: '" << temp->_id_string << " ' node_id: '" << temp->_node_id << "'" << endl;
        }
    }

    cout << "Location:" << endl;
    for (map<OTF2_LocationRef, OTF2_Location *>::const_iterator it = _location.begin(); it != _location.end(); ++it) {
        cout << "#" << (*it).first;
        OTF2_Location *temp = (*it).second;
        if (temp) {
            cout << " name: '" << temp->_id_string << " ' locationGroup: '" << temp->_group_id << "'" << endl;
        }
    }

    cout << "Function:" << endl;
    for (map<uint32_t, OTF2_Function>::const_iterator it = _functions.begin(); it != _functions.end(); ++it) {
        cout << "#" << (*it).first;
        OTF2_Function temp = (*it).second;
        cout << " name id: '" << temp._name_id << "' name:'" << _strings[temp._name_id] << "' region_description: '" << _strings[temp._region_description] << "' region_type: '" << temp._regionRole << "'" << endl;
    }
}
