/*
** 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(eos::portable_archive_exception e) {
				printf("fail while dumping serialized file ! %s %s\n", e.what(), filename);
				return false;
			}QT_CATCH(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
    
