#include "../../Scene/Paperdoll/RuEntityPaperdoll.h"

#pragma unmanaged

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

ruRTTI_DEFINE_SUBCLASS(CRuPaperdollTemplate, IRuObject)
ruHEAP_DEFINE_SUBCLASS(CRuPaperdollTemplate, "CRuPaperdollTemplate", "IRuObject")
ruCLASSGUID_DEFINE(CRuPaperdollTemplate, 0xA3335E93, 0xD8E34940, 0xAD79A30D, 0x9906E1FC)

ruRTTI_DEFINE_SUBCLASS(CRuPaperdoll, CRuEntity)
ruHEAP_DEFINE_SUBCLASS(CRuPaperdoll, "CRuPaperdoll", "CRuEntity")
ruCLASSGUID_DEFINE(CRuPaperdoll, 0xA098CF24, 0x5A064ee4, 0xB709479D, 0xF2780E94)

ruRTTI_DEFINE_SUBCLASS(CRuPlayerPaperdoll, IRuEntity_Renderable)
ruHEAP_DEFINE_SUBCLASS(CRuPlayerPaperdoll, "CRuPlayerPaperdoll", "IRuEntity_Renderable")
ruCLASSGUID_DEFINE(CRuPlayerPaperdoll, 0xB0AB1669, 0x847B416e, 0xB75E49E9, 0xFE48985F)

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

CRuPaperdollTemplate::CRuPaperdollTemplate()
:	m_modelTemplate(NULL)
{
	m_resourceNamePrefix = "";
	m_resourceNamePostfix = ".ros";

	m_baseItemResourcePath = "";
	m_itemResourcePath = "";
}

CRuPaperdollTemplate::~CRuPaperdollTemplate()
{
	if(m_modelTemplate)
	{
		m_modelTemplate->Release();
		m_modelTemplate = NULL;
	}

	// Iterate through each part structure and clean them up
	for(INT32 i = 0; i < m_parts.Count(); ++i)
	{
		Part *curPart = m_parts[i];

		// Enumerate items from the tree, delete them all
		INT32 numItems = curPart->m_baseItems.Count();
		for(INT32 j = 0; j < numItems; ++j)
		{
			// Retrieve the item from the tree
			BaseItem *item = curPart->m_baseItems.GetAtIndex(j);

			// Iterate through each item code
			INT32 numItemCodes = item->m_validBaseItemCodes.Count();
			for(INT32 k = 0; k < numItemCodes; ++k)
			{
				// Delete item code
				delete[] item->m_validBaseItemCodes[k];
			}

			// Iterate through each derived item
			for(INT32 k = 0; k < item->m_items.Count(); ++k)
			{
				delete item->m_items.GetAtIndex(k);
			}

			// Delete item
			delete item;
		}

		// Clear tree
		curPart->m_baseItems.Clear();

		// Delete part
		delete curPart;
	}

	// Clear part array
	m_parts.Clear();
}

BOOL CRuPaperdollTemplate::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Release old data

	// Read length of base item resource path, including NULL
	INT32 baseItemResourcePathLength;
	stream->Read(&baseItemResourcePathLength, sizeof(INT32));

	// Read base item resource path
	char baseItemResourcePath[4096];
	stream->Read(baseItemResourcePath, baseItemResourcePathLength);
	m_baseItemResourcePath = baseItemResourcePath;

	// Read length of item resource path, including NULL
	INT32 itemResourcePathLength;
	stream->Read(&itemResourcePathLength, sizeof(INT32));

	// Read item resource path
	char itemResourcePath[4096];
	stream->Read(itemResourcePath, itemResourcePathLength);
	m_itemResourcePath = itemResourcePath;

	// Serialize a copy of the model template from the stream
	IRuObject *templateObject = g_ruObjectFactory.CreateObject(stream);
	if(templateObject->GetType() == CRuCompositeModelTemplate::Type)
		m_modelTemplate = static_cast<CRuCompositeModelTemplate *>(templateObject);
	else
	{
		templateObject->Release();
		return FALSE;
	}

	// Read number of parts
	INT32 numParts;
	stream->Read(&numParts, sizeof(INT32));

	// Iterate through each part
	for(INT32 i = 0; i < numParts; ++i)
	{
		// Create part structure
		Part *part = ruNEW Part();

		// Read part ID
		stream->Read(&part->m_partID, sizeof(INT32));

		// Read number of items for this part
		INT32 numItems;
		stream->Read(&numItems, sizeof(INT32));

		// Iterate through each item
		for(INT32 j = 0; j < numItems; ++j)
		{
			// Create item structure
			BaseItem *item = ruNEW BaseItem();

			// Read length of the item name including null
			INT32 itemNameLength;
			stream->Read(&itemNameLength, sizeof(INT32));

			// Read the item name
			char itemName[4096];
			stream->Read(itemName, itemNameLength);

			// Read length of the item prefix string including null
			INT32 itemPrefixLength;
			stream->Read(&itemPrefixLength, sizeof(INT32));

			// Read the item prefix string
			char itemPrefix[4096];
			stream->Read(itemPrefix, itemPrefixLength);
			item->m_baseItemPrefix = itemPrefix;

			// Read number of item codes for this item
			INT32 numItemCodes;
			stream->Read(&numItemCodes, sizeof(INT32));

			// Iterate through each item code
			for(INT32 k = 0; k < numItemCodes; ++k)
			{
				// Read length of the item code string including NULL
				INT32 itemCodeLength;
				stream->Read(&itemCodeLength, sizeof(INT32));

				// Read the item code string
				char *itemCode = ruNEW char [itemCodeLength];
				stream->Read(itemCode, itemCodeLength);

				// Add item code to the list of valid item codes
				item->m_validBaseItemCodes.Add(itemCode);
			}

			// Read number of derived items
			INT32 numDerivedItems;
			stream->Read(&numDerivedItems, sizeof(INT32));

			// Iterate through each derived item
			for(INT32 k = 0; k < numDerivedItems; ++k)
			{
				Item *derivedItem = ruNEW Item();

				// Read length of the derived item name including null
				INT32 derivedItemNameLength;
				stream->Read(&derivedItemNameLength, sizeof(INT32));

				// Read the item name
				char derivedItemName[4096];
				stream->Read(derivedItemName, derivedItemNameLength);

				// Read length of the item prefix string including null
				INT32 derivedItemPrefixLength;
				stream->Read(&derivedItemPrefixLength, sizeof(INT32));

				// Read the item prefix string
				char derivedItemPrefix[4096];
				stream->Read(derivedItemPrefix, derivedItemPrefixLength);
				derivedItem->m_itemPrefix = derivedItemPrefix;

				// Read number of valid color codes
				INT32 numValidColorCodes;
				stream->Read(&numValidColorCodes, sizeof(INT32));

				// Iterate through each color code
				ColorCode colorCode;
				for(INT32 l = 0; l < numValidColorCodes; ++l)
				{
					// Read color codes
					stream->Read(&colorCode.m_color0Code, sizeof(INT32));
					stream->Read(&colorCode.m_color1Code, sizeof(INT32));

					// Add color code
					derivedItem->m_validColorCodes.Add(colorCode);
				}

				item->m_items.Add(derivedItemName, derivedItem);
			}

			// Add item to the tree
			CRuString key(itemName);
			part->m_baseItems.Add(key, item);
		}

		// Balance the items contained by the part
		part->m_baseItems.BalanceTree();

		// Add part to the array
		m_parts.Add(part);
	}

	return TRUE;
}

BOOL CRuPaperdollTemplate::SerializeTo(IRuStream *stream)
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return FALSE;

	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write length of base item resource path, including NULL
	INT32 baseItemResourcePathLength = m_baseItemResourcePath.GetLength() + 1;
	stream->Write(&baseItemResourcePathLength, sizeof(INT32));

	// Write base item resource path
	stream->Write((char *) m_baseItemResourcePath, baseItemResourcePathLength);

	// Write length of item resource path, including NULL
	INT32 itemResourcePathLength = m_itemResourcePath.GetLength() + 1;
	stream->Write(&itemResourcePathLength, sizeof(INT32));

	// Write item resource path
	stream->Write((char *) m_itemResourcePath, itemResourcePathLength);

	// Serialize a copy of the model template
	m_modelTemplate->SerializeTo(stream);

	// Write number of parts
	INT32 numParts = m_parts.Count();
	stream->Write(&numParts, sizeof(INT32));

	// Iterate through each part
	for(INT32 i = 0; i < numParts; ++i)
	{
		// Write part ID
		stream->Write(&m_parts[i]->m_partID, sizeof(INT32));

		// Write number of items for this part
		INT32 numItems = m_parts[i]->m_baseItems.Count();
		stream->Write(&numItems, sizeof(INT32));

		// Iterate through each item
		for(INT32 j = 0; j < numItems; ++j)
		{
			// Retrieve the item from the tree
			BaseItem *item = m_parts[i]->m_baseItems.GetAtIndex(j);

			// Write length of the item name including null
			const CRuString &itemName = m_parts[i]->m_baseItems.GetKeyAtIndex(j);
			INT32 itemNameLength = itemName.GetLength() + 1;
			stream->Write(&itemNameLength, sizeof(INT32));

			// Write the item name
			stream->Write((const char *) itemName, itemNameLength);

			// Write length of the item prefix string including null
			INT32 itemPrefixLength = item->m_baseItemPrefix.GetLength() + 1;
			stream->Write(&itemPrefixLength, sizeof(INT32));

			// Write the item prefix string
			stream->Write((const char *) item->m_baseItemPrefix, itemPrefixLength);

			// Write number of item codes for this item
			INT32 numItemCodes = item->m_validBaseItemCodes.Count();
			stream->Write(&numItemCodes, sizeof(INT32));

			// Iterate through each item code
			for(INT32 k = 0; k < numItemCodes; ++k)
			{
				// Write length of the item code string including NULL
				INT32 nameLength = strlen(item->m_validBaseItemCodes[k]) + 1;
				stream->Write(&nameLength, sizeof(INT32));

				// Write the item code string
				stream->Write((char *) item->m_validBaseItemCodes[k], nameLength);
			}

			// Write number of derived items
			INT32 numDerivedItems = item->m_items.Count();
			stream->Write(&numDerivedItems, sizeof(INT32));

			// Iterate through each derived item
			for(INT32 k = 0; k < numDerivedItems; ++k)
			{
				Item *derivedItem = item->m_items.GetAtIndex(k);

				// Write length of the derived item name including null
				const CRuString &derivedItemName = item->m_items.GetKeyAtIndex(k);
				INT32 derivedItemNameLength = derivedItemName.GetLength() + 1;
				stream->Write(&derivedItemNameLength, sizeof(INT32));

				// Write the derived item name
				stream->Write((const char *) derivedItemName, derivedItemNameLength);

				// Write length of the item prefix string including null
				INT32 derivedItemPrefixLength = derivedItem->m_itemPrefix.GetLength() + 1;
				stream->Write(&derivedItemPrefixLength, sizeof(INT32));

				// Write the item prefix string
				stream->Write((const char *) derivedItem->m_itemPrefix, derivedItemPrefixLength);

				// Write number of valid color codes
				INT32 numValidColorCodes = derivedItem->m_validColorCodes.Count();
				stream->Write(&numValidColorCodes, sizeof(INT32));

				// Iterate through each color code
				for(INT32 l = 0; l < numValidColorCodes; ++l)
				{
					// Write color codes
					stream->Write(&derivedItem->m_validColorCodes[l].m_color0Code, sizeof(INT32));
					stream->Write(&derivedItem->m_validColorCodes[l].m_color1Code, sizeof(INT32));
				}
			}
		}
	}

	return TRUE;
}

BOOL CRuPaperdollTemplate::Initialize(CRuCompositeModelTemplate *modelTemplate)
{
	// Release here...

	// Store model template and increment its reference
	m_modelTemplate = modelTemplate;
	m_modelTemplate->AddRef();

	// Setup part arrays
	INT32 numParts = m_modelTemplate->GetNumParts();
	for(INT32 i = 0; i < numParts; ++i)
	{
		// Add new part structure and setup its part ID
		m_parts.Add(ruNEW Part());
		m_parts[i]->m_partID = m_modelTemplate->GetPartID(i);
	}

	return TRUE;
}

const char *CRuPaperdollTemplate::GetBaseItemResourcePath() const
{
	return m_baseItemResourcePath;
}

void CRuPaperdollTemplate::SetBaseItemResourcePath(const char *baseItemResourcePath)
{
	m_baseItemResourcePath = baseItemResourcePath;
}

const char *CRuPaperdollTemplate::GetItemResourcePath() const
{
	return m_itemResourcePath;
}

void CRuPaperdollTemplate::SetItemResourcePath(const char *itemResourcePath)
{
	m_itemResourcePath = itemResourcePath;
}

BOOL CRuPaperdollTemplate::AddBaseItem(INT32 partID, const char *baseItemName, const char *baseItemCode, const char *baseItemPrefix)
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return FALSE;

	// Locate matching part index for the specified part ID
	INT32 partIndex = m_modelTemplate->GetPartIndex(partID);
	if(partIndex < 0)
		return FALSE;

	// Locate the item structure for the specified base item
	CRuString ruBaseItemName(baseItemName);
	BaseItem *baseItem = NULL;

	if(m_parts[partIndex]->m_baseItems.ContainsKey(ruBaseItemName))
	{
		// Retrieve the base item from the tree
		baseItem = m_parts[partIndex]->m_baseItems.Get(ruBaseItemName);
	}
	else
	{
		// The base item has not been created, so create a new item structure and add it to the tree
		baseItem = ruNEW BaseItem();
		m_parts[partIndex]->m_baseItems.Add(ruBaseItemName, baseItem);
	}

	// Set the base item prefix
	baseItem->m_baseItemPrefix = baseItemPrefix;

	if(baseItem)
	{
		// Make sure the base item code is not already in the template
		for(INT32 i = 0; i < baseItem->m_validBaseItemCodes.Count(); ++i)
		{
			if(strcmpi(baseItem->m_validBaseItemCodes[i], baseItemCode) == 0)
				return TRUE;
		}

		// Make a copy of the base item code
		INT32 baseItemCodeLength = strlen(baseItemCode) + 1;
		char *newBaseItemCode = ruNEW char [baseItemCodeLength];
		memcpy(newBaseItemCode, baseItemCode, baseItemCodeLength);

		// Add new item code to the list of valid item codes
		baseItem->m_validBaseItemCodes.Add(newBaseItemCode);

		return TRUE;
	}

	return FALSE;
}

BOOL CRuPaperdollTemplate::AddItem(const char *partName, const char *baseItemName, const char *itemName, const char *itemPrefix, INT32 color0Code, INT32 color1Code)
{
	// Find the part index
	INT32 partIndex = GetPartIndex(partName);

	if(partIndex >= 0)
	{
		// Locate the item structure for the specified base item
		CRuString ruBaseItemName(baseItemName);
		CRuString ruItemName(itemName);
		BaseItem *baseItem = NULL;
		Item *item = NULL;

		if(m_parts[partIndex]->m_baseItems.ContainsKey(ruBaseItemName))
		{
			// Retrieve the base item from the tree
			baseItem = m_parts[partIndex]->m_baseItems.Get(ruBaseItemName);

			if(baseItem->m_items.ContainsKey(ruItemName))
			{
				// Retrieve the derived item from the tree
				item = baseItem->m_items.Get(ruItemName);
			}
			else
			{
				// The item does not exist, so create a new entry for it
				item = ruNEW Item();
				baseItem->m_items.Add(ruItemName, item);
			}

			if(item)
			{
				// Set item prefix (the prefix is the resource name up to but not including the backslash and filename)
				item->m_itemPrefix = itemPrefix;

				// Make sure the specified color code combination does not exist
				for(INT32 i = 0; i < item->m_validColorCodes.Count(); ++i)
				{
					if(item->m_validColorCodes[i].m_color0Code == color0Code && item->m_validColorCodes[i].m_color1Code == color1Code)
					{
						return TRUE;
					}
				}

				// Add the specified color code combination
				ColorCode colorCode;
				colorCode.m_color0Code = color0Code;
				colorCode.m_color1Code = color1Code;
				item->m_validColorCodes.Add(colorCode);

				return TRUE;
			}
		}
	}

	return FALSE;
}

INT32 CRuPaperdollTemplate::GetNumParts() const
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return 0;

	return m_parts.Count();
}

INT32 CRuPaperdollTemplate::GetPartIndex(INT32 partID) const
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return -1;

	return m_modelTemplate->GetPartIndex(partID);
}

INT32 CRuPaperdollTemplate::GetPartIndex(const char *partName) const
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return -1;

	for(INT32 i = 0; i < m_modelTemplate->GetNumParts(); ++i)
	{
		if(stricmp(m_modelTemplate->GetPartName(i), partName) == 0)
			return i;
	}

	return -1;
}

INT32 CRuPaperdollTemplate::GetPartID(INT32 partIndex) const
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return -1;

	return m_modelTemplate->GetPartID(partIndex);
}

INT32 CRuPaperdollTemplate::GetPartID(const char *partName) const
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return -1;

	for(INT32 i = 0; i < m_modelTemplate->GetNumParts(); ++i)
	{
		if(stricmp(m_modelTemplate->GetPartName(i), partName) == 0)
			return m_modelTemplate->GetPartID(i);
	}

	return -1;
}

const char *CRuPaperdollTemplate::GetPartName(INT32 partIndex) const
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return "";

	return m_modelTemplate->GetPartName(partIndex);
}

INT32 CRuPaperdollTemplate::GetNumBaseItems(INT32 partIndex) const
{
	// Make sure we have an assigned model template
	if(!m_modelTemplate)
		return 0;

	// Return the number of items in the tree
	return m_parts[partIndex]->m_baseItems.Count();
}

const char *CRuPaperdollTemplate::GetBaseItemName(INT32 partIndex, INT32 baseItemIndex) const
{
	return m_parts[partIndex]->m_baseItems.GetKeyAtIndex(baseItemIndex);
}

const char *CRuPaperdollTemplate::GetBaseItemPrefix(INT32 partIndex, INT32 baseItemIndex) const
{
	BaseItem *item = m_parts[partIndex]->m_baseItems.GetAtIndex(baseItemIndex);

	return item->m_baseItemPrefix;
}

char *CRuPaperdollTemplate::GetBaseItemCodesAsString(INT32 partIndex, INT32 baseItemIndex) const
{
	BaseItem *item = m_parts[partIndex]->m_baseItems.GetAtIndex(baseItemIndex);

	CRuString ruItemCodeString;

	// Iterate through all acceptable item codes
	for(INT32 i = 0; i < item->m_validBaseItemCodes.Count(); ++i)
	{
		ruItemCodeString.Append(item->m_validBaseItemCodes[i]);
		ruItemCodeString.Append(" ");
	}

	char *itemCodeString = ruNEW char [ruItemCodeString.GetLength() + 1];
	memcpy(itemCodeString, (char *) ruItemCodeString, ruItemCodeString.GetLength() + 1);

	return itemCodeString;
}

INT32 CRuPaperdollTemplate::GetNumItems(INT32 partIndex, INT32 baseItemIndex) const
{
	// Return the number of items in the base item
	return m_parts[partIndex]->m_baseItems.GetAtIndex(baseItemIndex)->m_items.Count();
}

const char *CRuPaperdollTemplate::GetItemName(INT32 partIndex, INT32 baseItemIndex, INT32 itemIndex) const
{
	return m_parts[partIndex]->m_baseItems.GetAtIndex(baseItemIndex)->m_items.GetKeyAtIndex(itemIndex);
}

char *CRuPaperdollTemplate::GetItemColorCodesAsString(INT32 partIndex, INT32 baseItemIndex, INT32 itemIndex) const
{
	BaseItem *baseItem = m_parts[partIndex]->m_baseItems.GetAtIndex(baseItemIndex);
	Item* item = baseItem->m_items.GetAtIndex(itemIndex);

	CRuString ruColorCodeString;

	// Iterate through all acceptable item codes
	char tempStr[128];
	for(INT32 i = 0; i < item->m_validColorCodes.Count(); ++i)
	{
		sprintf(tempStr, "s%dh%d", item->m_validColorCodes[i].m_color0Code, item->m_validColorCodes[i].m_color1Code);
		ruColorCodeString.Append(tempStr);
		ruColorCodeString.Append(" ");
	}

	char *colorCodeString = ruNEW char [ruColorCodeString.GetLength() + 1];
	memcpy(colorCodeString, (char *) ruColorCodeString, ruColorCodeString.GetLength() + 1);

	return colorCodeString;
}

CRuPaperdoll *CRuPaperdollTemplate::Construct()
{
	CRuPaperdoll *paperdoll = ruNEW CRuPaperdoll();

	// Set template into paperdoll object and increment reference on self
	paperdoll->m_paperdollTemplate = this;
	paperdoll->SetResourceName(this->GetResourceName());
	this->AddRef();

	// Create a new composite model object
	paperdoll->m_paperdollModel = m_modelTemplate->Construct();
	paperdoll->AddChild(paperdoll->m_paperdollModel);

	return paperdoll;
}

CRuEntity *CRuPaperdollTemplate::FindSuitableItem(INT32 partIndex, const char *itemName, const CRuArrayList<const char *> &acceptableItemCodeList, INT32 color0Code, INT32 color1Code, const char **itemCodeOut) const
{
	return FindSuitableItem(partIndex, itemName, acceptableItemCodeList, NULL, color0Code, color1Code, NULL, itemCodeOut);
}

CRuEntity *CRuPaperdollTemplate::FindSuitableItem(INT32 partIndex, const char *itemName, const CRuArrayList<const char *> &acceptableItemCodeList, const char *baseItemKeyOverride, INT32 color0Code, INT32 color1Code, const char **baseItemKeyOut, const char **itemCodeOut) const
{
	const char *partName = GetPartName(partIndex);
	CRuString itemKey = itemName;

	// Iterate through all base items for the specified part index
	for(INT32 i = 0; i < m_parts[partIndex]->m_baseItems.Count(); ++i)
	{
		BaseItem *baseItem = m_parts[partIndex]->m_baseItems.GetAtIndex(i);
		const CRuString &baseItemKey = m_parts[partIndex]->m_baseItems.GetKeyAtIndex(i);

		// Attempt to locate the specified item name in the base item database
		if(baseItem->m_items.ContainsKey(itemKey))
		{
			Item *item = baseItem->m_items.Get(itemKey);

			// Iterate through all acceptable item codes
			for(INT32 j = 0; j < acceptableItemCodeList.Count(); ++j)
			{
				// Determine whether the acceptable item code is a valid item code
				for(INT32 k = 0; k < baseItem->m_validBaseItemCodes.Count(); ++k)
				{
					if(strcmpi(acceptableItemCodeList[j], baseItem->m_validBaseItemCodes[k]) == 0)
					{
						// Build model resource name
						INT32 requiredStringLength =	m_resourceNamePrefix.GetLength() +
														m_resourceNamePostfix.GetLength() +
														baseItem->m_baseItemPrefix.GetLength() + 
														strlen(partName) +
														baseItemKey.GetLength() +
														strlen(baseItem->m_validBaseItemCodes[k]) + 3 + 1;

						char *resourceName = ruNEW char [requiredStringLength];
						resourceName[0] = NULL;
						strcat(resourceName, m_resourceNamePrefix);
						strcat(resourceName, baseItem->m_baseItemPrefix);
						strcat(resourceName, "_");
						strcat(resourceName, partName);
						strcat(resourceName, "_");
						if(baseItemKeyOverride)
							strcat(resourceName, baseItemKeyOverride);
						else
							strcat(resourceName, baseItemKey);
						strcat(resourceName, "_");
						strcat(resourceName, baseItem->m_validBaseItemCodes[k]);
						strcat(resourceName, m_resourceNamePostfix);

						if(baseItemKeyOut)
						{
							*baseItemKeyOut = baseItemKey;
						}

						*itemCodeOut = baseItem->m_validBaseItemCodes[k];

						// Retrieve the resource
						CRuEntity *entity = g_ruResourceManager->LoadEntity(resourceName);
						delete[] resourceName;

						if(entity)
						{
							if(entity->GetType().IsSubClassOf(IRuEntity_Renderable::Type))
							{
								// Build diffuse texture resource name
								requiredStringLength =	item->m_itemPrefix.GetLength() + 
														strlen(partName) +
														baseItemKey.GetLength() +
														itemKey.GetLength() + 3 + 1;


								char *texResourceName = ruNEW char [requiredStringLength];
								texResourceName[0] = NULL;
								strcat(texResourceName, item->m_itemPrefix);
								strcat(texResourceName, "_");
								strcat(texResourceName, partName);
								strcat(texResourceName, "_");
								strcat(texResourceName, baseItemKey);
								strcat(texResourceName, "_");
								strcat(texResourceName, itemKey);

								// Retrieve the diffuse texture
								IRuTexture *texture = g_ruResourceManager->LoadTexture(texResourceName);
								delete[] texResourceName;

								if(texture)
								{
									IRuMesh *mesh;
									IRuMaterial *material;

									if(static_cast<IRuEntity_Renderable *>(entity)->GetMesh(0, &mesh, &material))
									{
										material->SetTexture(ruTEXCHANNEL_DIFFUSEMAP, texture);
										texture->Release();
									}
								}

								for(INT32 layer = 1; layer <= 4; ++layer)
								{
									// Build layer masking texture resource name
									requiredStringLength =	item->m_itemPrefix.GetLength() + 
															strlen(partName) +
															baseItemKey.GetLength() +
															itemKey.GetLength() + 3 + 1 + 7;

									char *layerTexResourceName = ruNEW char [requiredStringLength];
									layerTexResourceName[0] = NULL;
									strcat(layerTexResourceName, item->m_itemPrefix);
									strcat(layerTexResourceName, "_");
									strcat(layerTexResourceName, partName);
									strcat(layerTexResourceName, "_");
									strcat(layerTexResourceName, baseItemKey);
									strcat(layerTexResourceName, "_");
									strcat(layerTexResourceName, itemKey);
									strcat(layerTexResourceName, "_layer");
									layerTexResourceName[requiredStringLength - 2] = '1' + layer - 1;
									layerTexResourceName[requiredStringLength - 1] = 0;

									// Retrieve the layer masking texture
									IRuTexture *layerMaskTexture = g_ruResourceManager->LoadTexture(layerTexResourceName, ruSURFACEFMT_A8R8G8B8);
									delete[] layerTexResourceName;

									IRuMesh *mesh;
									IRuMaterial *material;

									if(static_cast<IRuEntity_Renderable *>(entity)->GetMesh(0, &mesh, &material))
									{
										if(layerMaskTexture)
										{
											material->SetTexture(static_cast<RuTextureChannel>(ruTEXCHANNEL_PAPERDOLL_MASK_0 + layer - 1), layerMaskTexture);
											ruSAFE_RELEASE(layerMaskTexture);
										}
										else
										{
											material->SetTexture(static_cast<RuTextureChannel>(ruTEXCHANNEL_PAPERDOLL_MASK_0 + layer - 1), NULL);
										}
									}
								}

								// Build gloss texture resource name
								requiredStringLength =	item->m_itemPrefix.GetLength() + 
														strlen(partName) +
														baseItemKey.GetLength() +
														itemKey.GetLength() + 3 + 1 + 6;


								char *glossTexResourceName = ruNEW char [requiredStringLength];
								glossTexResourceName[0] = NULL;
								strcat(glossTexResourceName, item->m_itemPrefix);
								strcat(glossTexResourceName, "_");
								strcat(glossTexResourceName, partName);
								strcat(glossTexResourceName, "_");
								strcat(glossTexResourceName, baseItemKey);
								strcat(glossTexResourceName, "_");
								strcat(glossTexResourceName, itemKey);
								strcat(glossTexResourceName, "_gloss");

								// Retrieve the gloss texture
								IRuTexture *glossTexture = g_ruResourceManager->LoadTexture(glossTexResourceName);
								delete[] glossTexResourceName;

								IRuMesh *mesh;
								IRuMaterial *material;

								if(static_cast<IRuEntity_Renderable *>(entity)->GetMesh(0, &mesh, &material))
								{
									if(glossTexture)
									{
										material->SetTexture(ruTEXCHANNEL_GLOSSMAP, glossTexture);
										ruSAFE_RELEASE(glossTexture);
									}
									else
									{
										material->SetTexture(ruTEXCHANNEL_GLOSSMAP, NULL);
									}
								}

								return entity;
							}

							// Release entity and return NULL
							entity->Release();
						}

/*
						// Iterate through all color codes
						for(INT32 l = 0; l < item->m_validColorCodes.Count(); ++l)
						{
							if(	(item->m_validColorCodes[l].m_color0Code == 0 || color0Code == 0 || item->m_validColorCodes[l].m_color0Code == color0Code) &&
								(item->m_validColorCodes[l].m_color1Code == 0 || color1Code == 0 || item->m_validColorCodes[l].m_color1Code == color1Code))
							{
								// Build model resource name
								INT32 requiredStringLength =	m_resourceNamePrefix.GetLength() +
																m_resourceNamePostfix.GetLength() +
																baseItem->m_baseItemPrefix.GetLength() + 
																strlen(partName) +
																baseItemKey.GetLength() +
																strlen(baseItem->m_validBaseItemCodes[k]) + 3 + 1;

								char *resourceName = ruNEW char [requiredStringLength];
								resourceName[0] = NULL;
								strcat(resourceName, m_resourceNamePrefix);
								strcat(resourceName, baseItem->m_baseItemPrefix);
								strcat(resourceName, "_");
								strcat(resourceName, partName);
								strcat(resourceName, "_");
								strcat(resourceName, baseItemKey);
								strcat(resourceName, "_");
								strcat(resourceName, baseItem->m_validBaseItemCodes[k]);
								strcat(resourceName, m_resourceNamePostfix);
								*itemCodeOut = baseItem->m_validBaseItemCodes[k];

								// Retrieve the resource
								CRuEntity *entity = g_ruResourceManager->LoadEntity(resourceName);
								delete[] resourceName;

								if(entity)
								{
									if(entity->GetType().IsSubClassOf(IRuEntity_Renderable::Type))
									{
										char colorCodeString[128] = "";

										if(item->m_validColorCodes[l].m_color0Code && item->m_validColorCodes[l].m_color1Code)
										{
											sprintf(colorCodeString, "s%dh%d", item->m_validColorCodes[l].m_color0Code, item->m_validColorCodes[l].m_color1Code);
										}
										else if(item->m_validColorCodes[l].m_color0Code)
										{
											sprintf(colorCodeString, "s%d", item->m_validColorCodes[l].m_color0Code);
										}
										else if(item->m_validColorCodes[l].m_color1Code)
										{
											sprintf(colorCodeString, "h%d", item->m_validColorCodes[l].m_color1Code);
										}

										// Build diffuse texture resource name
										requiredStringLength =	item->m_itemPrefix.GetLength() + 
																strlen(partName) +
																baseItemKey.GetLength() +
																itemKey.GetLength() + 3 + 1;


										if(strlen(colorCodeString) > 0)
										{
											requiredStringLength += strlen(colorCodeString) + 1;
										}

										char *texResourceName = ruNEW char [requiredStringLength];
										texResourceName[0] = NULL;
										strcat(texResourceName, item->m_itemPrefix);
										strcat(texResourceName, "_");
										strcat(texResourceName, partName);
										strcat(texResourceName, "_");
										strcat(texResourceName, baseItemKey);
										strcat(texResourceName, "_");
										strcat(texResourceName, itemKey);
										if(strlen(colorCodeString) > 0)
										{
											strcat(texResourceName, "_");
											strcat(texResourceName, colorCodeString);
										}

										// Retrieve the diffuse texture
										IRuTexture *texture = g_ruResourceManager->LoadTexture(texResourceName);
										delete[] texResourceName;

										if(texture)
										{
											IRuMesh *mesh;
											IRuMaterial *material;

											if(static_cast<IRuEntity_Renderable *>(entity)->GetMesh(0, &mesh, &material))
											{
												material->SetTexture(ruTEXCHANNEL_DIFFUSEMAP, texture);
												texture->Release();
											}
										}

										for(INT32 layer = 1; layer <= 4; ++layer)
										{
											// Build layer masking texture resource name
											requiredStringLength =	item->m_itemPrefix.GetLength() + 
																	strlen(partName) +
																	baseItemKey.GetLength() +
																	itemKey.GetLength() + 3 + 1 + 7;

											char *layerTexResourceName = ruNEW char [requiredStringLength];
											layerTexResourceName[0] = NULL;
											strcat(layerTexResourceName, item->m_itemPrefix);
											strcat(layerTexResourceName, "_");
											strcat(layerTexResourceName, partName);
											strcat(layerTexResourceName, "_");
											strcat(layerTexResourceName, baseItemKey);
											strcat(layerTexResourceName, "_");
											strcat(layerTexResourceName, itemKey);
											strcat(layerTexResourceName, "_layer");
											layerTexResourceName[requiredStringLength - 2] = '1' + layer - 1;
											layerTexResourceName[requiredStringLength - 1] = 0;

											// Retrieve the layer masking texture
											IRuTexture *layerMaskTexture = g_ruResourceManager->LoadTexture(layerTexResourceName, ruSURFACEFMT_A8R8G8B8);
											delete[] layerTexResourceName;

											IRuMesh *mesh;
											IRuMaterial *material;

											if(static_cast<IRuEntity_Renderable *>(entity)->GetMesh(0, &mesh, &material))
											{
												if(layerMaskTexture)
												{
													material->SetTexture(static_cast<RuTextureChannel>(ruTEXCHANNEL_PAPERDOLL_MASK_0 + layer - 1), layerMaskTexture);
													ruSAFE_RELEASE(layerMaskTexture);
												}
												else
												{
													material->SetTexture(static_cast<RuTextureChannel>(ruTEXCHANNEL_PAPERDOLL_MASK_0 + layer - 1), NULL);
												}
											}
										}

										// Build gloss texture resource name
										requiredStringLength =	item->m_itemPrefix.GetLength() + 
																strlen(partName) +
																baseItemKey.GetLength() +
																itemKey.GetLength() + 3 + 1 + 6;


										char *glossTexResourceName = ruNEW char [requiredStringLength];
										glossTexResourceName[0] = NULL;
										strcat(glossTexResourceName, item->m_itemPrefix);
										strcat(glossTexResourceName, "_");
										strcat(glossTexResourceName, partName);
										strcat(glossTexResourceName, "_");
										strcat(glossTexResourceName, baseItemKey);
										strcat(glossTexResourceName, "_");
										strcat(glossTexResourceName, itemKey);
										strcat(glossTexResourceName, "_gloss");

										// Retrieve the gloss texture
										IRuTexture *glossTexture = g_ruResourceManager->LoadTexture(glossTexResourceName);
										delete[] glossTexResourceName;

										IRuMesh *mesh;
										IRuMaterial *material;

										if(static_cast<IRuEntity_Renderable *>(entity)->GetMesh(0, &mesh, &material))
										{
											if(glossTexture)
											{
												material->SetTexture(ruTEXCHANNEL_GLOSSMAP, glossTexture);
												ruSAFE_RELEASE(glossTexture);
											}
											else
											{
												material->SetTexture(ruTEXCHANNEL_GLOSSMAP, NULL);
											}
										}

										return entity;
									}

									// Release entity and return NULL
									entity->Release();
								}
							}
						}

*/
					}
				}
			}



		}
	}

	*itemCodeOut = "";

	return NULL;
}

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

CRuPaperdoll::CRuPaperdoll()
:	m_paperdollTemplate(NULL),
	m_paperdollModel(NULL),
	m_color0Code(2),
	m_color1Code(2)
{
}

CRuPaperdoll::~CRuPaperdoll()
{
	// Release reference to paperdoll template
	ruSAFE_RELEASE(m_paperdollTemplate);

	// Detach model and release reference to it
	ruSAFE_DETACH(m_paperdollModel);
	ruSAFE_RELEASE(m_paperdollModel);

	// Release component entities
	for(INT32 i = 0; i < m_componentEntities.Count(); ++i)
	{
		ruSAFE_RELEASE(m_componentEntities[i]);
	}

	// Release component descriptors
	for(INT32 i = 0; i < m_componentDescriptors.Count(); ++i)
	{
		delete m_componentDescriptors[i];
	}
}

CRuPaperdollTemplate *CRuPaperdoll::GetPaperdollTemplate()
{
	return m_paperdollTemplate;
}

void CRuPaperdoll::SetColorCodes(INT32 color0Code, INT32 color1Code)
{
	m_color0Code = color0Code;
	m_color1Code = color1Code;
}

BOOL CRuPaperdoll::SetComponent(const char *partName, const char *itemName)
{
	// Ensure the paperdoll is properly initialized
	if(!m_paperdollTemplate)
		return FALSE;

	// Find the part index for the specified part
	INT32 partIndex = m_paperdollTemplate->GetPartIndex(partName);

	return SetComponent(partIndex, itemName);
}

BOOL CRuPaperdoll::SetComponent(INT32 partIndex, const char *itemName)
{
	// Ensure the paperdoll is properly initialized
	if(!m_paperdollTemplate)
		return FALSE;

	CRuString moo;
	while(m_components.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_components.Add(moo);
	}

	while(m_componentEntities.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_componentEntities.Add(NULL);
	}

	while(m_componentItemCodes.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_componentItemCodes.Add("x");
	}

	while(m_componentDescriptors.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_componentDescriptors.Add(ruNEW ComponentDesc());
	}

	if(partIndex >= 0 && partIndex < m_components.Count())
	{
		// Store the specified item name
		m_components[partIndex] = itemName;

		return TRUE;
	}

	return FALSE;
}

BOOL CRuPaperdoll::SetComponentColors(const char *partName, const BOOL *layerActivated, const RUCOLOR *layerColor)
{
	// Ensure the paperdoll is properly initialized
	if(!m_paperdollTemplate)
		return FALSE;

	// Find the part index for the specified part
	INT32 partIndex = m_paperdollTemplate->GetPartIndex(partName);

	return SetComponentColors(partIndex, layerActivated, layerColor);
}

BOOL CRuPaperdoll::SetComponentColors(INT32 partIndex, const BOOL *layerActivated, const RUCOLOR *layerColor)
{
	// Ensure the paperdoll is properly initialized
	if(!m_paperdollTemplate)
		return FALSE;

	CRuString moo;
	while(m_components.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_components.Add(moo);
	}

	while(m_componentEntities.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_componentEntities.Add(NULL);
	}

	while(m_componentItemCodes.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_componentItemCodes.Add("x");
	}

	while(m_componentDescriptors.Count() < m_paperdollTemplate->GetNumParts())
	{
		m_componentDescriptors.Add(ruNEW ComponentDesc());
	}

	if(partIndex >= 0 && partIndex < m_components.Count())
	{
		for(INT32 i = 0; i < 4; ++i)
		{
			m_componentDescriptors[partIndex]->m_layerActivated[i] = layerActivated[i];
			m_componentDescriptors[partIndex]->m_layerColor[i] = layerColor[i];
		}

		return TRUE;
	}

	return FALSE;
}

BOOL CRuPaperdoll::BuildPaperdoll()
{
	// Ensure the paperdoll is properly initialized
	if(!m_paperdollTemplate)
		return FALSE;

	// *** NOTE: the following code segment should be what is "script-enabled" in the future

	// NOTE: Clear m_componentResourceNames pointers here
	for(INT32 i = 0; i < m_componentEntities.Count(); ++i)
	{
		ruSAFE_RELEASE(m_componentEntities[i]);
		m_componentEntities[i] = NULL;
	}

	CRuArrayList<const char *> acceptableItemCodeList;

	INT32 handIndex = m_paperdollTemplate->GetPartIndex("hand");
	INT32 torsoIndex = m_paperdollTemplate->GetPartIndex("torso");
	INT32 legIndex = m_paperdollTemplate->GetPartIndex("leg");
	INT32 footIndex = m_paperdollTemplate->GetPartIndex("foot");
	INT32 beltIndex = m_paperdollTemplate->GetPartIndex("belt");
	INT32 headIndex = m_paperdollTemplate->GetPartIndex("head");
	INT32 shoulderIndex = m_paperdollTemplate->GetPartIndex("shoulder");
	INT32 backIndex = m_paperdollTemplate->GetPartIndex("back");
	INT32 helmetIndex = m_paperdollTemplate->GetPartIndex("helmet");
	INT32 hairIndex = m_paperdollTemplate->GetPartIndex("hair");

	// Search for torso models that have precedence over the hand
	acceptableItemCodeList.Add("b");
	acceptableItemCodeList.Add("d");
	acceptableItemCodeList.Add("f");

	// Get torso resource name
	const char *torsoBaseItemKey = NULL;
	m_componentEntities[torsoIndex] = m_paperdollTemplate->FindSuitableItem(torsoIndex, m_components[torsoIndex], acceptableItemCodeList, NULL, m_color0Code, m_color1Code, &torsoBaseItemKey, &m_componentItemCodes[torsoIndex]);

	if(m_componentEntities[torsoIndex])
	{
		// Prepare acceptable item codes for hand
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("b");			// Hand model cut to the wrist

		// Get hand resource name
		m_componentEntities[handIndex] = m_paperdollTemplate->FindSuitableItem(handIndex, m_components[handIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[handIndex]);
	}
	else
	{
		// There is no torso model that has precedence over the hand, so search for the hand first
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("a");			// Hand model cut to the forearm
		acceptableItemCodeList.Add("b");			// Hand model cut to the wrist

		// Get hand resource
		m_componentEntities[handIndex] = m_paperdollTemplate->FindSuitableItem(handIndex, m_components[handIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[handIndex]);

		if(m_componentEntities[handIndex])
		{
			// The hand model used is cut to the forearm
			if(stricmp(m_componentItemCodes[handIndex], "a") == 0)
			{
				acceptableItemCodeList.Clear();
				acceptableItemCodeList.Add("a0");
				acceptableItemCodeList.Add("c0");
				acceptableItemCodeList.Add("e0");

				// Get torso resource
				m_componentEntities[torsoIndex] = m_paperdollTemplate->FindSuitableItem(torsoIndex, m_components[torsoIndex], acceptableItemCodeList, NULL, m_color0Code, m_color1Code, &torsoBaseItemKey, &m_componentItemCodes[torsoIndex]);
			}
			// The hand model used is cut to the wrist
			else
			{
				acceptableItemCodeList.Clear();
				acceptableItemCodeList.Add("a1");
				acceptableItemCodeList.Add("c1");
				acceptableItemCodeList.Add("e1");

				// Get torso resource
				m_componentEntities[torsoIndex] = m_paperdollTemplate->FindSuitableItem(torsoIndex, m_components[torsoIndex], acceptableItemCodeList, NULL, m_color0Code, m_color1Code, &torsoBaseItemKey, &m_componentItemCodes[torsoIndex]);
			}
		}
	}

	// Prepare acceptable item codes for leg
	if(stricmp(m_componentItemCodes[torsoIndex], "a0") == 0 || stricmp(m_componentItemCodes[torsoIndex], "a1") == 0 || stricmp(m_componentItemCodes[torsoIndex], "b") == 0)
	{
		// Leg exists, look for leg covering foot first
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("b");

		// Get leg resource name
		m_componentEntities[legIndex] = m_paperdollTemplate->FindSuitableItem(legIndex, m_components[legIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[legIndex]);

		if(m_componentEntities[legIndex])
		{
			// Leg covers foot, look for foot
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("c");

			// Get foot resource name
			m_componentEntities[footIndex] = m_paperdollTemplate->FindSuitableItem(footIndex, m_components[footIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[footIndex]);
		}
		else
		{
			// Leg is covered by foot, look for available foot first
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("a");
			acceptableItemCodeList.Add("b");
			acceptableItemCodeList.Add("c");

			// Get foot resource name
			m_componentEntities[footIndex] = m_paperdollTemplate->FindSuitableItem(footIndex, m_components[footIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[footIndex]);

			// Look for compatible leg
			acceptableItemCodeList.Clear();

			if(stricmp(m_componentItemCodes[footIndex], "a") == 0)
			{
				// Foot is thigh length
				acceptableItemCodeList.Add("a0");
			}
			else if(stricmp(m_componentItemCodes[footIndex], "b") == 0)
			{
				// Foot is calf length
				acceptableItemCodeList.Add("a1");
			}
			else
			{
				// Foot is ankle length
				acceptableItemCodeList.Add("a2");
			}

			// Get leg resource name
			m_componentEntities[legIndex] = m_paperdollTemplate->FindSuitableItem(legIndex, m_components[legIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[legIndex]);
		}

	}
	else if(stricmp(m_componentItemCodes[torsoIndex], "c0") == 0 || stricmp(m_componentItemCodes[torsoIndex], "c1") == 0 || stricmp(m_componentItemCodes[torsoIndex], "d") == 0)
	{
		// Leg does not exist, robe is knee-length, look for matching lower leg part
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("c");
		m_componentEntities[legIndex] = m_paperdollTemplate->FindSuitableItem(legIndex, m_components[torsoIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[legIndex]);

		// Find compatible foot
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("b");
		acceptableItemCodeList.Add("a");	// <-- This is questionable, should this be allowed? Might have penetration problems.

		// Get foot resource name
		m_componentEntities[footIndex] = m_paperdollTemplate->FindSuitableItem(footIndex, m_components[footIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[footIndex]);
	}
	else if(stricmp(m_componentItemCodes[torsoIndex], "e0") == 0 || stricmp(m_componentItemCodes[torsoIndex], "e1") == 0 || stricmp(m_componentItemCodes[torsoIndex], "f") == 0)
	{
		// Leg does not exist, robe is ankle-length, look for matching lower leg part
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("c");
		m_componentEntities[legIndex] = m_paperdollTemplate->FindSuitableItem(legIndex, m_components[torsoIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[legIndex]);

		// Find compatible foot
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("c");
		acceptableItemCodeList.Add("b");	// <-- This is questionable, should this be allowed? Might have penetration problems.
		acceptableItemCodeList.Add("a");	// <-- This is questionable, should this be allowed? Might have penetration problems.

		// Get foot resource name
		m_componentEntities[footIndex] = m_paperdollTemplate->FindSuitableItem(footIndex, m_components[footIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[footIndex]);
	}

	// Belt uses same item name as torso
	if(m_components[beltIndex] && m_components[beltIndex].GetLength() > 0)
	{
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("x");

		m_componentEntities[beltIndex] = m_paperdollTemplate->FindSuitableItem(beltIndex, m_components[beltIndex], acceptableItemCodeList, torsoBaseItemKey, m_color0Code, m_color1Code, NULL, &m_componentItemCodes[beltIndex]);
	}
/*
	// Belt uses same item name as torso
	if(m_components[beltIndex] && m_components[beltIndex].GetLength() > 0)
	{
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("x");

		CRuEntity *textureEntity = m_paperdollTemplate->FindSuitableItem(beltIndex, m_components[beltIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[beltIndex]);
		m_componentEntities[beltIndex] = m_paperdollTemplate->FindSuitableItem(beltIndex, m_components[torsoIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[beltIndex]);

		if(textureEntity && m_componentEntities[beltIndex])
		{
			IRuMesh *destMesh, *srcMesh;
			IRuMaterial *destMaterial, *srcMaterial;

			if(	static_cast<IRuEntity_Renderable *>(m_componentEntities[beltIndex])->GetMesh(0, &destMesh, &destMaterial) &&
				static_cast<IRuEntity_Renderable *>(textureEntity)->GetMesh(0, &srcMesh, &srcMaterial))
			{
				destMaterial->SetTexture(ruTEXCHANNEL_DIFFUSEMAP, srcMaterial->GetTexture(ruTEXCHANNEL_DIFFUSEMAP));
			}
		}

		ruSAFE_RELEASE(textureEntity);
	}
*/
	// Shoulder
	acceptableItemCodeList.Clear();
	acceptableItemCodeList.Add("x");
	m_componentEntities[shoulderIndex] = m_paperdollTemplate->FindSuitableItem(shoulderIndex, m_components[shoulderIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[shoulderIndex]);

	// Back
	acceptableItemCodeList.Clear();
	acceptableItemCodeList.Add("a");
	acceptableItemCodeList.Add("b");
	m_componentEntities[backIndex] = m_paperdollTemplate->FindSuitableItem(backIndex, m_components[backIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[backIndex]);

	// Helmet exists, locate helmet
	acceptableItemCodeList.Clear();
	acceptableItemCodeList.Add("a");
	acceptableItemCodeList.Add("b");
	acceptableItemCodeList.Add("c");
	acceptableItemCodeList.Add("d");

	// Get helmet resource name
	m_componentEntities[helmetIndex] = m_paperdollTemplate->FindSuitableItem(helmetIndex, m_components[helmetIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[helmetIndex]);

	if(m_componentEntities[helmetIndex])
	{
		// Helmet exists
		if(stricmp(m_componentItemCodes[helmetIndex], "a") == 0)
		{
			// Helmet is a mask type
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("a");
			m_componentEntities[hairIndex] = m_paperdollTemplate->FindSuitableItem(hairIndex, m_components[hairIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[hairIndex]);

			// Head
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("x");
			m_componentEntities[headIndex] = m_paperdollTemplate->FindSuitableItem(headIndex, m_components[headIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[headIndex]);
		}
		else if(stricmp(m_componentItemCodes[helmetIndex], "b") == 0)
		{
			// Helmet is a partially covering cap
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("b");
			m_componentEntities[hairIndex] = m_paperdollTemplate->FindSuitableItem(hairIndex, m_components[hairIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[hairIndex]);

			// Head
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("x");
			m_componentEntities[headIndex] = m_paperdollTemplate->FindSuitableItem(headIndex, m_components[headIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[headIndex]);
		}
		else if(stricmp(m_componentItemCodes[helmetIndex], "c") == 0)
		{
			// Helmet is a partially covering cap
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("c");
			m_componentEntities[hairIndex] = m_paperdollTemplate->FindSuitableItem(hairIndex, m_components[hairIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[hairIndex]);

			// Head
			acceptableItemCodeList.Clear();
			acceptableItemCodeList.Add("x");
			m_componentEntities[headIndex] = m_paperdollTemplate->FindSuitableItem(headIndex, m_components[headIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[headIndex]);
		}
		else if(stricmp(m_componentItemCodes[helmetIndex], "d") == 0)
		{
			// Helmet is a fully covering helm
		}
	}
	else
	{
		// Full hair
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("a");
		m_componentEntities[hairIndex] = m_paperdollTemplate->FindSuitableItem(hairIndex, m_components[hairIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[hairIndex]);

		// Head
		acceptableItemCodeList.Clear();
		acceptableItemCodeList.Add("x");
		m_componentEntities[headIndex] = m_paperdollTemplate->FindSuitableItem(headIndex, m_components[headIndex], acceptableItemCodeList, m_color0Code, m_color1Code, &m_componentItemCodes[headIndex]);
	}

	// Assemble paperdoll
	for(INT32 i = 0; i < m_paperdollTemplate->GetNumParts(); ++i)
	{
		m_paperdollModel->SetPart(m_paperdollTemplate->GetPartID(i), NULL);
	}

	for(INT32 i = 0; i < m_componentEntities.Count(); ++i)
	{
		if(m_componentEntities[i])
		{
			// Retrieve the resource
			m_paperdollModel->SetPart(m_paperdollTemplate->GetPartID(i), static_cast<IRuEntity_Renderable *>(m_componentEntities[i]));
			m_paperdollModel->SetPartColors(m_paperdollTemplate->GetPartID(i), m_componentDescriptors[i]->m_layerActivated, m_componentDescriptors[i]->m_layerColor);
		}
	}

	// Build the composite model
	m_paperdollModel->BuildModel();

	// Enable skinning
	m_paperdollModel->SetOption(ruENTITYOPTION_SKINNED, 1);

	// Enable alpha testing
	GetParameterBlock()->SetParameter(ruPARAMTYPE_GEOMETRY_ALPHATESTENABLE, TRUE);
	GetParameterBlock()->SetParameter(ruPARAMTYPE_GEOMETRY_ALPHATESTVALUE, 170);

	return TRUE;
}

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

CRuPlayerPaperdoll::CRuPlayerPaperdoll()
{
}

CRuPlayerPaperdoll::~CRuPlayerPaperdoll()
{
}

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

#pragma managed
