// Infrastructure for serializable objects.
//
// How to make objects of class Foo serializable:
//
//    1. Derive Foo (directly or indirectly) from SerialObj.
//    2. Add a SER_FOO constant to SerialTypes below.
//    3. Add DECLARE_SERIAL(Foo) into class definition.
//    4. Add a (preferably protected) default ctor if it doesn't already exist.
//    5. For non-abstract classes, add IMPLEMENT_SERIAL(Foo, SER_FOO) to *.cc
//    6. Add two methods like this to *.cc (keep names of arguments!)
//
//       bool Foo::DoSerialize(SerialInfo* info) const
//           {
//           DO_SERIALIZE(SER_FOO, ParentClassOfFoo);
//           <... serialize class members via methods in Serializer ...>
//           return true if everything ok;
//	     }
//
//       bool Foo::DoUnserialize(UnserialInfo* info)
//           {
//           DO_UNSERIALIZE(ParentClassOfFoo);
//           <... unserialize class members via methods in Serializer ...>
//           return true if everything ok;
//           }
//
//   (7. If no parent class of Foo already contains Serialize()/Unserialize()
//       methods, these need to be added somewhere too. But most of the various
//       parts of the class hierarchy already have them.)


#ifndef SERIALOBJ_H
#define SERIALOBJ_H

#include <map>
#include <util.h>

#include "DebugLogger.h"
#include "Continuation.h"
#include "SerialTypes.h"
#include "config.h"

#if SIZEOF_LONG_LONG < 8
# error "Serialization requires that sizeof(long long) is at least 8. (Remove this message only if you know what you're doing.)"
#endif

class Serializer;
class SerialInfo;
class UnserialInfo;
class SerializationCache;

// Per-process unique ID.
class TransientID {
public:
	TransientID()	{ id = ++counter; }

	typedef unsigned long long ID;
	ID Value() const	{ return id; }

private:
	ID id;
	static ID counter;
};

// Abstract base class for serializable objects.
class SerialObj {
public:
	virtual ~SerialObj()	{ }

	virtual const TransientID* GetTID() const	{ return 0; }

	virtual SerialType GetSerialType() const	{ return 0; }

	bool IsBroObj() const { return IsBroObj(GetSerialType()); }
	bool IsCacheStable() const { return IsCacheStable(GetSerialType()); }

	static const uint64 NEVER = 0;
	static const uint64 ALWAYS = 1;

	// Returns time of last modification. This "time" is a monotonically
	// increasing counter which is incremented each time a modification is
	// performed (more precisely: each time an object is modified which
	// returns something different than NEVER). Such times can thus be
	// compared to see whether some modification took place before another.
	//
	// There are two special values:
	//    NEVER:  This object will never change.
	//    ALWAYS: Always consider this object as changed, i.e., don't
	//            cache it.
	virtual uint64 LastModified() const	{ return NEVER; }

	// Instantiate an object of the given type. Return nil
	// if unknown.
	static SerialObj* Instantiate(SerialType type);

	static const char* ClassName(SerialType type);

	// Associate a "factory" function with the given type.
	// A factory is a class or function that creates instances
	// of a certain type.

	typedef SerialObj* (*FactoryFunc)();
	static void Register(SerialType type, FactoryFunc f,
			const char* class_name);

	static bool IsBroObj(SerialType type)
		{ return type & SER_IS_BRO_OBJ; }

	static bool IsCacheStable(SerialType type)
		{ return type & SER_IS_CACHE_STABLE; }

	static bool CheckTypes(SerialType type1, SerialType type2)
		{ return (type1 & SER_TYPE_MASK_PARENT) ==
			 (type2 & SER_TYPE_MASK_PARENT); }

protected:
	friend class SerializationCache;

	SerialObj()
		{
#ifdef DEBUG
		serial_type = 0;
#endif
		}

	// Serializes this object. If info->cache is false, we can use
	// DECLARE_NON_CACHEABLE_SERIAL (instead of DECLARE_SERIAL) which
	// avoids storing a per-object id.
	bool Serialize(SerialInfo* info) const;

	// Unserializes next object.
	static SerialObj* Unserialize(UnserialInfo* info,
					SerialType type);

	virtual bool DoSerialize(SerialInfo* info) const;
	virtual bool DoUnserialize(UnserialInfo* info);

	typedef std::map<SerialType, FactoryFunc> FactoryMap;
	static FactoryMap* factories;

	typedef std::map<SerialType, const char*> ClassNameMap;
	static ClassNameMap* names;

	static uint64 time_counter;
	static uint64 IncreaseTimeCounter()	{ return ++time_counter; }
	static uint64 GetTimeCounter()	{ return time_counter; }

#ifdef DEBUG
	SerialType serial_type;
#endif
};

// A class that registers a factory function upon instantiation.
class SerialTypeRegistrator {
public:
	SerialTypeRegistrator(SerialType type, SerialObj::FactoryFunc func,
			const char* class_name)
		{
		SerialObj::Register(type, func, class_name);
		}
};


// Macro helpers.

#define DECLARE_ABSTRACT_SERIAL(classname) \
	virtual bool DoSerialize(SerialInfo*) const; \
	virtual bool DoUnserialize(UnserialInfo*); \

#define DECLARE_SERIAL(classname) \
	static classname* Instantiate(); \
	static SerialTypeRegistrator register_type; \
	virtual bool DoSerialize(SerialInfo*) const; \
	virtual bool DoUnserialize(UnserialInfo*); \
	virtual const TransientID*  GetTID() const	{ return &tid; } \
	virtual SerialType GetSerialType() const; \
	TransientID tid;

// Only needed (and usable) for non-abstract classes.
#define IMPLEMENT_SERIAL(classname, classtype) \
	SerialTypeRegistrator classname::register_type(classtype, \
			FactoryFunc(&classname::Instantiate), #classname); \
	SerialType classname::GetSerialType() const { return classtype; }; \
	classname* classname::Instantiate()	{ return new classname(); } \

// Pushes debug level on instantiation and pops when it goes out of scope.
class AutoPush {
public:
	AutoPush()	{ DBG_PUSH(DBG_SERIAL); }
	~AutoPush()	{ DBG_POP(DBG_SERIAL); }
};

// Note that by default we disable suspending.  Use DO_SERIALIZE_WITH_SUSPEND
// to enable, but be careful to make sure that whomever calls us is aware of
// the fact (or has already disabled suspension itself).
#define DO_SERIALIZE(classtype, super) \
	DBG_LOG(DBG_SERIAL, __PRETTY_FUNCTION__); \
	if ( info->type == SER_NONE ) \
		info->type = classtype; \
	DisableSuspend suspend(info); \
	AutoPush auto_push; \
	if ( ! super::DoSerialize(info) ) \
		return false;

// Unfortunately, this is getting quite long. :-(
#define DO_SERIALIZE_WITH_SUSPEND(classtype, super) \
	DBG_LOG(DBG_SERIAL, __PRETTY_FUNCTION__); \
	if ( info->type == SER_NONE ) \
		info->type = classtype; \
	AutoPush auto_push; \
	\
	bool call_super = info->cont.NewInstance(); \
	\
	if ( info->cont.ChildSuspended() ) \
		{ \
		void* user_ptr = info->cont.RestoreState(); \
		if ( user_ptr == &call_super ) \
			call_super = true; \
		} \
	\
	if ( call_super ) \
		{ \
		info->cont.SaveState(&call_super); \
		info->cont.SaveContext(); \
		bool result = super::DoSerialize(info); \
		info->cont.RestoreContext(); \
		if ( ! result ) \
			return false; \
		if ( info->cont.ChildSuspended() ) \
			return true; \
		info->cont.SaveState(0); \
		} \

#define DO_UNSERIALIZE(super) \
	DBG_LOG(DBG_SERIAL, __PRETTY_FUNCTION__); \
	AutoPush auto_push; \
	if ( ! super::DoUnserialize(info) ) \
		return false;

#define SERIALIZE(x) \
	info->s->Write(x, #x)

#define SERIALIZE_STR(x, y) \
	info->s->Write(x, y, #x)

#define SERIALIZE_BIT(bit) \
	info->s->Write(bool(bit), #bit)

#define UNSERIALIZE(x) \
	info->s->Read(x, #x)

#define UNSERIALIZE_STR(x, y) \
	info->s->Read(x, y, #x)

#define UNSERIALIZE_BIT(bit) \
	{ \
	bool tmp; \
	if ( ! info->s->Read(&tmp, #bit) ) \
		return false; \
	bit = (unsigned int) tmp; \
	}

// Some helpers for pointers which may be nil.
#define SERIALIZE_OPTIONAL(ptr) \
	{	\
	if ( ptr )	\
		{	\
		if ( ! info->cont.ChildSuspended() )	\
			if ( ! info->s->Write(true, "has_" #ptr) )	\
				return false;	\
		\
		info->cont.SaveContext();	\
		bool result = ptr->Serialize(info);	\
		info->cont.RestoreContext();	\
		if ( ! result )	\
			return false;	\
		\
		if ( info->cont.ChildSuspended() )	\
			return true;	\
		}	\
	\
	else if ( ! info->s->Write(false, "has_" #ptr) )	\
		return false;	\
	}

#define SERIALIZE_OPTIONAL_STR(str) \
	{	\
	if ( str )	\
		{	\
		if ( ! (info->s->Write(true, "has_" #str) && info->s->Write(str, "str")) )	\
			return false;	\
		}	\
	\
	else if ( ! info->s->Write(false, "has_" #str) )	\
		return false;	\
	}

#define UNSERIALIZE_OPTIONAL(dst, unserialize)	\
	{	\
	bool has_it;	\
	if ( ! info->s->Read(&has_it, "has_" #dst) )	\
		return false;	\
	\
	if ( has_it )	\
		{	\
		dst = unserialize;	\
		if ( ! dst )	\
			return false;	\
		}	\
	\
	else	\
		dst = 0;	\
	}

#define UNSERIALIZE_OPTIONAL_STR(dst)	\
	{	\
	bool has_it;	\
	if ( ! info->s->Read(&has_it, "has_" #dst) )	\
		return false;	\
	\
	if ( has_it )	\
		{	\
		if ( ! info->s->Read(&dst, 0, "has_" #dst) )	\
			return false;	\
		if ( ! dst )	\
			return false;	\
		}	\
	\
	else	\
		dst = 0;	\
	}

#define UNSERIALIZE_OPTIONAL_STR_DEL(dst, del)	\
	{	\
	bool has_it;	\
	if ( ! info->s->Read(&has_it, "has_" #dst) )	\
		{	\
		delete del;	\
		return 0;	\
		}	\
	\
	if ( has_it )	\
		{	\
		if ( ! info->s->Read(&dst, 0, "has_" #dst) )	\
			{	\
			delete del;	\
			return 0;	\
			}	\
		if ( ! dst )	\
			{	\
			delete del;	\
			return 0;	\
			}	\
		}	\
	\
	else	\
		dst = 0;	\
	}

#define UNSERIALIZE_OPTIONAL_STATIC(dst, unserialize, del)	\
	{	\
	bool has_it;	\
	if ( ! info->s->Read(&has_it, "has_" #dst) )	\
		{	\
		delete del;	\
		return 0;	\
		}	\
	\
	if ( has_it )	\
		{	\
		dst = unserialize;	\
		if ( ! dst )	\
			{	\
			delete del;	\
			return 0;	\
			}	\
		}	\
	\
	else	\
		dst = 0;	\
	}

#endif
