#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
