/*
creg - Code compoment registration system
Copyright 2005 Jelmer Cnossen
*/

#ifndef jc_CR_HEADER
#define jc_CR_HEADER

#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>

#include "ISerializer.h"
#include "Sync/SyncedPrimitive.h"

namespace creg {

	class IType;
	class Class;
	class ClassBinder;

	typedef unsigned int uint;

	// Fundamental/basic types
	enum BasicTypeID
	{
		crInt,		crUInt,
		crShort,	crUShort,
		crChar,		crUChar,
		crFloat,
		crDouble,
		crBool,
#if defined(SYNCDEBUG) || defined(SYNCCHECK)
		crSyncedSint,   crSyncedUint,
		crSyncedSshort, crSyncedUshort,
		crSyncedSchar,  crSyncedUchar,
		crSyncedFloat,
		crSyncedDouble,
		crSyncedBool,
#endif
	};

	class IType
	{
	public:
		// Type interface can go here...
		virtual ~IType();

		virtual void Serialize (ISerializer* s, void *instance) = 0;
		virtual std::string GetName () = 0;

		static boost::shared_ptr<IType> CreateBasicType (BasicTypeID t);
		static boost::shared_ptr<IType> CreateStringType ();
		static boost::shared_ptr<IType> CreateObjInstanceType (Class *objectType);
		static boost::shared_ptr<IType> CreateEnumeratedType (size_t size);
	};

	class IMemberRegistrator
	{
	public:
		virtual ~IMemberRegistrator();
		virtual void RegisterMembers (Class *cls) = 0;
	};

	enum ClassFlags {
		CF_None = 0,
		CF_Abstract = 4
	};

	struct _DummyStruct {};

	/** Class member flags to use with CR_MEMBER_SETFLAG */
	enum ClassMemberFlag {
		CM_NoSerialize = 1, /// Make the serializers skip the member
		CM_Config = 2,  // Exposed in config
	};

/**
 * Stores class bindings such as constructor/destructor
 */
	class ClassBinder
	{
	public:
		ClassBinder (const char *className, unsigned int cf, ClassBinder* base, IMemberRegistrator **mreg, int instanceSize, void (*constructorProc)(void *instance), void (*destructorProc)(void *instance));

		Class *class_;
		ClassBinder *base;
		ClassFlags flags;
		IMemberRegistrator **memberRegistrator;
		const char *name;
		int size; // size of an instance in bytes
		void (*constructor)(void *instance);
		void (*destructor)(void *instance); // needed for classes without virtual destructor (classes/structs declared with CR_DECLARE_STRUCT)

		ClassBinder* nextBinder;
	};

	class System
	{
	public:
		/// Return the global list of classes
		static std::vector<Class*> GetClasses() { return classes; }
		/// Initialization of creg, collects all the classes and initializes metadata
		static void InitializeClasses ();
		/// Shutdown of creg
		static void FreeClasses ();
		/// Find a class by name
		static Class* GetClass(const std::string& name);

		static void AddClassBinder(ClassBinder* cb);

	protected:
		static ClassBinder *binderList;
		static std::vector<Class*> classes;
	};

	/** Represents a C++ class or struct, declared with CR_DECLARE/CR_DECLARE_STRUCT */
	class Class
	{
	public:
		struct Member
		{
			const char* name;
			boost::shared_ptr<IType> type;
			unsigned int offset;
			int flags; // combination of ClassMemberFlag's
		};

		Class ();
		~Class ();

		/// Returns true if this class is equal to or derived from other
		bool IsSubclassOf (Class* other);
		void DeleteInstance (void *inst);
		/// Allocate an instance of the class
		void* CreateInstance ();
		/// Calculate a checksum from the class metadata
		void CalculateChecksum (unsigned int& checksum);
		void AddMember (const char *name, boost::shared_ptr<IType> type, unsigned int offset);
		void AddMember (const char *name, IType* type, unsigned int offset);
		void SetMemberFlag (const char *name, ClassMemberFlag f);
		Member* FindMember (const char *name);

		void BeginFlag (ClassMemberFlag flag);
		void EndFlag (ClassMemberFlag flag);

		bool IsAbstract() { return (binder->flags & CF_Abstract) != 0; }

		std::vector<Class*> GetImplementations(); // get all concrete classes that implement this class

		std::vector <Member*> members;
		std::vector <Class*> derivedClasses; // all classes that derive from this class
		ClassBinder* binder;
		std::string name;
		Class *base;
		void (_DummyStruct::*serializeProc)(ISerializer& s);
		void (_DummyStruct::*postLoadProc)();

		friend class ClassBinder;
	};

// -------------------------------------------------------------------
// Container Type templates
// -------------------------------------------------------------------

	// vector,deque container
	template<typename T>
	class DynamicArrayType : public IType
	{
	public:
		typedef typename T::iterator iterator;
		typedef typename T::value_type ElemT;

		boost::shared_ptr<IType> elemType;

		DynamicArrayType (boost::shared_ptr<IType> elemType) : elemType(elemType) {}
		~DynamicArrayType () {}

		void Serialize (ISerializer *s, void *inst) {
			T& ct = *(T*)inst;
			if (s->IsWriting ()) {
				int size = (int)ct.size();
				s->SerializeInt (&size,sizeof(int));
				for (int a=0;a<size;a++)
					elemType->Serialize (s, &ct[a]);
			} else {
				int size;
				s->SerializeInt (&size, sizeof(int));
				ct.resize (size);
				for (int a=0;a<size;a++)
					elemType->Serialize (s, &ct[a]);
			}
		}
		std::string GetName() { return elemType->GetName() + "[]"; }
	};

	class StaticArrayBaseType : public IType
	{
	public:
		boost::shared_ptr<IType> elemType;
		int size, elemSize;

		StaticArrayBaseType(boost::shared_ptr<IType> et, int Size, int ElemSize) :
			elemType(et), size(Size), elemSize(ElemSize) {}
		~StaticArrayBaseType() {}

		std::string GetName();
	};

	template<typename T, int Size>
	class StaticArrayType : public StaticArrayBaseType
	{
	public:
		typedef T ArrayType[Size];
		StaticArrayType(boost::shared_ptr<IType> elemType) : StaticArrayBaseType(elemType, Size, sizeof(ArrayType)/Size) {}
		void Serialize (ISerializer *s, void *instance)
		{
			T* array = (T*)instance;
			for (int a=0;a<Size;a++)
				elemType->Serialize (s, &array[a]);
		}
	};

	class EmptyType : public IType
	{
	public:
		int size;
		EmptyType(int Size) {size=Size;}
		~EmptyType() {}

		void Serialize (ISerializer* s, void *instance)
		{
			for (int a=0;a<size;a++) {
				char c=0;
				s->Serialize(&c,1);
			}
		}
		std::string GetName ()
		{
			return "void";
		}
	};

#include "TypeDeduction.h"

/** @def CR_DECLARE
 * Add the definitions for creg binding to the class
 * this should be put within the class definition */
#define CR_DECLARE(TCls)	public:					\
	static creg::ClassBinder binder;				\
	static creg::IMemberRegistrator *memberRegistrator;	 \
	static void _ConstructInstance(void* d);			\
	static void _DestructInstance(void* d);			\
	typedef TCls MyType;							\
	friend struct TCls##MemberRegistrator;			\
	virtual creg::Class* GetClass();				\
	inline static creg::Class *StaticClass() { return binder.class_; }

/** @def CR_DECLARE_STRUCT
 * Use this to declare a structure
 * this should be put in the class definition, instead of CR_DECLARE
 * For creg, the only difference between a class and a structure is having a vtable or not
 */
#define CR_DECLARE_STRUCT(TStr)		public:			\
	static creg::ClassBinder binder;				\
	typedef TStr MyType;							\
	static creg::IMemberRegistrator *memberRegistrator;	\
	static void _ConstructInstance(void* d);			\
	static void _DestructInstance(void* d);			\
	friend struct TStr##MemberRegistrator;			\
	creg::Class* GetClass();						\
	inline static creg::Class *StaticClass() { return binder.class_; }

/** @def CR_DECLARE_SUB
 *  @author Tobi Vollebregt
 * Use this to declare a sub class. This should be put in the class definition
 * of the superclass, alongside CR_DECLARE(CSuperClass) (or CR_DECLARE_STRUCT). */
#define CR_DECLARE_SUB(cl) \
	struct cl##MemberRegistrator;

/** @def CR_BIND_DERIVED
 * Bind a derived class declared with CR_DECLARE to creg
 * Should be used in the source file
 * @param TCls class to bind
 * @param TBase base class of TCls
 */
#define CR_BIND_DERIVED(TCls, TBase, ctor_args) \
	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
	creg::Class* TCls::GetClass() { return binder.class_; } \
	void TCls::_ConstructInstance(void *d) { new(d) MyType ctor_args; } \
	void TCls::_DestructInstance(void *d) { ((MyType*)d)->~MyType(); } \
	creg::ClassBinder TCls::binder(#TCls, 0, &TBase::binder, &TCls::memberRegistrator, sizeof(TCls), TCls::_ConstructInstance, TCls::_DestructInstance);

/** @def CR_BIND_DERIVED_SUB
 * Bind a derived class inside another class to creg
 * Should be used in the source file
 * @param TSuper class that contains the class to register
 * @param TCls subclass to bind
 * @param TBase base class of TCls
 */
#define CR_BIND_DERIVED_SUB(TSuper, TCls, TBase, ctor_args) \
	creg::IMemberRegistrator* TSuper::TCls::memberRegistrator=0;	 \
	creg::Class* TSuper::TCls::GetClass() { return binder.class_; }  \
	void TSuper::TCls::_ConstructInstance(void *d) { new(d) TCls ctor_args; }  \
	void TSuper::TCls::_DestructInstance(void *d) { ((TCls*)d)->~TCls(); }  \
	creg::ClassBinder TSuper::TCls::binder(#TSuper "::" #TCls, 0, &TBase::binder, &TSuper::TCls::memberRegistrator, sizeof(TSuper::TCls), TSuper::TCls::_ConstructInstance, TSuper::TCls::_DestructInstance);

/** @def CR_BIND
 * Bind a class not derived from CObject
 * should be used in the source file
 * @param TCls class to bind
 */
#define CR_BIND(TCls, ctor_args) \
	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
	creg::Class* TCls::GetClass() { return binder.class_; } \
	void TCls::_ConstructInstance(void *d) { new(d) MyType ctor_args; } \
	void TCls::_DestructInstance(void *d) { ((MyType*)d)->~MyType(); } \
	creg::ClassBinder TCls::binder(#TCls, 0, 0, &TCls::memberRegistrator, sizeof(TCls), TCls::_ConstructInstance, TCls::_DestructInstance);

/** @def CR_BIND_DERIVED_INTERFACE
 * Bind an abstract derived class
 * should be used in the source file
 * @param TCls abstract class to bind
 * @param TBase base class of TCls
 */
#define CR_BIND_DERIVED_INTERFACE(TCls, TBase)	\
	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
	creg::Class* TCls::GetClass() { return binder.class_; } \
	creg::ClassBinder TCls::binder(#TCls, (unsigned int)creg::CF_Abstract, &TBase::binder, &TCls::memberRegistrator, sizeof(TCls), 0, 0);

/** @def CR_BIND_INTERFACE
 * Bind an abstract class
 * should be used in the source file
 * This simply doesn't register a constructor to creg, so you can bind non-abstract class as abstract classes as well.
 * @param TCls abstract class to bind */
#define CR_BIND_INTERFACE(TCls)	\
	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
	creg::Class* TCls::GetClass() { return binder.class_; } \
	creg::ClassBinder TCls::binder(#TCls, (unsigned int)creg::CF_Abstract, 0, &TCls::memberRegistrator, sizeof(TCls), 0, 0);

/** @def CR_REG_METADATA
 * Binds the class metadata to the class itself
 * should be used in the source file
 * @param TClass class to register the info to
 * @param Members the metadata of the class\n
 * should consist of a series of single expression of metadata macros\n
 * for example: (CR_MEMBER(a),CR_POSTLOAD(PostLoadCallback))
 * @see CR_MEMBER
 * @see CR_ENUM_MEMBER
 * @see CR_SERIALIZER
 * @see CR_POSTLOAD
 * @see CR_MEMBER_SETFLAG
 */
#define CR_REG_METADATA(TClass, Members)				\
	struct TClass##MemberRegistrator : creg::IMemberRegistrator {\
	typedef TClass Type;						\
	TClass##MemberRegistrator() {				\
		Type::memberRegistrator=this;				\
	}												\
	void RegisterMembers(creg::Class* class_) {		\
		TClass* null=(Type*)0;						\
		(void)null; /*suppress compiler warning if this isn't used*/	\
		Members; }									\
	} static TClass##mreg;

/** @def CR_REG_METADATA_SUB
 *  @author Tobi Vollebregt
 * Just like CR_REG_METADATA, but for a subclass.
 *  @see CR_REG_METADATA
 */
#define CR_REG_METADATA_SUB(TSuperClass, TSubClass, Members)				\
	struct TSuperClass::TSubClass##MemberRegistrator : creg::IMemberRegistrator {\
	typedef TSuperClass::TSubClass Type;						\
	TSubClass##MemberRegistrator() {				\
		Type::memberRegistrator=this;				\
	}												\
	void RegisterMembers(creg::Class* class_) {		\
		Type* null=(Type*)0;						\
		Members; }									\
	} static TSuperClass##TSubClass##mreg;

/** @def CR_MEMBER
 * Registers a class/struct member variable, of a type that is:
 * - a struct registered with CR_DECLARE_STRUCT/CR_BIND_STRUCT
 * - a class registered with CR_DECLARE/CR_BIND*
 * - an int,short,char,long,double,float or bool, or any of the unsigned variants of those
 * - a std::set/multiset included with STL_Set.h
 * - a std::list included with STL_List.h
 * - a std::deque included with STL_Deque.h
 * - a std::map/multimap included with STL_Map.h
 * - a std::vector
 * - a std::string
 * - an array
 * - a pointer/reference to a creg registered struct or class instance
 * For enumerated type members, @see CR_ENUM_MEMBER
 */
#define CR_MEMBER(Member) \
	class_->AddMember ( #Member, creg::GetType (null->Member), (unsigned int)(((char*)&null->Member)-((char*)0)))

/** @def CR_ENUM_MEMBER
 * Registers a class/struct member variable with an enumerated type */
#define CR_ENUM_MEMBER(Member) \
	class_->AddMember ( #Member, creg::IType::CreateEnumeratedType(sizeof(null->Member)), (unsigned int)(((char*)&null->Member)-((char*)0)))

/** @def CR_RESERVED
 *  @author Victor Muraviev
 * Registers a unused space for compatibility
 * Size = 1:
 * - char, synced char, bool, synced bool
 * - pointer
 * - enum
 * Size = 2:
 * - short, synced short
 * - enum
 * Size = 4:
 * - int, synced int, long, synced long, float, synced float
 * - std::set/multiset
 * - std::list
 * - std::deque
 * - std::map/multimap
 * - std::vector
 * - std::string
 * - enum
 * Size = 8:
 * - double, synced double
 */

#define CR_RESERVED(Size) \
	class_->AddMember ( "Reserved", new creg::EmptyType(Size), 0)

/** @def CR_MEMBER_SETFLAG
 * Set a flag for a class/struct member
 * This should come after the CR_MEMBER or CR_ENUM_MEMBER for the member
 * @param Member the class member variable
 * @param Flag the class member flag @see ClassMemberFlag
 * @see ClassMemberFlag */
#define CR_MEMBER_SETFLAG(Member, Flag) \
	class_->SetMemberFlag (#Member, creg::Flag)

#define CR_MEMBER_BEGINFLAG(Flag) \
	class_->BeginFlag(creg::Flag)

#define CR_MEMBER_ENDFLAG(Flag) \
	class_->EndFlag(creg::Flag)

/** @def CR_SERIALIZER
 * Registers a custom serialization method for the class/struct
 * this function will be called when an instance is serialized.
 * There can only be one serialize method per class/struct.
 * On serialization, the registered members will be serialized first,
 * and then this function will be called if specified
 *
 * @param SerializeFunc the serialize method, should be a member function of the class
 */
#define CR_SERIALIZER(SerializeFunc) \
	(class_->serializeProc = (void(creg::_DummyStruct::*)(creg::ISerializer&))&Type::SerializeFunc)

/** @def CR_POSTLOAD
 * Registers a custom post-loading method for the class/struct
 * this function will be called during package loading when all serialization is finished
 * There can only be one postload method per class/struct */
#define CR_POSTLOAD(PostLoadFunc) \
	(class_->postLoadProc = (void(creg::_DummyStruct::*)())&Type::PostLoadFunc)
};

#endif
