/**
 *
 * @file src/parser/ParserSplitted.cpp
 *
 * @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 Thibault Soucarre
 * @author Augustin Degomme
 *
 * @date 2024-07-17
 */
#include <iostream>
#include <fstream>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <list>
#include <vector>

/* -- */
#include "common/common.hpp"
#include "common/Errors.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
/* -- */
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
/* -- */
#include "parser/Parser.hpp"
/* -- */
#include <boost/serialization/string.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/archive/tmpdir.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include "boost/serialization/map.hpp"

#include "trace/Serializer_values.hpp"
#include "trace/Serializer_types.hpp"
#include "trace/Serializer_structs.hpp"
#include "trace/Serializer_container.hpp"
#include "trace/SerializerDispatcher.hpp"

#include "trace/values/Values.hpp"
#include "trace/tree/Interval.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
#include "trace/Container.hpp"

#include "parser/ParserSplitted.hpp"

#define message *Message::get_instance() << "(" << __FILE__ << " l." << __LINE__ << "): "

using namespace std;

ParserSplitted::ParserSplitted() :
    _loaded_itc(0),
    _nb_itc(0) { }

ParserSplitted::ParserSplitted(const string &filename) :
    Parser(filename),
    _loaded_itc(0),
    _nb_itc(0) { }

ParserSplitted::~ParserSplitted() { }

void ParserSplitted::parse(Trace &trace,
                           bool finish_trace_after_parse) {
    Info::Splitter::load_splitted = true;

    // dump containers
    //      char* filename=(char*)malloc(128*sizeof(char));
    //      sprintf(filename,"%s/%s.vite", Info::Splitter::path, );
    Serializer<Container>::Instance().clear();
    Serializer<ContainerType>::Instance().clear();
    Serializer<EntityType>::Instance().clear();

    std::list<ContainerType *> conts_types;
    std::list<Container *> conts;
    std::ifstream ifs(_file_to_parse.c_str(), std::ios::in);
    if (ifs.fail()) {
        message << "Error while opening file" << _file_to_parse << Message::ende;
        return;
    }

    QT_TRY {
        boost::archive::text_iarchive ia(ifs);

        ia.register_type(static_cast<StateType *>(NULL));
        ia.register_type(static_cast<EventType *>(NULL));
        ia.register_type(static_cast<VariableType *>(NULL));
        ia.register_type(static_cast<LinkType *>(NULL));
        ia.register_type(static_cast<ContainerType *>(NULL));
        ia.register_type(static_cast<Container *>(NULL));
        ia.register_type(static_cast<Color *>(NULL));
        ia.register_type(static_cast<Date *>(NULL));
        ia.register_type(static_cast<Double *>(NULL));
        ia.register_type(static_cast<Hex *>(NULL));
        ia.register_type(static_cast<Integer *>(NULL));
        ia.register_type(static_cast<Name *>(NULL));
        ia.register_type(static_cast<String *>(NULL));

        ia >> conts_types;
        trace.set_container_types(conts_types);
        // int test;
        // ia >> test;
        //  restore the schedule from the archive
        ia >> conts;

        trace.set_root_containers(conts);

        // we need to load a config from file
        if (Info::Splitter::xml_filename.length() != 0) {
            bool xml_success = trace.load_config_from_xml(QString(Info::Splitter::xml_filename.c_str()));
            if (!xml_success) {
                std::cerr << "Error while parsing" << Info::Splitter::xml_filename << std::endl;
            }
        }

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

        ia >> _state_types;
        trace.set_state_types(_state_types);

        ia >> _event_types;
        trace.set_event_types(_event_types);

        ia >> _link_types;
        trace.set_link_types(_link_types);

        ia >> _variable_types;
        trace.set_variable_types(_variable_types);

        Date d;
        ia >> d;
        trace.set_max_date(d);
    }
    QT_CATCH(boost::archive::archive_exception e) {
        std::cerr << "failed while restoring serialized file !"
                  << e.what() << " with file "
                  << _file_to_parse << std::endl;
        return;
    }

    // we count the number of containers to load, in order to have a pseudo relevant loaded percentage to display_build
    // we should do this with ITC, but that would mean to handle all of the loading here or to have a feedback from trace and containers
    {
        stack<Container *> containers;
        const Container::Vector *root_containers = trace.get_view_root_containers();
        if (root_containers->empty())
            root_containers = trace.get_root_containers();

        Container::VectorIt i = root_containers->begin();
        Container::VectorIt const &end = root_containers->end();

        for (; i != end; i++)
            containers.push(*i);

        while (!containers.empty()) {
            Container *c = containers.top();
            _nb_itc += c->get_intervalsOfContainer()->size();
            containers.pop();
            {
                const Container::Vector *children = c->get_view_children();
                if (children->empty())
                    children = c->get_children();

                Container::VectorIt it = children->begin();
                Container::VectorIt const &it_end = children->end();

                for (; it != it_end; it++)
                    containers.push(*it);
            }
        }
    }

    if (Info::Splitter::load_splitted == true) {
        // we are in the preview mode
        if (Info::Splitter::_x_max == 0)
            Info::Splitter::preview = true;

        if (Info::Splitter::preview == true)
            trace.loadPreview();
        else {
            Interval *interval = new Interval(Info::Splitter::_x_min, Info::Splitter::_x_max);
            // trace.loadTraceInside(i);
            // in order to manage the percentage of loading, do this inside parser and not in trace

            stack<Container *> containers;
            const Container::Vector *root_containers = trace.get_view_root_containers();
            if (root_containers->empty())
                root_containers = trace.get_root_containers();

            Container::VectorIt i = root_containers->begin();
            Container::VectorIt const &end = root_containers->end();

            for (; i != end; i++)
                containers.push(*i);

            while (!containers.empty()) {
                Container *c = containers.top();
                c->loadItcInside(interval);

                /* Just an approximation, based on the total number of itc,
                 * and not on the number of loaded itc (maybe heavy to parse)
                 */
                _loaded_itc += c->get_intervalsOfContainer()->size();
                containers.pop();
                {
                    const Container::Vector *children = c->get_view_children();
                    if (children->empty())
                        children = c->get_children();

                    Container::VectorIt it = children->begin();
                    Container::VectorIt const &it_end = children->end();

                    for (; it != it_end; it++)
                        containers.push(*it);
                }
            }

            SerializerDispatcher::Instance().kill_all_threads();
        }
    }

    if (finish_trace_after_parse) {
        // we need to build the display only if we are not splitting the trace
        trace.finish();
        finish();
    }
    //  unsigned long long  after = getCurrentTime();
    //  g_totalTime+=(after-before);
}

void ParserSplitted::releasefile() { }

float ParserSplitted::get_percent_loaded() const {
    if (_nb_itc != 0)
        return (double)_loaded_itc / (double)_nb_itc;
    else
        return 0.0;
}
#endif
