//
// Tools_StreamingModuleNames.cpp
//
// Created by Richard Rice - 10/04/2006
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#ifdef ASURA_USE_STREAMING
#include "Streaming/Tools_StreamingModuleNames.h"

//asura
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Mesh/Asura_Mesh.h"
#include "Rendering/Asura_Module.h"

//tools
#include "Streaming/Tools_Streaming_System.h"


////////////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------**
** Tools_StreamingModuleNames **
**----------------------------*/
u_int Tools_StreamingModuleNames::s_uNumConnectedZones = 0;
Tools_StreamingModuleNames_Zone** Tools_StreamingModuleNames::s_ppxConnectedZones = 0;


void Tools_StreamingModuleNames::Initialise()
{
}

void Tools_StreamingModuleNames::Shutdown()
{
	for(u_int i=0;i<s_uNumConnectedZones;i++)
	{
		delete s_ppxConnectedZones[i];
	}
	delete [] s_ppxConnectedZones;

	s_ppxConnectedZones = 0;
	s_uNumConnectedZones = 0;
}

void Tools_StreamingModuleNames::ResyncAll()
{
	ResyncConnectionIndices();
	for(u_int i=0;i<s_uNumConnectedZones;i++)
	{
		s_ppxConnectedZones[i]->Resync(i);
	}
}

void Tools_StreamingModuleNames::ResyncConnectionIndices()
{
	const Asura_Streaming_Zone* pxMainZone = Tools_Streaming_System::GetStreamingZoneForThisLevel();

	//Determine if connection indices have changed
	int* piConnectionRemap = s_uNumConnectedZones ? new int[s_uNumConnectedZones] : 0;
	bool bConnectionIndicesChanged = pxMainZone->GetNumConnection() != s_uNumConnectedZones;

	for(u_int i=0;i<s_uNumConnectedZones;i++)
	{
		piConnectionRemap[i] = -1;
		
		for(u_int j=0;j<pxMainZone->GetNumConnection();j++)
		{
			const Asura_Streaming_Connection* pxConnection = pxMainZone->GetConnection(j);

			if(!stricmp(s_ppxConnectedZones[i]->GetName(),pxConnection->GetToZoneName()))
			{
				//found matching zone
				piConnectionRemap[i] = j;
				break;
			}
		}
		if(piConnectionRemap[i] != (int)i) bConnectionIndicesChanged = true;
	}
	//update connection indices in module linking
	if(bConnectionIndicesChanged)
	{
		Asura_Module_System::GetCurrentHandler().RemapModuleLinkConnectionIndices(s_uNumConnectedZones,piConnectionRemap);
	}

	//Update array of zones if changed
	if(bConnectionIndicesChanged)
	{
		u_int uNewNumConnectedZones = pxMainZone->GetNumConnection();
		Tools_StreamingModuleNames_Zone** ppxNewConnectedZones = uNewNumConnectedZones ? new Tools_StreamingModuleNames_Zone*[uNewNumConnectedZones] : 0;

		for(u_int i=0;i<uNewNumConnectedZones;i++)
		{
			ppxNewConnectedZones[i] = 0;
		}

		//copy over any old ones that are still in use
		for(u_int i=0;i<s_uNumConnectedZones;i++)
		{
			if(piConnectionRemap[i] == -1)
			{
				delete s_ppxConnectedZones[i];
			}
			else
			{
				ppxNewConnectedZones[piConnectionRemap[i]] = s_ppxConnectedZones[i];
			}
		}

		//allocate any that didn't exist already
		for(u_int i=0;i<uNewNumConnectedZones;i++)
		{
			if(ppxNewConnectedZones[i]) continue;

			const Asura_Streaming_Connection* pxConnection = pxMainZone->GetConnection(i);

			ppxNewConnectedZones[i] = new Tools_StreamingModuleNames_Zone(pxConnection->GetToZoneName());
			ppxNewConnectedZones[i]->Resync(i);
		}

		//replace array
		delete [] s_ppxConnectedZones;
		s_ppxConnectedZones = ppxNewConnectedZones;
		s_uNumConnectedZones = uNewNumConnectedZones;
	}

	delete [] piConnectionRemap;
}

void Tools_StreamingModuleNames::ResyncConnection(u_int uIndex)
{
	Asura_Assert(uIndex<s_uNumConnectedZones,"");
	s_ppxConnectedZones[uIndex]->Resync(uIndex);
}

void Tools_StreamingModuleNames::ReadFromChunkStream(Asura_Chunk_Stream& xStream,int iVersion)
{
	if(s_uNumConnectedZones)
	{
		Asura_Assert(0,"Streaming module names already loaded");
		Shutdown();
		Initialise();
	}

	xStream >> s_uNumConnectedZones;
	s_ppxConnectedZones = s_uNumConnectedZones ? new Tools_StreamingModuleNames_Zone*[s_uNumConnectedZones] : 0; 

	for(u_int i=0;i<s_uNumConnectedZones;i++)
	{
		s_ppxConnectedZones[i] = new Tools_StreamingModuleNames_Zone;
		s_ppxConnectedZones[i]->ReadFromChunkStream(xStream,iVersion);
	}

	
}

void Tools_StreamingModuleNames::WriteToChunkStream(Asura_Chunk_Stream& xStream)
{
	xStream << s_uNumConnectedZones;
	for(u_int i=0;i<s_uNumConnectedZones;i++)
	{
		s_ppxConnectedZones[i]->WriteToChunkStream(xStream);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------**
** Tools_StreamingModuleNames_Zone **
**---------------------------------*/

Tools_StreamingModuleNames_Zone::Tools_StreamingModuleNames_Zone()
{
	m_uNumModules = 0;
	m_pxModuleNames = 0;
}

Tools_StreamingModuleNames_Zone::Tools_StreamingModuleNames_Zone(const char* szZoneName)
{
	m_xZoneName = szZoneName;
	m_uNumModules = 0;
	m_pxModuleNames = 0;
}

Tools_StreamingModuleNames_Zone::~Tools_StreamingModuleNames_Zone()
{
	delete [] m_pxModuleNames;
}

void Tools_StreamingModuleNames_Zone::Resync(u_int uIndex)
{
	//If zone hasn't been loaded yet ,we are out of luck
	Asura_Hash_ID uHash = Asura_GetHashID(m_xZoneName.GetString());
	Asura_Module_Handler* pxHandler = Asura_Module_System::GetHandler(uHash);
	if(!pxHandler) return;

	//Determine if any old module indices have changed
	int* piModuleRemap = m_uNumModules ? new int[m_uNumModules] : 0;
	bool bModuleIndicesChanged = m_uNumModules != (u_int)pxHandler->GetNumberOfModules();

	for(u_int i=0;i<m_uNumModules;i++)
	{
		//does module exist at same index as before?
		if(i<(u_int)pxHandler->GetNumberOfModules())
		{
			if(!m_pxModuleNames[i].Compare(pxHandler->GetMeshForModule(i)->Name))
			{
				piModuleRemap[i] = i;
				continue;
			}
		}
		//check as a different index
		piModuleRemap[i] = -1;
		bModuleIndicesChanged = true;

		for(u_int j=0;j<(u_int)pxHandler->GetNumberOfModules();j++)
		{
			if(!m_pxModuleNames[i].Compare(pxHandler->GetMeshForModule(j)->Name))
			{
				piModuleRemap[i] = j;
				break;
			}
		}
	}

	//update module indices in module linking
	if(bModuleIndicesChanged)
	{
		Asura_Module_System::GetCurrentHandler().RemapModuleLinkModuleIndices(uIndex,m_uNumModules,piModuleRemap);
	}

	//update array of module names if changed
	if(bModuleIndicesChanged)
	{
		u_int uNewNumModules = pxHandler->GetNumberOfModules();
		Asura_String* pxNewModuleNames = uNewNumModules ? new Asura_String[uNewNumModules] : 0;

		for(u_int i=0;i<uNewNumModules;i++)
		{
			pxNewModuleNames[i] = pxHandler->GetMeshForModule(i)->Name;
		}

		//replace array
		delete [] m_pxModuleNames;
		m_pxModuleNames = pxNewModuleNames;
		m_uNumModules = uNewNumModules;
	}

	delete [] piModuleRemap;
}

void Tools_StreamingModuleNames_Zone::ReadFromChunkStream(Asura_Chunk_Stream& xStream,int iVersion)
{
	xStream >> m_xZoneName;
	xStream >> m_uNumModules;

	m_pxModuleNames = m_uNumModules ? new Asura_String[m_uNumModules] : 0;

	for(u_int i=0;i<m_uNumModules;i++)
	{
		xStream >> m_pxModuleNames[i];
	}
}

void Tools_StreamingModuleNames_Zone::WriteToChunkStream(Asura_Chunk_Stream& xStream)
{
	xStream << m_xZoneName;
	xStream << m_uNumModules;

	for(u_int i=0;i<m_uNumModules;i++)
	{
		xStream << m_pxModuleNames[i];
	}
}

#endif // ASURA_USE_STREAMING
