#ifndef _RUTYPE_OBJECT_H_
#define _RUTYPE_OBJECT_H_

// Base types
#include "../../Core/Type/RuType_Base.h"

// Memory manager
#include "../../Core/Memory/RuMemoryManager.h"

// Base exceptions
#include "../../Core/Exception/RuExceptions.h"

// Base collections
#include "../../Core/Collection/RuCollections.h"
#include "../../Core/Collection/RuCollectionsHash.h"
#include "../../Core/Collection/RuCollectionsLRU.h"

// Archive and stream IO
#include "../../Core/Stream/RuArchiveIO.h"
#include "../../Core/Stream/RuStreamIO.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

#define ruCLASSGUID_DECLARE																					\
	public:																									\
		virtual const CRuGUID&		GetClassGUID() const { return s_classGUID; }							\
		static const CRuGUID		s_classGUID;

#define ruCLASSGUID_DEFINE(name, long0, long1, long2, long3)												\
	const CRuGUID name::s_classGUID(long0, long1, long2, long3);

// ************************************************************************************************************************************************************

#define ruOBJECTMODULE_DEFINE(moduleName, className)														\
	class moduleName : public IRuObjectModule																\
	{																										\
	public:																									\
		virtual									~moduleName() { }											\
																											\
		virtual const CRuGUID&					GetClassGUID() const { return className::s_classGUID; }		\
		virtual IRuObject*						CreateObject() const { return new className(); }			\
	};

// ************************************************************************************************************************************************************

#define ruSAFE_RELEASE(x) if(x) { x->Release(); x = NULL; }

// ************************************************************************************************************************************************************

extern BOOL					RuReadGUID(IRuStream *stream, CRuGUID &guidOut);

// ************************************************************************************************************************************************************

/*!
	Base object specification for the system object model.

	@version 2004/08/01
*/
class IRuObject : public IRuReferenceCounted
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	char*									m_resourceName;									//!< Resource name of the object
	CRuGUID									m_guid;											//!< The object's run-time GUID

public:
	RUAPI									IRuObject();
	RUAPI virtual							~IRuObject();

	RUAPI const char*						GetResourceName() const;
	RUAPI void								SetResourceName(const char *resourceName);

	// Overidable interface
	RUAPI virtual const CRuGUID&			GetGUID() const;								//!< Returns the unique identifier for this object
	RUAPI virtual IRuObject*				Clone(IRuObject *clonedObject = NULL);			//!< Clones the object

	// Serialization interface
	RUAPI virtual BOOL						SerializeFrom(IRuStream *stream);				//!< Serializes the object from the stream
	RUAPI virtual BOOL						SerializeTo(IRuStream *stream);					//!< Serializes the object to the stream
};

// ************************************************************************************************************************************************************

class IRuObjectModule
{
public:
	RUAPI virtual							~IRuObjectModule() { }

	RUAPI virtual const CRuGUID&			GetClassGUID() const = 0;						// Gets the class GUID of the object this module creates
	RUAPI virtual IRuObject*				CreateObject() const = 0;						// Creates an object
};

// ************************************************************************************************************************************************************

class CRuObjectFactory
{
protected:
	CRuHashMap<CRuGUID, IRuObjectModule *>*	m_moduleHash;

public:
	RUAPI 									CRuObjectFactory();
	RUAPI 									~CRuObjectFactory();

	RUAPI void								AttachModule(IRuObjectModule *module);			// Attaches specified module
	RUAPI IRuObject*						CreateObject(const CRuGUID &classID);			// Creates an object from the class ID
	RUAPI IRuObject*						CreateObject(IRuStream *stream);				// Creates an object from the stream

protected:
	BOOL									ClearModuleDictionary();
	static BOOL								ClearModuleDictionaryCallback(const void *key, const void *data, void *userData);
};

// ************************************************************************************************************************************************************

inline IRuObject::IRuObject()
:	m_resourceName(NULL)
{
}

inline IRuObject::~IRuObject()
{
	// Free the resource name
	delete[] m_resourceName;
}

/*!
	Gets the object's system name. This is usually the resource path from which the object
	is loaded.

	@return The object's system name, or an empty string if the object is unnamed.
*/
inline const char *IRuObject::GetResourceName() const
{
	if(m_resourceName)
	{
		return m_resourceName;
	}

	return "";
}

/*!
	Sets the object's system name. Defined as the resource path from which the object
	is loaded. If the object is not directly loaded as a resource, this method should not
	be called.

	@param resourceName The name that this entity will assume.
*/
inline void IRuObject::SetResourceName(const char *resourceName)
{
	delete[] m_resourceName;

	if(resourceName)
	{
		m_resourceName = ruNEW char [strlen(resourceName) + 1];
		strcpy(m_resourceName, resourceName);
	}
	else
	{
		m_resourceName = NULL;
	}
}

/*!
	Clones the object.

	The clone function of any given subclass must call its parent class' Clone
	function to properly complete the cloning chain. In general, the following
	steps should be followed for cloning:

	1. If clonedObject is NULL, create a new instance of the class being cloned.
	   Otherwise, use the supplied clonedObject.
	2. Call parent class' Clone function using clonedObject as the parameter.
	3. Perform cloning operations on the class.
	4. Return pointer to clonedObject if successful, or NULL if the object cannot
	   be cloned or there is a failure.

	@param clonedObject Pointer to a cloned object previously created by a subclass.

	@return A pointer to the newly cloned object.
*/
inline IRuObject *IRuObject::Clone(IRuObject *clonedObject)
{
	// Create a new cloned object if necessary
	clonedObject = clonedObject ? clonedObject : ruNEW IRuObject();

	// Copy the resource name if it exists
	if(m_resourceName)
	{
		clonedObject->m_resourceName = ruNEW char [strlen(m_resourceName) + 1];
		strcpy(clonedObject->m_resourceName, m_resourceName);
	}

	return clonedObject;
}

inline const CRuGUID &IRuObject::GetGUID() const
{
	return m_guid;
}

inline BOOL IRuObject::SerializeFrom(IRuStream *stream)
{
	return TRUE;
}

inline BOOL IRuObject::SerializeTo(IRuStream *stream)
{
	return TRUE;
}

// ************************************************************************************************************************************************************

extern CRuObjectFactory						g_ruObjectFactory;

// ************************************************************************************************************************************************************

#pragma managed

#endif
