#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
