/*---------------------------------------------------------------
	FILE: CSerializable.h
	USE: See description below.

   Part of the MRPT Library
   ISA - Universidad de Malaga - http://www.isa.uma.es
  ---------------------------------------------------------------*/
#ifndef  CSERIALIZABLE_H
#define  CSERIALIZABLE_H

#include <MRPT/UTILS/utils_defs.h>
#include <MRPT/UTILS/CStream.h>
#include <MRPT/UTILS/MRPT_OS.h>
#include <MRPT/UTILS/CStdOutStream.h>

/*---------------------------------------------------------------
	Class
  ---------------------------------------------------------------*/
namespace UTILS
{
	class CSerializable;

	/** An structure that holds runtime class type information.
	  */
	struct TRuntimeClassId
	{
		char*					className;
		int						objectSize;
		/** Create an object of the related class, or NULL if it is virtual.
		  */
		CSerializable*			(*ptrCreateObject)();
		/** Gets the base class runtime id.
		  */
		TRuntimeClassId*		(*getBaseClass)();

		// Operations
		CSerializable*			createObject();
		bool					derivedFrom(const TRuntimeClassId* pBaseClass) const;

		// Implementation
		void					writeTo(CStream& out) const;
		static TRuntimeClassId* loadFrom(CStream& in);
	};

	/** Register a class into the MRPT internal list of "CSerializable" descendents.
	  *  Used internally in the macros DEFINE_SERIALIZABLE, etc...
	  * \sa getAllRegisteredClasses
	  */
	void registerClass(TRuntimeClassId* pNewClass);

	/** Returns a list with all the classes registered in the system through UTILS::registerClass.
	  * \sa registerClass
	  */
	std::vector<const TRuntimeClassId*> getAllRegisteredClasses();

	/** Access to runtime class ID for a defined class name.
	  */
	#define CLASS_ID(class_name) ((TRuntimeClassId*)(&class_name::class##class_name))

	/** Access to runtime class ID for a defined class name.
	  */
	#define CLASS_ID_NAMESPACE(class_name,namespaceName) ((TRuntimeClassId*)(&namespaceName::class_name::class##class_name))

	/** Access to runtime class ID for a defined template class name.
	  */
	#define CLASS_ID_TEMPLATE(class_name,T) ((TRuntimeClassId*)(& template <class T> class_name<T>::class##class_name))

	/**
	  */
	struct CLASSINIT
	{
		CLASSINIT(TRuntimeClassId* pNewClass)
		{
			registerClass(pNewClass);
		}
	};

	/** This declaration must be inserted in all CSerializable classes definition:
	  */
	#define DEFINE_SERIALIZABLE(class_name) \
	protected: \
		static  TRuntimeClassId* _GetBaseClass(); \
		static CLASSINIT _init_##class_name;\
		void  writeToStream(CStream &out, int *getVersion) const;\
		void  readFromStream(CStream &in, int version);\
	public: \
		static  TRuntimeClassId class##class_name; \
		virtual TRuntimeClassId* GetRuntimeClass() const; \
		static  CSerializable* CreateObject(); \
		friend CStream& operator>>(CStream& in, class_name* &pObj); \


	/** This must be inserted in all CSerializable classes implementation files:
	  */
	#define IMPLEMENTS_SERIALIZABLE(class_name, base,NameSpace) \
		CSerializable* class_name::CreateObject() \
			{ return (CSerializable*)new class_name; } \
		TRuntimeClassId* class_name::_GetBaseClass() \
			{ return CLASS_ID(base); } \
		TRuntimeClassId class_name::class##class_name = { \
			#class_name, sizeof(class class_name), class_name::CreateObject, \
				&class_name::_GetBaseClass }; \
		TRuntimeClassId* class_name::GetRuntimeClass() const \
			{ return CLASS_ID(class_name); } \
		CLASSINIT class_name::_init_##class_name(CLASS_ID(class_name)); \
		CStream& NameSpace::operator>>(CStream& in, class_name* &pObj) \
		{ pObj = (class_name*) in.ReadObject(); return in; } \

  	/** This declaration must be inserted in virtual CSerializable classes definition:
	  */
	#define DEFINE_VIRTUAL_SERIALIZABLE(class_name) \
	protected: \
		static  TRuntimeClassId* _GetBaseClass(); \
	public: \
		static const TRuntimeClassId class##class_name; \
		virtual TRuntimeClassId* GetRuntimeClass() const; \
		friend class CStream; \
		friend CStream& operator>>(CStream& in, class_name* &pObj); \

  	/** This must be inserted as implementation of some required members for
	  *  virtual CSerializable classes:
	  */
	#define IMPLEMENTS_VIRTUAL_SERIALIZABLE(class_name, base_class_name,NameSpace) \
		TRuntimeClassId* class_name::_GetBaseClass() \
			{ return CLASS_ID(base_class_name); } \
		TRuntimeClassId const class_name::class##class_name = { \
			#class_name, sizeof(class class_name), NULL, \
				&class_name::_GetBaseClass }; \
		TRuntimeClassId* class_name::GetRuntimeClass() const \
			{ return CLASS_ID(class_name); } \
		CStream& NameSpace::operator>>(CStream& in, class_name* &pObj) \
		{ pObj = (class_name*) in.ReadObject(); return in; } \

/*		CStream& NameSpace::operator<<(CStream& out, class_name *obj) \
			{ out.WriteObject(obj);  \
				return out; } \
		CStream& NameSpace::operator>>(CStream& in, class_name* &pObj) \
		{ pObj = (class_name*) in.ReadObject(); \
			return in; } \
*/

	/** This declaration must be inserted in a CSerializable class if it is a template class:
	  */
	#define DEFINE_SERIALIZABLE_TEMPLATE(class_name,T)\
	protected: \
		static  TRuntimeClassId* _GetBaseClass(); \
		static CLASSINIT _init_##class_name;\
		void  writeToStream(CStream &out, int *getVersion) const;\
		void  readFromStream(CStream &in, int version);\
	public: \
		static  TRuntimeClassId class##class_name##T; \
		virtual TRuntimeClassId* GetRuntimeClass() const; \
		static  CSerializable* CreateObject(); \
		friend CStream& operator>>(CStream& in, class_name<T>* &pObj);

	/** This must be inserted in a CSerializable class' implementation files if it is a template class:
	  */
	#define IMPLEMENTS_SERIALIZABLE_TEMPLATE(class_name,base,NameSpace,T) \
		template <class T> CSerializable* class_name<T>::CreateObject() \
			{ return (CSerializable*)new class_name<T>; } \
		template <class T> TRuntimeClassId* class_name<T>::_GetBaseClass() \
			{ return CLASS_ID(base); } \
		template <class T> TRuntimeClassId class_name<T>::class##class_name##T = { \
			#class_name##T, sizeof(class class_name<T>), class_name::CreateObject, \
				&class_name::_GetBaseClass }; \
		template <class T> TRuntimeClassId* class_name<T>::GetRuntimeClass() const \
			{ return CLASS_ID(class_name<T>); } \
		template <class T> CLASSINIT class_name<T>::_init_##class_name##T(CLASS_ID(class_name<T>));\



	/** The virtual base class which provide a unified interface for serialization of objects.
	     Refer to the tutorial about <a href="topic_serialization.html" >serialization</a>.
	 * \sa CStream
	 */
	class CSerializable
	{
	protected:
		static  TRuntimeClassId* _GetBaseClass();
	public:
		static const TRuntimeClassId classCSerializable;
		friend class CStream;

		/** Returns information about the class of an object in runtime.
		  */
		virtual TRuntimeClassId* GetRuntimeClass() const;

		/** Returns a copy of the object, independtly of its class.
		  */
		CSerializable *duplicate() const;

		virtual ~CSerializable();

	protected:
		 /** Introduces a pure virtual method responsible for writing to a CStream.
		  *  This can not be used directly be users, instead use "stream << object;"
		  *   for writing it to a stream.
		  * \param out The output binary stream where object must be dumped.
		  * \param getVersion If NULL, the object must be dumped. If not, only the
		  *		version of the object dump must be returned in this pointer. This enables
		  *     the versioning of objects dumping and backward compatibility with previously
		  *     stored data.
 		  *	\exception std::exception On any error, see CStream::WriteBuffer
		  * \sa CStream
		  */
		virtual void  writeToStream(CStream &out, int *getVersion) const = 0;

		 /** Introduces a pure virtual method responsible for loading from a CStream
		  *  This can not be used directly be users, instead use "stream >> object;"
		  *   for reading it from a stream or "stream >> object_ptr;" if the class is
		  *   unknown apriori.
		  * \param in The input binary stream where the object data must read from.
		  * \param version The version of the object stored in the stream: use this version
		  *                number in your code to know how to read the incoming data.
		  *	\exception std::exception On any error, see CStream::ReadBuffer
		  * \sa CStream
		  */
		virtual void  readFromStream(CStream &in, int version) = 0;


	}; // End of class def.


	/** Used to pass MRPT objects into a CORBA-like object (strings). See doc about "Integration with BABEL".
	 * \param o The object to be serialized.
	 * \return The string containing the binay version of object.
	 * \sa StringToObject, <a href="babel.html">Integration with BABEL</a>
	 */
	std::string ObjectToString(CSerializable *o);

	/** Used to pass CORBA-like objects (strings) into a MRPT object.
	 * \param str An string generated with ObjectToString
	 * \param obj A currently empty pointer, where a pointer to the newly created object will be stored.
	 * \exception None On any internal exception, this function returns NULL.
	 * \sa ObjectToString, <a href="babel.html">Integration with BABEL</a>
	 */
	void StringToObject(std::string &str, CSerializable **obj);


} // End of namespace

#endif
