#include "legopartpresenter.h"

#include "legovideomanager.h"
#include "misc.h"
#include "misc/legocontainer.h"
#include "misc/legostorage.h"
#include "misc/legotexture.h"
#include "mxdirectx/mxdirect3d.h"
#include "mxdsaction.h"
#include "mxdssubscriber.h"
#include "viewmanager/viewlodlist.h"

DECOMP_SIZE_ASSERT(LegoLODList, 0x18)
DECOMP_SIZE_ASSERT(LegoNamedPart, 0x14)
DECOMP_SIZE_ASSERT(LegoNamedPartList, 0x18)

// GLOBAL: LEGO1 0x100f7aa0
MxS32 g_partPresenterConfig1 = 1;

// GLOBAL: LEGO1 0x100f7aa4
MxS32 g_partPresenterConfig2 = 100;

// FUNCTION: LEGO1 0x1007c990
void LegoPartPresenter::configureLegoPartPresenter(MxS32 p_partPresenterConfig1, MxS32 p_partPresenterConfig2)
{
	g_partPresenterConfig1 = p_partPresenterConfig1;
	g_partPresenterConfig2 = p_partPresenterConfig2;
}

// FUNCTION: LEGO1 0x1007c9b0
MxResult LegoPartPresenter::AddToManager()
{
	VideoManager()->RegisterPresenter(*this);
	return SUCCESS;
}

// FUNCTION: LEGO1 0x1007c9d0
void LegoPartPresenter::Destroy(MxBool p_fromDestructor)
{
	ENTER(m_criticalSection);
	VideoManager()->UnregisterPresenter(*this);

	if (m_parts) {
		delete m_parts;
		m_parts = NULL;
	}
	m_parts = NULL;

	m_criticalSection.Leave();
	if (!p_fromDestructor) {
		MxMediaPresenter::Destroy(FALSE);
	}
}

// FUNCTION: LEGO1 0x1007ca30
MxResult LegoPartPresenter::Read(MxDSChunk& p_chunk)
{
	MxResult result = FAILURE;
	LegoU32 numROIs, numLODs;
	LegoMemory storage(p_chunk.GetData());
	LegoU32 textureInfoOffset, i, j, numTextures;
	LegoU32 roiNameLength, roiInfoOffset, surplusLODs;
	LegoLODList* lods;
	LegoNamedPart* namedPart;
	LegoChar* roiName = NULL;
	LegoChar* textureName = NULL;
	LegoTexture* texture = NULL;
	LegoTextureInfo* textureInfo = NULL;
	LegoS32 hardwareMode = VideoManager()->GetDirect3D()->AssignedDevice()->GetHardwareMode();

	if (storage.Read(&textureInfoOffset, sizeof(LegoU32)) != SUCCESS) {
		goto done;
	}
	if (storage.SetPosition(textureInfoOffset) != SUCCESS) {
		goto done;
	}
	if (storage.Read(&numTextures, sizeof(LegoU32)) != SUCCESS) {
		goto done;
	}

	for (i = 0; i < numTextures; i++) {
		LegoU32 textureNameLength;

		storage.Read(&textureNameLength, sizeof(LegoU32));
		textureName = new LegoChar[textureNameLength + 1];
		storage.Read(textureName, textureNameLength);
		textureName[textureNameLength] = '\0';

		strlwr(textureName);

		if (textureName[0] == '^') {
			strcpy(textureName, textureName + 1);

			if (g_partPresenterConfig1) {
				texture = new LegoTexture();
				if (texture->Read(&storage, hardwareMode) != SUCCESS) {
					goto done;
				}

				LegoTexture* discardTexture = new LegoTexture();
				if (discardTexture->Read(&storage, FALSE) != SUCCESS) {
					goto done;
				}
				delete discardTexture;
			}
			else {
				LegoTexture* discardTexture = new LegoTexture();
				if (discardTexture->Read(&storage, FALSE) != SUCCESS) {
					goto done;
				}
				delete discardTexture;

				texture = new LegoTexture();
				if (texture->Read(&storage, hardwareMode) != SUCCESS) {
					goto done;
				}
			}
		}
		else {
			texture = new LegoTexture();
			if (texture->Read(&storage, hardwareMode) != SUCCESS) {
				goto done;
			}
		}

		if (TextureContainer()->Get(textureName) == NULL) {
			textureInfo = LegoTextureInfo::Create(textureName, texture);

			if (textureInfo == NULL) {
				goto done;
			}

			TextureContainer()->Add(textureName, textureInfo);
		}

		delete[] textureName;
		textureName = NULL;
		delete texture;
		texture = NULL;
	}

	if (storage.SetPosition(4) != SUCCESS) {
		goto done;
	}

	m_parts = new LegoNamedPartList();

	if (storage.Read(&numROIs, sizeof(LegoU32)) != SUCCESS) {
		goto done;
	}

	for (i = 0; i < numROIs; i++) {
		if (storage.Read(&roiNameLength, sizeof(LegoU32)) != SUCCESS) {
			goto done;
		}

		roiName = new LegoChar[roiNameLength + 1];
		if (storage.Read(roiName, roiNameLength) != SUCCESS) {
			goto done;
		}

		roiName[roiNameLength] = '\0';
		strlwr(roiName);

		if (storage.Read(&numLODs, sizeof(LegoU32)) != SUCCESS) {
			goto done;
		}
		if (storage.Read(&roiInfoOffset, sizeof(LegoU32)) != SUCCESS) {
			goto done;
		}

		if (numLODs > g_partPresenterConfig2) {
			surplusLODs = numLODs - g_partPresenterConfig2;
			numLODs = g_partPresenterConfig2;
		}
		else {
			surplusLODs = 0;
		}

		lods = new LegoLODList();

		for (j = 0; j < numLODs; j++) {
			LegoLOD* lod = new LegoLOD(VideoManager()->GetRenderer());

			if (lod->Read(VideoManager()->GetRenderer(), TextureContainer(), &storage) != SUCCESS) {
				goto done;
			}

			if (j == 0) {
				if (surplusLODs != 0 && lod->IsExtraLOD()) {
					numLODs++;
					surplusLODs--;
				}
			}

			lods->Append(lod);
		}

		storage.SetPosition(roiInfoOffset);

		namedPart = new LegoNamedPart(roiName, lods);
		m_parts->Append(namedPart);

		delete[] roiName;
		roiName = NULL;
	}

	result = SUCCESS;

done:
	if (roiName != NULL) {
		delete[] roiName;
	}
	if (result != SUCCESS && m_parts != NULL) {
		delete m_parts;
		m_parts = NULL;
	}

	return result;
}

// FUNCTION: LEGO1 0x1007deb0
void LegoPartPresenter::ReadyTickle()
{
	MxStreamChunk* chunk = m_subscriber->PeekData();

	if (chunk != NULL && chunk->GetTime() <= m_action->GetElapsedTime()) {
		ParseExtra();
		ProgressTickleState(e_starting);

		chunk = m_subscriber->PopData();
		MxResult result = Read(*chunk);
		m_subscriber->FreeDataChunk(chunk);

		if (result == SUCCESS) {
			Store();
		}

		EndAction();
	}
}

// FUNCTION: LEGO1 0x1007df20
void LegoPartPresenter::Store()
{
	LegoNamedPartListCursor partCursor(m_parts);
	LegoNamedPart* part;

	while (partCursor.Next(part)) {
		ViewLODList* lodList = GetViewLODListManager()->Lookup(part->GetName()->GetData());

		if (lodList == NULL) {
			lodList = GetViewLODListManager()->Create(part->GetName()->GetData(), part->GetList()->GetNumElements());

			LegoLODListCursor lodCursor(part->GetList());
			LegoLOD* lod;

			while (lodCursor.First(lod)) {
				lodCursor.Detach();
				lodList->PushBack(lod);
			}
		}
		else {
			lodList->Release();
		}
	}

	if (m_parts != NULL) {
		delete m_parts;
	}

	m_parts = NULL;
}
