/**
 *
 * @file src/parser/OTF2Parser/ParserDefinitionOTF2.hpp
 *
 * @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.hpp
 *
 *  @author François Trahay
 *  @author Lagrasse Olivier
 *  @author Johnny Jazeix
 *  @author Mathieu Faverge
 *
 */

#ifndef PARSERDEFINITIONOTF2_HPP
#define PARSERDEFINITIONOTF2_HPP

#include <otf2/otf2.h>

class Trace;

/*!
 * \def NB_COLORS
 * The number of default colors.
 * They are created in the constructor, deleted in destructor.
 */
#define NB_COLORS 10

struct OTF2_Location;
struct OTF2_LocationGroup;
/*!
 * \struct OTF2_SystemTreeNode
 * \brief Contains the definition of a machine (equivalent in Paje : Container)
 */
struct OTF2_SystemTreeNode
{
    /*! \brief Id of the node */
    OTF2_SystemTreeNodeRef _node_id;
    /*! \brief String that identifies the node */
    std::string _id_string;
    /*! \brief Name of the node */
    uint32_t _name_id;
    /*! \brief id of the parent node */
    OTF2_SystemTreeNodeRef _parent;
    /*! \brief child system tree nodes */
    std::map<OTF2_SystemTreeNodeRef, OTF2_SystemTreeNode *> _child_nodes;
    /*! \brief child location groups */
    std::map<OTF2_LocationGroupRef, OTF2_LocationGroup *> _location_group;
};

/*!
 * \struct OTF2_LocationGroup
 * \brief Contains the definition of a processGroup (equivalent in Paje : Container)
 */
struct OTF2_LocationGroup
{
    /*! \brief Id of the LocationGroup */
    OTF2_LocationGroupRef _group_id;
    /*! \brief String that identifies the group */
    std::string _id_string;
    /*! \brief Name of the LocationGroup */
    uint32_t _name_id;
    /*! \brief Child locations */
    std::map<OTF2_LocationRef, OTF2_Location *> _location;
    /*! \brief Id of the parent node */
    OTF2_SystemTreeNodeRef _node_id;
    /*! \brief Container corresponding to this Location */
    Container *container;
};

/*!
 * \struct OTF2_Location
 * \brief Contains the definition of a process (equivalent in Paje : Container)
 */
struct OTF2_Location
{
    /*! \brief Id of the Location */
    OTF2_LocationRef _location_id;
    /*! \brief String that identifies the location */
    std::string _id_string;
    /*! \brief Name of the Location */
    uint32_t _name_id;
    /*! \brief Id of the parent group */
    OTF2_LocationGroupRef _group_id;
    /*! \brief Number of events in this Location */
    uint64_t _number_of_events;
    /*! \brief Container corresponding to this Location */
    Container *container;
};

/*!
 * \struct OTF2_Function
 * \brief Contains the definition of a function (equivalent in Paje : State)
 */
struct OTF2_Function
{
    /*! \brief Name of the state */
    OTF2_StringRef _name_id;
    /*! \brief Alternative name of the region (e.g. mangled name */
    OTF2_StringRef _canonicalName;
    /*! \brief A more detailed description of this region */
    OTF2_StringRef _region_description;
    /*! \brief Region role. */
    OTF2_RegionRole _regionRole;
    /*! \brief Paradigm. */
    OTF2_Paradigm _paradigm;
    /*! \brief Region flags. */
    OTF2_RegionFlag _regionFlags;
    /*! \brief The source file where this region was declared */
    OTF2_StringRef _sourceFile;
    /*! \brieg Starting line number of this region in the source file. */
    uint32_t _begin_line_number;
    /*! \brieg Ending line number of this region in the source file. */
    uint32_t _end_line_number;
};

struct OTF2_MetricMember
{
    OTF2_MetricMemberRef _id;
    OTF2_StringRef _name;
    OTF2_StringRef _description;
    OTF2_MetricType _metricType;
    OTF2_MetricMode _metricMode;
    OTF2_Type _valueType;
    OTF2_Base _base;
    int64_t _exponent;
    OTF2_StringRef _unit;
};

struct OTF2_MetricClass
{
    OTF2_MetricRef _id;
    std::vector<OTF2_MetricMemberRef> _metricMembers;
    OTF2_MetricOccurrence _metricOccurrence;
    OTF2_RecorderKind _recorderKind;
};

struct OTF2_Group
{
    OTF2_GroupRef _id;
    OTF2_StringRef _name;
    OTF2_GroupType _type;
    OTF2_Paradigm _paradigm;
    OTF2_GroupFlag _flags;
    std::vector<uint64_t> _members;
};

struct OTF2_Comm_Locations
{
    OTF2_GroupRef _id;
    OTF2_StringRef _name;
    OTF2_GroupType _type;
    OTF2_Paradigm _paradigm;
    OTF2_GroupFlag _flags;
    std::vector<OTF2_LocationRef> _members;
};

struct OTF2_Comm
{
    OTF2_CommRef _id;
    OTF2_StringRef _name;
    OTF2_GroupRef _group;
    OTF2_CommRef _parent;
};

/*!
 *
 * \class ParserDefinitionOTF2
 * \brief Parse the definitions of the trace and store them.
 *
 */
class ParserDefinitionOTF2
{
private:
    /*!
     * Reader for the file
     */
    OTF2_GlobalDefReader *_global_def_reader;

    OTF2_GlobalDefReaderCallbacks *_global_def_callbacks;

    /*!
     * Maps in order to easily retrieve the events.
     */
    static std::map<OTF2_SystemTreeNodeRef, OTF2_SystemTreeNode *> _system_tree_node;
    static std::map<OTF2_LocationGroupRef, OTF2_LocationGroup *> _location_group;
    static std::map<OTF2_LocationRef, OTF2_Location *> _location;

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

    static std::map<uint32_t, OTF2_Function> _functions;
    static std::map<uint32_t, const char *> _strings;

    static std::map<OTF2_CommRef, OTF2_Comm *> _comms; // an MPI communicator. it corresponds to a group
    static std::map<OTF2_GroupRef, OTF2_Group *> _groups; // a group is a set of MPI ranks

    static struct OTF2_Comm_Locations _comm_locations;

    static uint64_t _ticks_per_second;
    static double _first_timestamp;

    /*!
     * Table containing default colors.
     * We take the state id modulo the max number to get the color.
     */
    static std::vector<Color *> _default_colors;

#if OTF2_VERSION_MAJOR >= 3
    static OTF2_CallbackCode handler_DefTimerResolution(void *, uint64_t, uint64_t, uint64_t, uint64_t);
#else
    static OTF2_CallbackCode handler_DefTimerResolution(void *, uint64_t, uint64_t, uint64_t);
#endif

    static OTF2_CallbackCode handler_DefString(void *, OTF2_StringRef, const char *);

    // System tree callback
    static OTF2_CallbackCode handler_DefSystemTreeNode(void * /*userData  */,
                                                       OTF2_SystemTreeNodeRef tree_node_id,
                                                       OTF2_StringRef name_id,
                                                       OTF2_StringRef class_id,
                                                       OTF2_SystemTreeNodeRef parent_node_id);

#if OTF2_VERSION_MAJOR >= 3
    static OTF2_CallbackCode handler_DefLocationGroup(void * /*userdata*/,
                                                      OTF2_LocationGroupRef location_group_identifier,
                                                      OTF2_StringRef name,
                                                      OTF2_LocationGroupType type,
                                                      OTF2_SystemTreeNodeRef system_tree_parent,
                                                      OTF2_LocationGroupRef creatingLocationGroup);
#else
    static OTF2_CallbackCode handler_DefLocationGroup(void * /*userdata*/,
                                                      OTF2_LocationGroupRef location_group_identifier,
                                                      OTF2_StringRef name,
                                                      OTF2_LocationGroupType type,
                                                      OTF2_SystemTreeNodeRef system_tree_parent);
#endif

    static OTF2_CallbackCode handler_DefLocation(void *userData,
                                                 OTF2_LocationRef locationIdentifier,
                                                 OTF2_StringRef name_id,
                                                 OTF2_LocationType location_type,
                                                 uint64_t numberOfEvents,
                                                 OTF2_LocationGroupRef locationGroup);

    // Region callback
    static OTF2_CallbackCode handler_DefState(void *, OTF2_RegionRef, OTF2_StringRef, OTF2_StringRef, OTF2_StringRef description, OTF2_RegionRole, OTF2_Paradigm, OTF2_RegionFlag, OTF2_StringRef, uint32_t, uint32_t);

    static OTF2_CallbackCode 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 OTF2_VERSION_MAJOR >= 3
    static OTF2_CallbackCode handler_DefComm(void *userData,
                                             OTF2_CommRef self,
                                             OTF2_StringRef name,
                                             OTF2_GroupRef group,
                                             OTF2_CommRef parent,
                                             OTF2_CommFlag flags);
#else
    static OTF2_CallbackCode handler_DefComm(void *userData,
                                             OTF2_CommRef self,
                                             OTF2_StringRef name,
                                             OTF2_GroupRef group,
                                             OTF2_CommRef parent);
#endif

    static OTF2_CallbackCode 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);

    static OTF2_CallbackCode handler_DefMetricClass(void *userData,
                                                    OTF2_MetricRef self,
                                                    uint8_t numberOfMetrics,
                                                    const OTF2_MetricMemberRef *metricMembers,
                                                    OTF2_MetricOccurrence metricOccurrence,
                                                    OTF2_RecorderKind recorderKind);

    static OTF2_CallbackCode handler_DefMetricInstance(void *userData,
                                                       OTF2_MetricRef self,
                                                       OTF2_MetricRef metricClass,
                                                       OTF2_LocationRef recorder,
                                                       OTF2_MetricScope metricScope,
                                                       uint64_t scope);
    static OTF2_CallbackCode handler_DefMetricClassRecorder(void *userData,
                                                            OTF2_MetricRef metric,
                                                            OTF2_LocationRef recorder);

    ParserDefinitionOTF2(const ParserDefinitionOTF2 &);

public:
    /*!
     * \fn ParserDefinitionOTF2(OTF2_Reader *reader)
     * \brief constructor
     */
    ParserDefinitionOTF2(OTF2_Reader *reader);

    /*!
     * \fn ~ParserDefinitionOTF2()
     * \brief destructor
     */
    ~ParserDefinitionOTF2();

    /*!
     * \fn set_handlers(Trace *t)
     * \brief Create and set the handlers for the definition parsing.
     * \param t The trace we want to store in.
     */
    void set_handlers(Trace *t);

    /*!
     * \fn read_definitions(OTF2_Reader *reader)
     * \brief Begin the reading of the definitions
     * \param reader The main otf2 file we want to read in.
     */
    void read_definitions(OTF2_Reader *reader);

    /*!
     * \fn create_container_types(Trace *t)
     * \brief Create all the container types needed for the trace
     * It is run at the end of the definitions parsing.
     * \param t The trace where we store data
     */
    void create_container_types(Trace *t);

    void create_metric_member(Trace *t, OTF2_MetricMember m);
    void create_metric_class(Trace *t, OTF2_MetricClass m);

    /*!
     * \fn initialize_types(Trace *t)
     * \brief Create all the types needed for the trace
     * It is run at the end of the definitions parsing.
     * \param t The trace where we store data
     */
    void initialize_types(Trace *t);

    void create_location(Trace *t, OTF2_Location *l);
    void create_location_group(Trace *t, OTF2_LocationGroup *lg);
    void create_system_tree_node(Trace *t, OTF2_SystemTreeNode *node);

    static void generate_string_id(OTF2_Location *l);
    static void generate_string_id(OTF2_LocationGroup *lg);
    static void generate_string_id(OTF2_SystemTreeNode *n);

    static std::string get_string_id(OTF2_Location *l);
    static std::string get_string_id(OTF2_LocationGroup *lg);
    static std::string get_string_id(OTF2_SystemTreeNode *n);

    /*!
     * \fn print_definitions()
     * \brief Print all the definitions stored. Useful for debug
     */
    void print_definitions();

    static OTF2_MetricClass get_metric_class(const OTF2_MetricRef id);

    static OTF2_MetricMember get_metric_member(const OTF2_MetricClass metric_class, int index);

    /*!
     * \fn get_system_tree_node_by_id(const uint32_t id)
     * \brief Accessor for the system_tree_node map
     * \param id the id we want the corresponding system tree node
     * \return The system tree node associated to id
     */
    static OTF2_SystemTreeNode *get_system_tree_node_by_id(const OTF2_SystemTreeNodeRef id);

    /*!
     * \fn get_location_group_by_id(const uint32_t id)
     * \brief Accessor for the location_group map
     * \param id the id we want the corresponding location group
     * \return The location group associated to id
     */
    static OTF2_LocationGroup *get_location_group_by_id(const OTF2_LocationGroupRef id);

    /*!
     * \fn get_location_by_id(const uint32_t id)
     * \brief Accessor for the location map
     * \param id the id we want the corresponding location
     * \return The location associated to id
     */
    static OTF2_Location *get_location_by_id(const OTF2_LocationRef id);

    /*!
     * \fn get_location_in_communicator(const OTF2_CommRef comm, uint32_t rank)
     * \brief Search for the location that corresponds to an MPI rank in an MPI communicator
     * \param comm the MPI communicator
     * \param rank the rank of the location group
     * \return The location associated to rank
     */
    static OTF2_Location *get_location_in_communicator(const OTF2_CommRef comm, uint32_t rank);

    /*!
     * \fn get_location_group_in_communicator(const OTF2_CommRef comm, uint32_t rank)
     * \brief Search for the location group that corresponds to an MPI rank in an MPI communicator
     * \param comm the MPI communicator
     * \param rank the rank of the location group
     * \return The location group associated to rank within comm
     */
    static OTF2_LocationGroup *get_location_group_in_communicator(const OTF2_CommRef comm, uint32_t rank);

    /*!
     * \fn get_function_by_id(const uint32_t id)
     * \brief Accessor for the function map
     * \param id the id we want the corresponding function
     * \return The OTF2_Function associated to id
     */
    static OTF2_Function get_function_by_id(const uint32_t id);

    /*!
     * \fn get_group_by_id(const OTF2_GroupRef id)
     * \brief Accessor for the function map
     * \param id the id we want the corresponding group
     * \return The OTF2_Group associated to id
     */
    static OTF2_Group *get_group_by_id(OTF2_GroupRef id);

    /*!
     * \fn get_comm_by_id(const OTF2_CommRef id)
     * \brief Accessor for the function map
     * \param id the id we want the corresponding comm
     * \return The OTF2_Comm associated to id
     */
    static OTF2_Comm *get_comm_by_id(OTF2_CommRef id);

    /*!
     * \fn get_ticks_per_second()
     * \brief Accessor for the tick_per_second (equivalent to a time unit)
     * \return The number of ticks per second
     */
    static uint64_t get_ticks_per_second();

    static double get_timestamp(OTF2_TimeStamp ts);

    static Color *get_color(uint32_t func_id);

    static const char *get_string_by_id(uint32_t id);
};

#endif // PARSERDEFINITIONOTF2_HPP
