/**
 *
 * @file tests/stubs/Trace.hpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Mathieu Faverge
 *
 * @date 2024-07-17
 */
#ifndef TRACE_HPP
#define TRACE_HPP

#include <vector>
#include <map>
#include "Color.hpp"
#include "Date.hpp"
#include "Double.hpp"
#include "Hex.hpp"
#include "Integer.hpp"
#include "Name.hpp"
#include "String.hpp"
#include "Value.hpp"

/*!
 *
 * \file Trace.hpp
 * \author  NOISETTE
 * \brief Bouchon
 *
 *
 *
 */

typedef std::string Container;
typedef std::string ContainerType;
typedef std::string EntityType;
typedef std::string StateType;
typedef std::string EntityValue;
typedef std::string EventType;
typedef std::string VariableType;
typedef std::string LinkType;

class Trace
{

private:
    mutable std::vector<std::string *> to_freed;
    void free_str();

public:
    Trace();
    ~Trace();
    std::string to_string(const std::map<std::string, Value *> &extra_fields);
    /*!
     *
     *\fn define_container_type()
     *\brief This function is to define a container type
     *
     *\param String : the type of his parent container
     *\param Name : an object that can contain a name, an alias or both
     *
     */
    void define_container_type(Name &alias, ContainerType *container_type_parent, const std::map<std::string, Value *> &extra_fields);

    /*!
     *
     *\fn create_container()
     *\brief This function is to create a container
     *
     *
     *\param Date : When the container is created
     *\param Name : an object that can contain a name, an alias or both
     *\param String : the type of the container
     *\param String : the parent of the container
     *
     */
    void create_container(const Date &time, const Name &alias, ContainerType *type, Container *parent, const std::map<std::string, Value *> &extra_fields);

    /*!
     *
     *\fn destroy_container()
     *\brief This function is to destroy a container
     *
     *\param Date : When the container is destroyed
     *\param Name : an object that can contain a name, an alias or both
     *\param Sring : the type of the container
     *
     */
    void destroy_container(const Date &time, Container *cont, ContainerType *type, const std::map<std::string, Value *> &extra_fields);

    /*!
     *
     *\fn define_event_type()
     *\brief This function is to define a type of event
     *
     *\param Name : an object that can contain a name, an alias or both
     *\param String : the type of the container
     *
     */
    void define_event_type(const Name &alias, ContainerType *container_type, const std::map<std::string, Value *> &extra_fields);

    /*!
     *
     *\fn define_state_type()
     *\brief This function is to define a type of state
     *
     *\param Name : an object that can contain a name, an alias or both
     *\param String : the type of the container
     *
     *
     */
    void define_state_type(const Name &alias, ContainerType *container_type, const std::map<std::string, Value *> &extra_fields);

    /*!
     *
     *\fn define_variable_type()
     *\brief This function is to define a type of variable
     *
     *\param Name : an object that can contain a name, an alias or both
     *\param String : the type of the container
     *
     */
    void define_variable_type(const Name &alias, ContainerType *container_type, const std::map<std::string, Value *> &extra_fields);

    /*!
     *
     *\fn define_link_type()
     *\brief This function is to define a type of link
     *
     *\param Name : an object that can contain a name, an alias or both
     *\param String : the type of the container that is the common ancestor of both container
     *\param String : the type of the container where the link starts
     *\param String : the type of the container where the link goes
     *
     */
    void define_link_type(const Name &alias, ContainerType *ancestor, ContainerType *source, ContainerType *destination, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn define_entity_value()
     *\brief This function is to define_entity_value
     *
     *\param Name : an object that can contain a name, an alias or both
     *\param String : the type of the entity
     *
     */
    void define_entity_value(const Name &alias, EntityType *entity_type, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn set_state()
     *\brief This function is to set a state
     *
     *\param Date : Moment when it changes of state
     *\param String : the type of the entity
     *\param String : the container
     *\param String : the new value of the state
     *
     */
    void set_state(const Date &time, StateType *type, Container *container, EntityValue *value, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn push_state()
     *\brief This function is to push a state on the stack
     *
     *\param Date : Moment when the state is pushed
     *\param String : the type of the entity
     *\param String : the container
     *\param String : the new value of the state
     *
     */
    void push_state(const Date &time, StateType *type, Container *container, EntityValue *value, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn pop_state()
     *\brief This function is to pop a state from the stack
     *
     *\param Date : Moment when the state is popped
     *\param String : the type of the entity
     *\param String : the container
     *
     */
    void pop_state(const Date &time, StateType *type, Container *container, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn new_event()
     *\brief This function is to create a new event
     *
     *\param Date : When the new event arrives
     *\param String : the type of the entity
     *\param String : the container
     *\param String : the value of the event
     *
     */
    void new_event(const Date &time, EventType *type, Container *container, EntityValue *value, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn set_variable()
     *\brief This function is to set a value to the variable
     *
     *\param Date : When the variable is set
     *\param String : the type of the entity
     *\param String : the container
     *\param double : the value of the variable
     *
     */
    void set_variable(const Date &time, VariableType *type, Container *container, const Double &value, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn add_variable()
     *\brief This function is to add a new variable
     *
     *\param Date : When th variable is incremented
     *\param String : the type of the entity
     *\param String : the container
     *\param double : the value of the variable
     *
     */
    void add_variable(const Date &time, VariableType *type, Container *container, const Double &value, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn sub_variable()
     *\brief This function is to substract a value to the variable
     *
     *\param Date : When the variable is decremented
     *\param String : the type of the entity
     *\param String : the container
     *\param double : the value of the variable
     *
     */
    void sub_variable(const Date &time, VariableType *type, Container *container, const Double &value, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn start_link()
     *\brief This function is to start a link
     *
     *\param Date : When the link starts
     *\param String : the type of the entity
     *\param String : the container
     *\param String : the source of the link
     *\param String : the value of the variable
     *
     */
    void start_link(Date &time, LinkType *type, Container *ancestor, Container *source, EntityValue *value, const String &key, const map<string, Value *> &extra_fields);

    /*!
     *
     *\fn end_link()
     *\brief This function is to end a link
     *
     *\param Date : When the link ends
     *\param String : the type of the entity
     *\param String : the container
     *\param String : the destination of the link
     *\param String : the value of the variable
     *
     */
    void end_link(Date &time, LinkType *type, Container *ancestor, Container *destination, EntityValue *value, const String &key, const map<string, Value *> &extra_fields);

    /*!
     *
     * \fn get_root_container
     * \brief returns the list of the root container
     *
     */

    /*!
     *
     * \fn search_container_type
     * \brief search a container type by his name or alias
     *
     */
    Container *search_container_type(const String &name) const;

    /*!
     *
     * \fn search_container
     * \brief search a container by his name or alias
     *
     */
    Container *search_container(const String &name) const;
    /*!
     *
     * \fn search_container
     * \brief search a container by his name or alias
     *
     */
    Container *search_container(const String &name) const;

    /*!
     *
     * \fn search_event_type
     * \brief search a event type by his name or alias
     *
     */
    Container *search_event_type(const String &name);

    /*!
     *
     * \fn search_state_type
     * \brief search a container by his name or alias
     *
     */
    Container *search_state_type(const String &name);

    /*!
     *
     * \fn search_variable_type
     * \brief search a variable type by his name or alias
     *
     */
    VariableType *search_variable_type(const String &name);

    /*!
     *
     * \fn search_link_type
     * \brief search a container by his name or alias
     *
     */
    LinkType *search_link_type(const String &name); /*!
                                                     *
                                                     * \fn search_entity_value
                                                     * \brief search a container by his name or alias
                                                     *
                                                     */
    EntityValue *search_entity_value(const String &name, EntityType *entity_type) const;
    EntityType *search_entity_type(const String &name) const;

    void finish();

}; // end class

#endif
