#include "../../Core/Type/RuType_Object.h"

#pragma unmanaged

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

ruRTTI_DEFINE_ROOTCLASS(IRuObject)
ruHEAP_DEFINE_ROOTCLASS(IRuObject, "IRuObject")
ruCLASSGUID_DEFINE(IRuObject, 0x905982B3, 0xFF48498a, 0x93334D0A, 0x3CB24A39)

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

BOOL RuReadGUID(IRuStream *stream, CRuGUID &guidOut)
{
	// Read four UINT32 values from the stream to determine class GUID
	UINT32 guidValues[4];

	for(INT32 i = 0; i < 4; ++i)
	{
		if(stream->Read(&guidValues[i], sizeof(UINT32)) != sizeof(UINT32))
		{
			return FALSE;
		}
	}

	guidOut.Set(guidValues[0], guidValues[1], guidValues[2], guidValues[3]);

	return TRUE;
}

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

CRuObjectFactory::CRuObjectFactory()
{
	// Create the hash map for object modules
	m_moduleHash = ruNEW CRuHashMap<CRuGUID, IRuObjectModule *>(8192);
}

CRuObjectFactory::~CRuObjectFactory()
{
	// Destroy all object modules held by the factory
	ClearModuleDictionary();

	// Destroy hash map
	delete m_moduleHash;
}

void CRuObjectFactory::AttachModule(IRuObjectModule *module)
{
	CRuGUID classGUID = module->GetClassGUID();

	// If the factory already has the specified object module, ignore and return
	if(m_moduleHash->ContainsKey(module->GetClassGUID(), module->GetClassGUID().HashCode()))
	{
		return;
	}

	// Add object module to the hash map
	m_moduleHash->Add(module->GetClassGUID(), module->GetClassGUID().HashCode(), module);
}

IRuObject *CRuObjectFactory::CreateObject(const CRuGUID &classID)
{
	// Attempt to locate a proper module to create the object
	IRuObjectModule *module = NULL;
	if(m_moduleHash->Get(classID, classID.HashCode(), module))
	{
		// Return the created object
		return module->CreateObject();
	}

	return NULL;
}

IRuObject *CRuObjectFactory::CreateObject(IRuStream *stream)
{
	// Read class GUID
	CRuGUID classGUID;
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return NULL;
	}

	// Rewind
	stream->Seek(-16, ruSSM_Current);

	// Attempt to locate a proper module to create the object
	IRuObjectModule *module = NULL;
	if(m_moduleHash->Get(classGUID, classGUID.HashCode(), module))
	{
		// Create the object
		IRuObject *object = module->CreateObject();
		
		if(object)
		{
			// Serialize object from stream
			object->SerializeFrom(stream);

			return object;
		}
	}

	return NULL;
}

BOOL CRuObjectFactory::ClearModuleDictionary()
{
	// Release all modules held in the dictionary
	m_moduleHash->ForAllElements(ClearModuleDictionaryCallback, this);

	// Clear all module entries
	m_moduleHash->Clear();

	return TRUE;
}

BOOL CRuObjectFactory::ClearModuleDictionaryCallback(const void *key, const void *data, void *userData)
{
	// Release module
	delete (*((IRuObjectModule **) data));

	return TRUE;
}

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

CRuObjectFactory						g_ruObjectFactory;

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

#pragma managed
