/**
 *
 * @file src/trace/Serializer_types.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_TYPES
#define SERIALIZER_TYPES

#include "trace/Serializer.hpp"

#include <limits>
#include <float.h>
#include "trace/EntityTypes.hpp"
#include "trace/EntityValue.hpp"
#include "trace/values/Values.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/map.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <trace/Serializer.hpp>
#include "trace/Serializer_values.hpp"
using namespace std;

// serialize in this file all EntityTypes

using namespace boost::serialization;

// EntityType serialization methods

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

template <class Archive>
void save(Archive &ar, const EntityType &l, const unsigned int) {
    ar &l.get_name();
    int type_uid = Serializer<ContainerType>::Instance().getUid(l.get_container_type());
    ar &type_uid;
    std::map<std::string, Value *> *opt = l.get_extra_fields();
    ar &opt;

    // experimental

    const std::map<Name, EntityValue *> *values = l.get_values();
    ar &values;
    type_uid = Serializer<EntityType>::Instance().getUid(&l);
    ar &type_uid;
}

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

    Name _name;
    const ContainerType *_container_type;
    std::map<std::string, Value *> *_extra_fields;
    std::map<Name, EntityValue *> *_values;
    int _uid;

    ar &_name;
    int type_uid;
    ar &type_uid;
    _container_type = Serializer<ContainerType>::Instance().getValue(type_uid);
    ar &_extra_fields;

    ar &_values;
    ar &_uid;
    Serializer<EntityType>::Instance().setUid(_uid, &l);

    l.set_name(_name);
    l.set_container_type(const_cast<ContainerType *>(_container_type));
    l.set_extra_fields(_extra_fields);
    l.set_values(*_values);
    // new(&l) EntityType(_name,const_cast<ContainerType*> _container_type, *_extra_fields); //sets the value in the object pointed by l
}

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

// ContainerType serialization methods

BOOST_SERIALIZATION_SPLIT_FREE(ContainerType)

template <class Archive>
void save(Archive &ar, const ContainerType &l, const unsigned int) {
    ar &l.get_name();
    const ContainerType *c = l.get_parent();
    ar &c;
    const std::list<ContainerType *> *_children = l.get_children();
    ar &*_children;
    int uid = Serializer<ContainerType>::Instance().getUid(&l);
    ar &uid;
}

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

    Name _name;
    ContainerType *_parent;
    std::list<ContainerType *> _children;
    int _uid;

    ar &_name;
    ar &_parent;
    ar &_children;
    ar &_uid;
    Serializer<ContainerType>::Instance().setUid(_uid, &l);

    new (&l) ContainerType(_name, _parent);
    std::list<ContainerType *>::const_iterator it_end = _children.end();
    for (std::list<ContainerType *>::iterator it = _children.begin(); it != it_end; it++) {
        l.add_child(*it); // add children
    }
}

// EventType serialization methods
template <class Archive>
void serialize(Archive &ar, EventType &l, const unsigned int) {
    ar &boost::serialization::base_object<EntityType>(l);
}

// StateType serialization methods

template <class Archive>
void serialize(Archive &ar, StateType &l, const unsigned int) {
    ar &boost::serialization::base_object<EntityType>(l);
}

// LinkType serialization methods

BOOST_SERIALIZATION_SPLIT_FREE(LinkType)

template <class Archive>
void save(Archive &ar, const LinkType &l, const unsigned int) {
    ar &boost::serialization::base_object<EntityType>(l);
    int type_uid = Serializer<ContainerType>::Instance().getUid(l.get_source_type());
    ar &type_uid;
    type_uid = Serializer<ContainerType>::Instance().getUid(l.get_destination_type());
    ar &type_uid;
}

template <class Archive>
void load(Archive &ar, LinkType &l, const unsigned int) {
    ar &boost::serialization::base_object<EntityType>(l);
    int uid;
    ar &uid;
    const ContainerType *c = Serializer<ContainerType>::Instance().getValue(uid);
    l.set_source_type(const_cast<ContainerType *>(c));
    ar &uid;
    const ContainerType *d = Serializer<ContainerType>::Instance().getValue(uid);
    l.set_destination_type(const_cast<ContainerType *>(d));
}

// VariableType serialization methods
template <class Archive>
void serialize(Archive &ar, VariableType &l, const unsigned int) {
    ar &boost::serialization::base_object<EntityType>(l);
}

BOOST_SERIALIZATION_SPLIT_FREE(EntityValue)

BOOST_CLASS_TRACKING(EntityValue, track_never)

template <class Archive>
void save(Archive &ar, const EntityValue &l, const unsigned int) {
    Name n = l.get_name();
    ar &n;
    // const EntityType* t=l.get_type();
    // ar & t;
    int uid = Serializer<EntityType>::Instance().getUid(l.get_type());
    ar &uid;
    const std::map<std::string, Value *> *g = l.get_extra_fields();
    ar &g;

    uid = Serializer<EntityValue>::Instance().getUid(&l);
    ar &uid;
}
template <class Archive>
void load(Archive &ar, EntityValue &l, const unsigned int) {
    Name _name;
    const EntityType *_type;
    std::map<std::string, Value *> *_opt;

    ar &_name;
    int uid;
    ar &uid;
    _type = Serializer<EntityType>::Instance().getValue(uid);
    // ar & _type;
    ar &_opt;
    ar &uid;
    Serializer<EntityValue>::Instance().setUid(uid, &l);

    new (&l) EntityValue(_name, const_cast<EntityType *>(_type), *_opt);
}

#endif
