/*
** This file is part of the ViTE project.
**
** This software is governed by the CeCILL-A license under French law
** and abiding by the rules of distribution of free software. You can
** use, modify and/or redistribute the software under the terms of the
** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following
** URL: "http://www.cecill.info".
**
** As a counterpart to the access to the source code and rights to copy,
** modify and redistribute granted by the license, users are provided
** only with a limited warranty and the software's author, the holder of
** the economic rights, and the successive licensors have only limited
** liability.
**
** In this respect, the user's attention is drawn to the risks associated
** with loading, using, modifying and/or developing or reproducing the
** software by the user in light of its specific status of free software,
** that may mean that it is complicated to manipulate, and that also
** therefore means that it is reserved for developers and experienced
** professionals having in-depth computer knowledge. Users are therefore
** encouraged to load and test the software's suitability as regards
** their requirements in conditions enabling the security of their
** systems and/or data to be ensured and, more generally, to use and
** operate it in the same conditions as regards security.
**
** The fact that you are presently reading this means that you have had
** knowledge of the CeCILL-A license and that you accept its terms.
**
**
** ViTE developers are (for version 0.* to 1.0):
**
**        - COULOMB Kevin
**        - FAVERGE Mathieu
**        - JAZEIX Johnny
**        - LAGRASSE Olivier
**        - MARCOUEILLE Jule
**        - NOISETTE Pascal
**        - REDONDY Arthur
**        - VUCHENER Clément
**
*/
#include <iostream>
#include <string>
#include <map>
#include <list>
#include <vector>
#include <stack>
#include <algorithm>


#include "common/common.hpp"
#include "common/Info.hpp"
#include <sstream>
#include <cstring>
#include <iostream>
#include <fstream>
#include "common/Message.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/values/Date.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Event.hpp"
#include "trace/tree/Interval.hpp"
#include "trace/tree/Node.hpp"
#include "trace/tree/BinaryTree.hpp"

#include "trace/State.hpp"
#include "trace/StateChange.hpp"
#include "trace/Variable.hpp"
#include "trace/Event.hpp"
#include "trace/Link.hpp"

#include <iomanip>
#include <iostream>
#include <fstream>
#include <string>

 #ifdef USE_ITC

 #if defined(BOOST_SERIALIZE)
//necessary, otherwise BOOST_VERSION is not set and portable_archive gets lost
#include <boost/version.hpp>
#include <boost/archive/tmpdir.hpp>
#include <trace/portable_archive_exception.hpp>
#include <trace/portable_iarchive.hpp>
#include <trace/portable_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include "boost/serialization/map.hpp"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/split_member.hpp>

#ifdef BOOST_GZIP
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#endif
#endif

#include "trace/Serializer_values.hpp"
#include "trace/Serializer_types.hpp"
#include "trace/Serializer_structs.hpp"
#include "trace/IntervalOfContainer.hpp"
#include <limits>
#include <float.h>
using namespace std;


 IntervalOfContainer::IntervalOfContainer(): _loaded(1),_beginning(0.0),_end(0.0),_statechanges(NULL), _n_statechanges(0),_states(NULL), _n_states(0), _states_values_map(NULL), _events(NULL),_n_events(0),_links(NULL),_n_links(0),_variables(NULL), _n_variables(0){

}


     IntervalOfContainer::~IntervalOfContainer() {

        if(_loaded){
            if(_n_statechanges!=0){
                for(int i=0;i<_n_statechanges;i++) _statechanges[i].clear();

                free (_statechanges);
                                _statechanges=NULL;
            }

            if(_n_states!=0){
                for(int i=0;i<_n_states;i++) _states[i].clear();

                free (_states);
                                _states=NULL;
            }

            if(_n_events!=0){
                for(int i=0;i<_n_events;i++) _events[i].clear();
                free( _events);
                                _events=NULL;
            }

            if(_n_links!=0){
                for(int i=0;i<_n_links;i++) _links[i].clear();
                free( _links);
                                _links=NULL;
            }


            if(_n_variables!=0){
                for(int i=0;i<_n_variables;i++)  _variables[i].clear();

                free (_variables);
                                _variables=NULL;
            }
        }

    }

#if defined(BOOST_SERIALIZE)
    void IntervalOfContainer::unload(){
          if(_n_statechanges!=0){
              free (_statechanges);
                          _statechanges=NULL;
    }

    if(_n_states!=0){
        for(int i=0;i<_n_states;i++) _states[i].clear();

        free (_states);
                _states=NULL;
    }

    if(_n_events!=0){
        for(int i=0;i<_n_events;i++) _events[i].clear();
        free ( _events);
                _events=NULL;
    }


    if(_n_links!=0){
        for(int i=0;i<_n_links;i++) _links[i].clear();

        free (_links);
                _links=NULL;
    }


    if(_n_variables!=0){
        for(int i=0;i<_n_variables;i++) _variables[i].clear();

        free (_variables);
                _variables=NULL;
    }

    _loaded=false;
}

#endif


Date IntervalOfContainer::get_beginning(){
    return _beginning;
}

Date IntervalOfContainer::get_end(){
    return _end;
}
std::map<EntityValue* , double>* IntervalOfContainer::get_states_values_map(){
    return _states_values_map;
}


State* IntervalOfContainer::add_state(Date start, Date end, StateType *type, EntityValue *value, Container *container, std::map<std::string, Value *> opt){

            if(_n_states==0){


                _states=(State*)malloc( sizeof(State)*(_n_states+N_EVENTS_PER_INTERVAL+2));//as we have two states per statechange, we need one more at he end (which will be shared with the next itc)
                _states_values_map=new std::map<EntityValue*, double>();
            }
            #if defined(BOOST_SERIALIZE)
            //set an unique id for the EntityValue (the use of bimaps inside entityvalues guarantees its unicity)
            Serializer<EntityValue>::Instance().setUid(value);
            #endif

            //create a State inside the previously allocated array (just call the constructor)
                        new (&_states[_n_states]) State (start, end, type, container, value, opt);
            //_states[_n_states].set_state_values( start, end, type, value, container, opt);
            _n_states++;

            (*_states_values_map)[value]+=(end-start);

            return & (_states[_n_states-1]);

    }

bool IntervalOfContainer::add_statechange(Date time, State *left, State *right){
    if(_n_statechanges==N_EVENTS_PER_INTERVAL)return false;
    if (_n_statechanges==0||time<_beginning)_beginning=time;
    if (time>_end)_end=time;

    if(_statechanges==NULL)_statechanges= (StateChange*)malloc(sizeof(StateChange)*N_EVENTS_PER_INTERVAL);
        new(& (_statechanges[_n_statechanges])) StateChange (time, left, right);
    //set_statechange_values(& _statechanges[_n_statechanges],time, left, right);
    _n_statechanges++;
    return true;
}

void IntervalOfContainer::add_event(Date time, EventType *type, Container* cont, EntityValue *value, map<string, Value *> opt){
//if (time<_beginning)_beginning=time;
    if (_beginning.get_value()==0.0||time<_beginning)_beginning=time;
    if (time>_end)_end=time;
        if(_n_events==0 || _n_events%N_EVENTS_PER_INTERVAL==0)_events=(Event*)realloc(_events, sizeof(Event)*(_n_events+N_EVENTS_PER_INTERVAL));

        #if defined(BOOST_SERIALIZE)
        //set an unique id for the EntityValue (the use of bimaps inside entityvalues guarantees its unicity)
        Serializer<EntityValue>::Instance().setUid(value);
        #endif

            new (&(_events[_n_events])) Event(time, type, cont, value, opt);
       // set_event_values(&_events[_n_events], time, type, cont, value, opt);
        _n_events++;
}


void IntervalOfContainer::set_variable(Container * container, VariableType *type ){

    if(_n_variables==0 || _n_variables%N_EVENTS_PER_INTERVAL==0)_variables=(Variable*)realloc(_variables, sizeof(Variable)*(_n_variables+N_EVENTS_PER_INTERVAL));
    new (&(_variables[_n_variables])) Variable(container, type);
    _n_variables++;

    }


void IntervalOfContainer::set_variables(std::map<VariableType *, Variable *>* map_var){

    _variables=(Variable*)malloc(sizeof(Variable)*map_var->size());
    memset(_variables,0,sizeof(Variable)*map_var->size());
    _n_variables=map_var->size();
    int i=0;
    map<VariableType *, Variable *>::const_iterator it_end=map_var->end();
    for (map<VariableType *, Variable *>::iterator it=map_var->end();it!=it_end;it++){
            _variables[i]=*(*it).second;
            i++;
        }

    }


bool IntervalOfContainer::add_link(Date start, Date end, LinkType *type, Container *container, Container *source, Container *destination, EntityValue *value, std::map<std::string, Value *> opt){

   if(_n_links==N_EVENTS_PER_INTERVAL)return false;
    Date min, max;
    if(start<end){
        min=start;
        max=end;
    }else{
        min=end;
        max=start;}
    if (_beginning.get_value()==0.0||min<_beginning)_beginning=min;
    if (max>_end)_end=max;
    if(_n_links==0 || _n_links%N_EVENTS_PER_INTERVAL==0)_links=(Link*)realloc(_links, sizeof(Link)*(_n_links+N_EVENTS_PER_INTERVAL));
    #if defined(BOOST_SERIALIZE)
    //set an unique id for the EntityValue (the use of bimaps inside entityvalues guarantees its unicity)
    Serializer<EntityValue>::Instance().setUid(value);
    #endif
        new (&_links[_n_links]) Link(start,end, type, container, source, destination, value, opt);
    //set_link_values(&_links[_n_links],   start,end, type, container, source, destination, value, opt);
    _n_links++;
    return true;
}

 #if defined(BOOST_SERIALIZE)
    bool IntervalOfContainer::dump_on_disk(const char* filename){
#ifdef BOOST_GZIP
        // make an archive
        std::ofstream ofs(filename, std::ios::out|std::ios::binary);
                //std::ofstream ofs(filename, std::ios::out);
        {
        boost::iostreams::filtering_streambuf<boost::iostreams::output> out;
        out.push(boost::iostreams::gzip_compressor());
        out.push(ofs);
        //boost::archive::binary_oarchive oa(out);
                //boost::archive::text_oarchive oa(out);
                eos::portable_oarchive oa(out);
                QT_TRY {
                oa << *this;
                }
                QT_CATCH(...) {
                        printf("fail ! %s", filename);
                }
        }
        return true;
#else
        std::ofstream ofs(filename);
                if(ofs.fail()){
                        printf("file ofs fail ! %s\n", filename);
                        return false;
                }
                {
                        //eos::portable_oarchive oa(ofs);
                        boost::archive::text_oarchive oa(ofs);
                        QT_TRY {
                        oa << *this;
                        }
                        QT_CATCH(const eos::portable_archive_exception &e) {
                                printf("fail while dumping serialized file ! %s %s\n", e.what(), filename);
                                return false;
                        }QT_CATCH(const exception &e) {
                                printf("fail while dumping serialized file ! %s %s\n", e.what(), filename);

                                return false;
                        }
                }
        return true;
                #endif

    }


    bool IntervalOfContainer::retrieve(const char* filename){
        //open the archive
#ifdef BOOST_GZIP
        IntervalOfContainer* itc=this;
      std::ifstream ifs(filename, std::ios::in|std::ios::binary);
                //std::ifstream ifs(filename, std::ios::in);
        assert(ifs.good()); // XXX catch if file not found
        boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
        in.push(boost::iostreams::gzip_decompressor());
        in.push(ifs);
        //boost::archive::binary_iarchive ia(in);
                //boost::archive::text_iarchive ia(in);
                eos::portable_iarchive ia(in);
        ia >> *itc;
        return true;
#else
        IntervalOfContainer* itc=this;
        std::ifstream ifs(filename);
        if(ifs.fail())return false;
        boost::archive::text_iarchive ia(ifs);
       //eos::portable_iarchive ia(ifs);
        // restore the schedule from the archive
                                QT_TRY {
        ia >> *itc;
        }QT_CATCH(eos::portable_archive_exception e) {
                                printf("fail while restoring serialized file ! %s %s\n", e.what(), filename);

                                return false;
                        }
        return true;
#endif
    }
#endif

#endif
