/**
 *
 * @file src/trace/Serializer_structs.hpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Augustin Degomme
 * @author Thibault Soucarre
 *
 * @date 2024-07-17
 */
#ifndef SERIALIZER_STRUCTS
#define SERIALIZER_STRUCTS

#include "trace/Serializer.hpp"

#include <limits>
#include <float.h>
#include "trace/Entitys.hpp"
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <trace/Serializer.hpp>

using namespace std;

// Serialization functions for All Entitys : Links, Events, States, StateChanges and Variables

using namespace boost::serialization;

// Entity serialization methods

/* BOOST_SERIALIZATION_SPLIT_FREE(Entity)//we have 2 functions (load/save) instead of one serialize


template<class Archive>
void save(Archive &ar,const Entity& l, const unsigned int )
{
    int uid=Serializer<Container>::Instance().getUid(l.get_container());
    ar & uid;
    std::map<std::string, Value *>* opt =l.get_extra_fields();
    ar & opt;

}


    template<class Archive>
void load(Archive &ar, Entity& l, const unsigned int )
{

    const Container                *_container;
    std::map<std::string, Value *>* _extra_fields;
    int cont_uid;


    ar & cont_uid;
    _container = Serializer<Container>::Instance().getValue(cont_uid);
    ar & _extra_fields;

    if(_extra_fields==NULL){
        std::map<std::string, Value *> t;
        _extra_fields=&t ; //dirty because this will be discarded in the constructor ...
    }
    new(&l) Entity(_container, *_extra_fields); //sets the value in the object pointed by l
}

BOOST_SERIALIZATION_ASSUME_ABSTRACT(Entity);//this type can be inherited by other serialized classes

*/

// Link serialization methods

BOOST_SERIALIZATION_SPLIT_FREE(Link)

template <class Archive>
void save(Archive &ar, const Link &l, const unsigned int) {

    // ar & boost::serialization::base_object<Entity>(l);
    int uid = Serializer<Container>::Instance().getUid(l.get_container());
    ar &uid;
    const std::map<std::string, Value *> *_extra_fields = l.get_extra_fields();
    ar &_extra_fields;
    Date _start = l.get_start_time();
    ar &_start;
    Date _end = l.get_end_time();
    ar &_end;
    uid = Serializer<EntityType>::Instance().getUid(l.get_type());
    ar &uid;
    // const EntityValue * v= l.get_value();
    //  printf("value : %p\n", v);
    // ar & v;
    uid = Serializer<EntityValue>::Instance().getUid(l.get_value());
    ar &uid;
    uid = Serializer<Container>::Instance().getUid(l.get_source());
    ar &uid;
    uid = Serializer<Container>::Instance().getUid(l.get_destination());
    ar &uid;
}

template <class Archive>
void load(Archive &ar, Link &l, const unsigned int) {
    static std::map<std::string, Value *> *_extra_fields_empty = new std::map<std::string, Value *>();
    const Container *_container;
    const std::map<std::string, Value *> *_extra_fields;
    Date _start, _end;
    const LinkType *_type;
    const EntityValue *_value;
    const Container *_source, *_destination;
    /*ar & boost::serialization::base_object<Entity>(l);
    _container=l.get_container(); //TODO : avoid doing that .. do not use Entitys ?
    _extra_fields=l.get_extra_fields();*/
    int cont_uid;
    ar &cont_uid;
    _container = Serializer<Container>::Instance().getValue(cont_uid);
    ar &_extra_fields;

    ar &_start;
    ar &_end;

    int uid;
    ar &uid;
    _type = (const LinkType *)Serializer<EntityType>::Instance().getValue(uid);
    // ar & _value;
    ar &uid;
    _value = (const EntityValue *)Serializer<EntityValue>::Instance().getValue(uid);
    ar &uid;
    _source = Serializer<Container>::Instance().getValue(uid);
    ar &uid;
    _destination = Serializer<Container>::Instance().getValue(uid);
    if (_extra_fields == NULL) {
        _extra_fields = _extra_fields_empty; // dirty because this will be discarded in the constructor ...
    }
    new (&l) Link(_start, _end, const_cast<LinkType *>(_type), const_cast<Container *>(_container), const_cast<Container *>(_source), const_cast<Container *>(_destination), const_cast<EntityValue *>(_value), *const_cast<std::map<std::string, Value *> *>(_extra_fields));
}

// State serialization methods

BOOST_SERIALIZATION_SPLIT_FREE(State)

template <class Archive>
void save(Archive &ar, const State &l, const unsigned int) {

    // ar & boost::serialization::base_object<Entity>(l);
    int uid = Serializer<Container>::Instance().getUid(l.get_container());
    ar &uid;

    const std::map<std::string, Value *> *_extra_fields = l.get_extra_fields();
    ar &_extra_fields;

    Date d = l.get_start_time();
    ar &d;
    Date d2 = l.get_end_time();
    ar &d2;
    uid = Serializer<EntityType>::Instance().getUid(l.get_type());
    ar &uid;
    // const EntityValue * v= l.get_value();
    //  printf("value : %p\n", v);
    // ar & v;
    uid = Serializer<EntityValue>::Instance().getUid(l.get_value());
    ar &uid;
}

template <class Archive>
void load(Archive &ar, State &l, const unsigned int) {

    static std::map<std::string, Value *> *_extra_fields_empty = new std::map<std::string, Value *>();
    const Container *_container;
    const std::map<std::string, Value *> *_extra_fields;
    Date _start, _end;
    const StateType *_type;
    const EntityValue *_value;

    /*ar & boost::serialization::base_object<Entity>(l);
    _container=l.get_container(); //TODO : avoid doing that .. do not use Entitys ?
    _extra_fields=l.get_extra_fields();*/
    int cont_uid;
    ar &cont_uid;
    _container = Serializer<Container>::Instance().getValue(cont_uid);
    ar &_extra_fields;

    ar &_start;
    ar &_end;
    int uid;
    ar &uid;
    _type = (const StateType *)Serializer<EntityType>::Instance().getValue(uid);
    // ar & _value;
    ar &uid;
    _value = (const EntityValue *)Serializer<EntityValue>::Instance().getValue(uid);
    if (_extra_fields == NULL) {
        _extra_fields = _extra_fields_empty; // dirty because this will be discarded in the constructor ...
    }
    new (&l) State(_start, _end, const_cast<StateType *>(_type), const_cast<Container *>(_container), const_cast<EntityValue *>(_value), *const_cast<std::map<std::string, Value *> *>(_extra_fields));
}

// StateChange serialization methods

BOOST_SERIALIZATION_SPLIT_FREE(StateChange)

template <class Archive>
void save(Archive &ar, const StateChange &l, const unsigned int) {
    const State *s = l.get_left_state();
    ar &s;
    const State *s2 = l.get_right_state();
    ar &s2;
    Date d = l.get_time();
    ar &d;
}

template <class Archive>
void load(Archive &ar, StateChange &l, const unsigned int) {

    Date _time;
    State *_right;
    State *_left;

    ar &_left;
    ar &_right;
    ar &_time;
    new (&l) StateChange(_time, _left, _right);
}

// Variable serialization methods

BOOST_SERIALIZATION_SPLIT_FREE(Variable)

template <class Archive>
void save(Archive &ar, const Variable &l, const unsigned int) {
    // ar & boost::serialization::base_object<Entity>(l);
    const std::map<std::string, Value *> *_extra_fields = l.get_extra_fields();
    int uid = Serializer<Container>::Instance().getUid(l.get_container());
    ar &uid;
    ar &_extra_fields;

    const std::list<std::pair<Date, Double>> *d = l.get_values();
    ar &d;
    uid = Serializer<EntityType>::Instance().getUid(l.get_type());
    ar &uid;
}

template <class Archive>
void load(Archive &ar, Variable &l, const unsigned int) {
    const Container *_container;
    const std::map<std::string, Value *> *_extra_fields;
    std::list<std::pair<Date, Double>> *_values;
    Double _min, _max;
    const VariableType *_type;

    /*ar & boost::serialization::base_object<Entity>(l);
    _container=l.get_container(); //TODO : avoid doing that .. do not use Entitys ?
    _extra_fields=l.get_extra_fields();*/
    int cont_uid;
    ar &cont_uid;
    _container = Serializer<Container>::Instance().getValue(cont_uid);
    ar &_extra_fields;

    ar &_values;

    int uid;
    ar &uid;
    _type = (const VariableType *)Serializer<EntityType>::Instance().getValue(uid);
    new (&l) Variable(const_cast<Container *>(_container), const_cast<VariableType *>(_type));

    std::list<std::pair<Date, Double>>::const_iterator it_end = _values->end();
    for (std::list<std::pair<Date, Double>>::iterator it = _values->begin(); it != it_end; it++) {
        l.add_value((*it).first, (*it).second); // add children
    }
}

BOOST_SERIALIZATION_SPLIT_FREE(Event)

template <class Archive>
void save(Archive &ar, const Event &l, const unsigned int) {
    // ar & boost::serialization::base_object<Entity>(l);
    const std::map<std::string, Value *> *_extra_fields = l.get_extra_fields();
    int uid = Serializer<Container>::Instance().getUid(l.get_container());
    ar &uid;
    ar &_extra_fields;

    Date t = l.get_time();
    ar &t;
    uid = Serializer<EntityType>::Instance().getUid(l.get_type());
    ar &uid;
    // const EntityValue * v= l.get_value();
    //  printf("value : %p\n", v);
    // ar & v;
    uid = Serializer<EntityValue>::Instance().getUid(l.get_value());
    ar &uid;
}
template <class Archive>
void load(Archive &ar, Event &l, const unsigned int) {
    static std::map<std::string, Value *> *_extra_fields_empty = new std::map<std::string, Value *>();
    const Container *_container;
    const std::map<std::string, Value *> *_extra_fields;
    /*ar & boost::serialization::base_object<Entity>(l);
    _container=l.get_container(); //TODO : avoid doing that .. do not use Entitys ?
    _extra_fields=l.get_extra_fields();*/
    int cont_uid;
    ar &cont_uid;
    _container = Serializer<Container>::Instance().getValue(cont_uid);
    ar &_extra_fields;

    Date _time;
    const EventType *_type;
    const EntityValue *_value;
    ar &_time;
    // ar & _type;
    int uid;
    ar &uid;
    _type = (const EventType *)Serializer<EntityType>::Instance().getValue(uid);
    // ar & _value;
    ar &uid;
    _value = (const EntityValue *)Serializer<EntityValue>::Instance().getValue(uid);
    if (_extra_fields == NULL) {
        _extra_fields = _extra_fields_empty; // dirty because this will be discarded in the constructor ...
    }

    new (&l) Event(_time, const_cast<EventType *>(_type), const_cast<Container *>(_container), const_cast<EntityValue *>(_value), *const_cast<std::map<std::string, Value *> *>(_extra_fields));
}

#endif
