/* +---------------------------------------------------------------------------+
   |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
   |                                                                           |
   |                   http://mrpt.sourceforge.net/                            |
   |                                                                           |
   |   Copyright (C) 2005-2008  University of Malaga                           |
   |                                                                           |
   |    This software was written by the Perception and Robotics               |
   |      research group, University of Malaga (Spain).                        |
   |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
   |                                                                           |
   |  This file is part of the MRPT project.                                   |
   |                                                                           |
   |     MRPT is free software: you can redistribute it and/or modify          |
   |     it under the terms of the GNU General Public License as published by  |
   |     the Free Software Foundation, either version 3 of the License, or     |
   |     (at your option) any later version.                                   |
   |                                                                           |
   |   MRPT is distributed in the hope that it will be useful,                 |
   |     but WITHOUT ANY WARRANTY; without even the implied warranty of        |
   |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
   |     GNU General Public License for more details.                          |
   |                                                                           |
   |     You should have received a copy of the GNU General Public License     |
   |     along with MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
   |                                                                           |
   +---------------------------------------------------------------------------+ */
#ifndef  CSERIALIZABLE_H
#define  CSERIALIZABLE_H

#include <MRPT/UTILS/utils_defs.h>
#include <MRPT/UTILS/CStream.h>
#include <MRPT/UTILS/safe_pointers.h>

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

	/** An structure that holds runtime class type information. Use CLASS_ID(<class_name>) to get a reference to the class_name's TRuntimeClassId descriptor.
	  */
	struct TRuntimeClassId
	{
		const 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.
		  */
		const TRuntimeClassId*		(*getBaseClass)();

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

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

	/** A wrapper class for a "TRuntimeClassId *", well-defined with respect to copy operators and constructors.
	  */
	typedef safe_ptr<TRuntimeClassId> TRuntimeClassIdPtr;

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

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

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

	/** Access to runtime class ID for a defined class name.
	  */
	#define CLASS_ID_NAMESPACE(class_name,namespaceName) static_cast<const UTILS::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) static_cast<const UTILS::TRuntimeClassId*>(& template <class T> class_name<T>::class##class_name)

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

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

	/**  This declaration must be inserted in all CSerializable classes definition, after the class declaration.
  	  */
	#define DEFINE_SERIALIZABLE_POST(class_name) \
		UTILS::CStream& operator>>(UTILS::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 static_cast<CSerializable*>( new class_name ); } \
		const UTILS::TRuntimeClassId* class_name::_GetBaseClass() \
			{ return CLASS_ID(base); } \
		UTILS::TRuntimeClassId class_name::class##class_name = { \
			#class_name, sizeof(class class_name), class_name::CreateObject, \
				&class_name::_GetBaseClass }; \
		const UTILS::TRuntimeClassId* class_name::GetRuntimeClass() const \
			{ return CLASS_ID(class_name); } \
		UTILS::CLASSINIT class_name::_init_##class_name(CLASS_ID(class_name)); \
		UTILS::CStream& NameSpace::operator>>(UTILS::CStream& in, class_name* &pObj) \
		{ pObj = static_cast<class_name*>( in.ReadObject() ); return in; }


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

  	/** This must be inserted as implementation of some required members for
	  *  virtual CSerializable classes:
	  */
	#define IMPLEMENTS_VIRTUAL_SERIALIZABLE(class_name, base_class_name,NameSpace) \
		const UTILS::TRuntimeClassId* class_name::_GetBaseClass() \
			{ return CLASS_ID(base_class_name); } \
		const UTILS::TRuntimeClassId class_name::class##class_name = { \
			#class_name, sizeof(class class_name), NULL, \
				&class_name::_GetBaseClass }; \
		const UTILS::TRuntimeClassId* class_name::GetRuntimeClass() const \
			{ return CLASS_ID(class_name); } \
		UTILS::CStream& NameSpace::operator>>(UTILS::CStream& in, class_name* &pObj) \
		{ pObj = static_cast<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 const  UTILS::TRuntimeClassId* _GetBaseClass(); \
		static UTILS::CLASSINIT _init_##class_name;\
		void  writeToStream(UTILS::CStream &out, int *getVersion) const;\
		void  readFromStream(UTILS::CStream &in, int version);\
	public: \
		static  UTILS::TRuntimeClassId class##class_name##T; \
		virtual const UTILS::TRuntimeClassId* GetRuntimeClass() const; \
		static  CSerializable* CreateObject(); \
		friend UTILS::CStream& operator>>(UTILS::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() const \
			{ return (CSerializable*)new class_name<T>; } \
		template <class T> UTILS::TRuntimeClassId* class_name<T>::_GetBaseClass() \
			{ return CLASS_ID(base); } \
		template <class T> UTILS::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> UTILS::TRuntimeClassId* class_name<T>::GetRuntimeClass() const \
			{ return CLASS_ID(class_name<T>); } \
		template <class T> UTILS::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="http://babel.isa.uma.es/mrpt/index.php/Serialization" >serialization</a>.
	 * \sa CStream
	 */
	class CSerializable
	{
	protected:
		static  UTILS::TRuntimeClassId* _GetBaseClass();
	public:
		static const UTILS::TRuntimeClassId classCSerializable;
		friend class UTILS::CStream;

		/** Returns information about the class of an object in runtime.
		  */
		virtual const UTILS::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(UTILS::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(UTILS::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="http://babel.isa.uma.es/mrpt/index.php/Integration_with_BABEL">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="http://babel.isa.uma.es/mrpt/index.php/Integration_with_BABEL">Integration with BABEL</a>
	 */
	void StringToObject(std::string &str, CSerializable **obj);


} // End of namespace

//UTILS::CStream& operator>>(UTILS::CStream& in, CSerializable* &pObj);


#endif
