/**
 *
 * @file src/trace/Trace.hpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Mathieu Faverge
 * @author Kevin Coulomb
 * @author Philippe Swartvagher
 * @author Olivier Lagrasse
 * @author Augustin Degomme
 * @author Clement Vuchener
 *
 * @date 2024-07-17
 */
#ifndef TRACE_HPP
#define TRACE_HPP

class Interval;
class Palette;
class QDomElement;
class QDomDocument;
class QString;

#include <list>
#include <map>
#include <set>

#include "trace/values/Date.hpp"
#include "trace/values/Values.hpp"
#include "trace/Container.hpp"

#include "common/common.hpp"

class Container;
class ContainerType;
class StateType;
class EventType;
class LinkType;
class VariableType;
class EntityType;
class EntityValue;
class Name;
class String;
class Value;

/*!
 *
 * \file Trace.hpp
 * \brief This file contains the datas definition for the storage of the trace
 * \date 30 janvier 09
 *
 */
/*!
 *
 * \class Trace
 * \brief This class contains the trace
 *
 */

class Trace
{
private:
    Date _max_date;

    std::string _filename;

    std::list<ContainerType *> _root_container_types;
    Container::Vector _root_containers;
    Container::Vector _view_root_containers;

    std::map<std::string, StateType *> _state_types;
    std::map<std::string, EventType *> _event_types;
    std::map<std::string, LinkType *> _link_types;
    std::map<std::string, VariableType *> _variable_types;

    std::map<std::string, EntityValue *> *_state_values;
    std::map<std::string, EntityValue *> *_event_values;
    std::map<std::string, EntityValue *> *_link_values;
    std::map<std::string, EntityValue *> *_vars_values;

    /*Palette *_palette;
     Palette * _event_types_palette;
     Palette * _link_types_palette;*/
    /*!
     * \brief Set of the containers to be drawn in the partial drawing function
     */
    std::vector<const Container *> *_selected_container;

    /*!
     * \brief Interval to display (without browsing the other data
     */
    Interval *_interval_constrained;
    /*!
     * \brief zoom level
     */
    double _filter;

    /*!
     * \brief Store the depth of the trace. (number of stacked containers).
     */
    int _depth;

    /*!
     * \brief add all the children of parent to the list
     * \param containers the list where we add the children
     * \param parent the parent container
     */

    void add_containers(Container::Vector &containers, const Container *parent) const;

    inline bool is_in_loaded_interval(Date &time) {
        if (!_sub_trace_to_load)
            return true;
        return (time.get_value() > _begin_trace.get_value() && time.get_value() < _end_trace.get_value());
    }

public:
    Trace(const std::string &filename);
    ~Trace();

    static void select_sub_trace(const Date &begin, const Date &end) {
        _sub_trace_to_load = true;
        _begin_trace = begin;
        _end_trace = end;
    }

    static bool _sub_trace_to_load;
    static Date _begin_trace;
    static Date _end_trace;

    /*!
     * \brief Define a container type
     * \param alias Name of the container type
     * \param parent_container_type Type of the parent container
     * \param opt Extra fields
     */
    void define_container_type(Name &alias, ContainerType *parent_container_type, std::map<std::string, Value *> &opt);

    /*!
     * \brief Create a container
     * \param time Date of the event
     * \param alias Name of the container
     * \param type Type of the container
     * \param parent Parent of the container (NULL if the container is root)
     * \param opt Extra fields
     */
    Container *create_container(Date &time, Name &alias, ContainerType *type, Container *parent, std::map<std::string, Value *> &opt);

    /*!
     * \brief Destroy a container
     * \param time Date of the event
     * \param cont Container to destroy
     * \param type Type of the container
     * \param opt Extra fields
     */
    void destroy_container(Date &time, Container *cont, ContainerType *type, std::map<std::string, Value *> &opt);

    /*!
     * \brief Define a type of event
     * \param alias Name of the type
     * \param container_type Type of the container for these events
     * \param opt Extra fields
     */
    void define_event_type(Name &alias, ContainerType *container_type, std::map<std::string, Value *> &opt);

    /*!
     * \brief Define a type of state
     * \param alias Name of the type
     * \param container_type Type of the container for these states
     * \param opt Extra fields
     */
    void define_state_type(Name &alias, ContainerType *container_type, std::map<std::string, Value *> &opt);

    /*!
     * \brief Define a type of variable
     * \param alias Name of the type
     * \param container_type Type of the container for these variables
     * \param opt Extra fields
     */
    void define_variable_type(Name &alias, ContainerType *container_type, std::map<std::string, Value *> &opt);

    /*!
     * \brief Define a type of link
     * \param alias Name of the type
     * \param ancestor Type of the ancestor container
     * \param source Type of the source container
     * \param destination Type of the destination container
     * \param opt Extra fields
     */
    void define_link_type(Name &alias, ContainerType *ancestor, ContainerType *source, ContainerType *destination, std::map<std::string, Value *> &opt);

    /*!
     * \brief Define an entity value
     * \param alias Name of the value
     * \param entity_type Type of the entity
     * \param opt Extra fields
     */
    void define_entity_value(Name &alias, EntityType *entity_type, std::map<std::string, Value *> &opt);

    /*!
     * \brief Set the state of a container
     * \param time Date of the event
     * \param type Type of the state
     * \param container Container whose state is changed
     * \param value Value of the state
     * \param opt Extra fields
     */
    void set_state(Date &time, StateType *type, Container *container, EntityValue *value, std::map<std::string, Value *> &opt);

    /*!
     * \brief Set the state of a container and save the previous one
     * \param time Date of the event
     * \param type Type of the state
     * \param container Container whose state is changed
     * \param value Value of the state
     * \param opt Extra fields
     */
    void push_state(Date &time, StateType *type, Container *container, EntityValue *value, std::map<std::string, Value *> &opt);

    /*!
     * \brief Restore a previously saved state of a container
     * \param time Date of the event
     * \param type Type of the state
     * \param container Container whose state is changed
     * \param opt Extra fields
     */
    void pop_state(Date &time, StateType *type, Container *container, std::map<std::string, Value *> &opt);

    /*!
     * \brief Purge a container from all the stacked states
     * \param time Date of the event
     * \param type Type of the state
     * \param container Container whose state is changed
     * \param opt Extra fields
     */
    void reset_state(Date &time, StateType *type, Container *container, std::map<std::string, Value *> &opt);

    /*!
     * \brief Add a new event to a container
     * \param time Date of the event
     * \param type Type of the event
     * \param container Container of the event
     * \param value Value of the event
     * \param opt Extra fields
     */
    void new_event(Date &time, EventType *type, Container *container, const String &value, std::map<std::string, Value *> &opt);

    /*!
     * \brief Set the value of a variable
     * \param time Date of the new value
     * \param type Type of the variable whose value is changed
     * \param container Container of the variable
     * \param value New value of the variable
     * \param opt Extra fields
     */
    void set_variable(Date &time, VariableType *type, Container *container, const Double &value, std::map<std::string, Value *> &opt);

    /*!
     * \brief Add a value to a variable
     * \param time Date of the new value
     * \param type Type of the variable whose value is changed
     * \param container Container of the variable
     * \param value Value to add
     * \param opt Extra fields
     */
    void add_variable(Date &time, VariableType *type, Container *container, const Double &value, std::map<std::string, Value *> &opt);

    /*!
     * \brief Substract a value to a variable
     * \param time Date of the new value
     * \param type Type of the variable whose value is changed
     * \param container Container of the variable
     * \param value Value to substract
     * \param opt Extra fields
     */
    void sub_variable(Date &time, VariableType *type, Container *container, const Double &value, std::map<std::string, Value *> &opt);

    /*!
     * \brief Start a new link identified by key
     * \param time Date of the event
     * \param type Type of the link
     * \param ancestor Ancestor container
     * \param source Source container
     * \param value Value of the link
     * \param key Key to match the end of the link
     * \param opt Extra fields
     */
    void start_link(Date &time, LinkType *type, Container *ancestor, Container *source, EntityValue *value, const String &key, std::map<std::string, Value *> &opt);

    /*!
     * \brief End a link identified by key
     * \param time Date of the event
     * \param type Type of the link
     * \param ancestor Ancestor container
     * \param destination Destination container
     * \param value Value of the link
     * \param key Key to match the Start of the link
     * \param opt Extra fields
     */
    void end_link(Date &time, LinkType *type, Container *ancestor, Container *destination, /*EntityValue *value, */ const String &key, std::map<std::string, Value *> &opt);

    /*!
     * \fn finish()
     * \brief Finish to initialize the trace
     */
    void finish();

#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
    /*!
     * \fn dump()
     * \brief Finish  by dumping all ITC in the trace
     */
    void dump(std::string path, std::string filename);
#endif

    /*!
     * \fn get_root_containers() const
     * \brief Get the list of the root containers
     */
    const Container::Vector *get_root_containers() const;

    /*!
     * \fn get_view_root_containers() const
     * \brief Get the list of the root containers to be displayed
     */
    const Container::Vector *get_view_root_containers() const;

    /*!
     * \fn set_root_containers()
     * \brief allows to replace the root containers of the trace, to reorder them
     */
    void set_root_containers(std::list<Container *> &conts);

    /*!
     * methods used to rebuild a trace from serialized data
     */
    void set_container_types(std::list<ContainerType *> &conts);
    void set_state_types(std::map<std::string, StateType *> &conts);
    void set_event_types(std::map<std::string, EventType *> &conts);
    void set_link_types(std::map<std::string, LinkType *> &conts);
    void set_variable_types(std::map<std::string, VariableType *> &conts);

    /*!
     * \fn set_max_date()
     * \brief Function that sets the max of the date
     */
    void set_max_date(Date d);

#ifdef BOOST_SERIALIZE
    void updateTrace(Interval *interval);
    /*!
     * \fn loadTraceInside(Interval* i)
     * \brief Load all the data inside the IntervalOfContainers, bounded by interval i
     */
    void loadTraceInside(Interval *i);

    /*!
     * \fn loadPreview(Interval* i)
     * \brief Load the preview
     */
    void loadPreview();
#endif

    /*!
     * \fn set_root_containers()
     * \brief allows to change the view of containers
     */
    void set_view_root_containers(std::list<Container *> &conts);

    /*!
     * \fn get_all_containers(Container::Vector &list_to_fill) const
     * \brief Get the list of all the containers
     * \param list_to_fill the list to fill
     */
    void get_all_containers(Container::Vector &list_to_fill) const;

    /*!
     * \brief Get all the stateTypes
     */
    const std::map<std::string, StateType *> *get_state_types() const;

    /*!
     * \brief Get all the eventTypes
     */
    const std::map<std::string, EventType *> *get_event_types() const;

    /*!
     * \brief Get all the eventTypes
     */
    const std::map<std::string, LinkType *> *get_link_types() const;

    const std::string &get_filename() const;

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

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

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

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

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

    /*!
     * \fn search_link_type(const String &name) const
     * \brief Search a container by his name or alias
     */
    LinkType *search_link_type(const String &name) const;

    /*!
     * \fn search_entity_type(const String &name) const
     * \brief Search an entity type by his name or alias
     */
    EntityType *search_entity_type(const String &name) const;

    /*!
     * \fn search_entity_value(const std::string &alias, EntityType *entity_type) const
     * \fn search_entity_value(const Name &alias_name, EntityType *entity_type, bool create=true)
     * \brief Search an entity value by his name or alias
     */
    EntityValue *search_entity_value(const std::string &alias, EntityType *entity_type) const;
    EntityValue *search_entity_value(const Name &alias_name, EntityType *entity_type, bool create = true) const;

    /*!
     * \fn get_max_date()
     * \brief Function that return the max of the date
     */
    Date get_max_date();

    /*!
     * \fn get_depth()
     * \brief Return the depth of the trace. Trace loading MUST be completed!
     */
    int get_depth();

    // Set
    void set_selected_container(std::vector<const Container *> *);
    void set_interval_constrained(Interval *);
    void set_filter(double);
    // Get
    std::vector<const Container *> *get_selected_container();
    Interval *get_interval_constrained();
    double get_filter();

    /*!
     * \fn get_all_variables(std::map<std::string, Variable *> &map_to_fill)
     * \brief Get a map of all the variables.
     * \param map_to_fill The map to fill.
     * A Variable do not have a name so the key is:
     * Container name + " " + VariableType name
     */
    void get_all_variables(std::map<std::string, Variable *> &map_to_fill);

    /*!
     * \fn load_config_from_xml(const QString &filename)
     * \brief load an xml config file into the view_root_containers
     * \param QString filename the xml file
     */
    bool load_config_from_xml(const QString &filename);

    void load_names_rec(Container *current_container, QDomElement &element);

    std::map<long int, double> update_text_variable_values(const Times &date);

    /*!
     * \brief Get the list of all EntityValue of one kind
     */
    std::map<std::string, EntityValue *> *
    get_all_entityvalues(EntityClass_t type);

    std::map<std::string, EntityValue *> *
    get_all_entityvalues(const std::string &type);

}; // end class

#endif
