/*
    This file is part of Mitsuba, a physically based rendering system.

    Copyright (c) 2007-2011 by Wenzel Jakob and others.

    Mitsuba is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License Version 3
    as published by the Free Software Foundation.

    Mitsuba 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/

#if !defined(__SERIALIZATION_H)
#define __SERIALIZATION_H

#include <mitsuba/mitsuba.h>

MTS_NAMESPACE_BEGIN

/** \brief Base class of all reference-counted objects with serialization support
 *
 * To support unserialization from a stream, the implementation should use one of the
 * RTTI macros \ref MTS_IMPLEMENT_CLASS_S or \ref MTS_IMPLEMENT_CLASS_IS.
 *
 * \ingroup libcore
 */
class MTS_EXPORT_CORE SerializableObject : public Object {
public:
	/// Unserialize a serializable object
	SerializableObject(Stream *stream, InstanceManager *manager);

	/// Serialize this object to a stream
	virtual void serialize(Stream *stream, InstanceManager *manager) const = 0;

	MTS_DECLARE_CLASS()
protected:
	/// Construct a serializable object
	inline SerializableObject() { }

	/// Virtual deconstructor
	virtual ~SerializableObject() { }
};

/** \brief Coordinates the serialization and unserialization of object graphs
 *
 * When serializaing a complicated object graph to a binary data stream,
 * the instance manager annotates the data stream to avoid serializing
 * objects twice or becoming stuck in a cyclic dependency. This allows
 * arbitrary connected graphs to be serialized.
 *
 * Similarly when unserializing a stream, it ensures that the resulting
 * object graph has the same structure.
 *
 * \ingroup libcore
 */
class MTS_EXPORT_CORE InstanceManager : public Object {
	friend class SerializableObject;
public:
	/// \brief Construct a new instance manager
	InstanceManager();

	/// Retrieve an instance from the given stream
	SerializableObject *getInstance(Stream *stream);

	/// Store an instance to the given stream
	void serialize(Stream *stream, const SerializableObject *inst);

	MTS_DECLARE_CLASS()
private:
	/// Virtual destructor
	virtual ~InstanceManager();

	/// Called from the unserialization constructor of SerializableObject
	void registerInstance(SerializableObject *object);
private:
	unsigned int m_counter, m_lastID;
	std::vector<SerializableObject *> m_fullyAllocated;
	std::map<unsigned int, SerializableObject *> m_idToObj;
	std::map<const SerializableObject *, unsigned int> m_objToId;
};

MTS_NAMESPACE_END

#endif /* __SERIALIZATION_H */
