//
// Asura_StaticCorona_System.cpp
//
// Created by Richard Rice - 21/02/2003
// 
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "SpecialFX/Asura_StaticCorona_System.h"
#include "Debugging/Asura_Assert.h"

#include "Core/Asura_TextureManagement.h"
#include "SpecialFX/Asura_Corona_System.h"

#ifdef ASURA_TOOLS
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#include "Lighting/Tools_LightSets.h"
#ifdef ASURA_USE_MATERIAL_RESPONSE_SYSTEM
#include "Materials/Tools_MaterialResponse_System.h"
#endif
#endif

///////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Asura_StaticCorona_System **
**---------------------------*/

#ifdef ASURA_USE_STREAMING
Asura_StaticCorona_Handler*							Asura_StaticCorona_System::s_pxCurrentHandler;
Asura_TemplateHashTree<Asura_StaticCorona_Handler>	Asura_StaticCorona_System::s_xHandlerTree;
#else //#ifdef ASURA_USE_STREAMING
Asura_StaticCorona_Handler Asura_StaticCorona_System::s_xHandler;
#endif //#else //#ifdef ASURA_USE_STREAMING

void Asura_StaticCorona_System::Initialise()
{
#ifndef ASURA_USE_STREAMING

	s_xHandler.Initialise();
#endif //#else // #ifndef ASURA_USE_STREAMING
}

void Asura_StaticCorona_System::Shutdown()
{
#ifdef ASURA_USE_STREAMING
	// Just free up the whole tree
	s_xHandlerTree.Reset();

#else // #ifdef ASURA_USE_STREAMING

	s_xHandler.Shutdown();
#endif //#else // #ifdef ASURA_USE_STREAMING
}

void Asura_StaticCorona_System::Reset()
{
#ifdef ASURA_USE_STREAMING
	s_xHandlerTree.Traverse(Reset_Callback);

#else // #ifdef ASURA_USE_STREAMING

	s_xHandler.Reset();
#endif //#else // #ifdef ASURA_USE_STREAMING
}

#ifdef ASURA_USE_STREAMING
void Asura_StaticCorona_System::Reset_Callback(Asura_Hash_ID uHash, Asura_StaticCorona_Handler* pxData)
{
	// Don't need the hash, just reset the data in the pointer
	if (pxData)
	{
		pxData->Reset();
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_StaticCorona_System::CreateHandler
// Author       : Anthony Gregson
// Date         : 23-Mar-2006
//
// Description  : Attempts to create a static corona system handler with the specified
//				  hash-ID.
//
// Parameters   : The hash-ID of the handler to create.
//
// Return value : A pointer to the handler created.
////////////////////////////////////////////////////////////////////////////////

Asura_StaticCorona_Handler* Asura_StaticCorona_System::CreateHandler(const Asura_Hash_ID uHash)
{
	Asura_StaticCorona_Handler* pxHandler = s_xHandlerTree.GetDataByHash(uHash);

	// Do we already have this handler?
	if(pxHandler == NULL)
	{
		// No - create one...
		pxHandler = s_xHandlerTree.AddNode(uHash);
	}
	else
	{
		// Yes - this shouldn't happen...
		Asura_Assert(false, "Already have a static corona handler with this hash ID.");
	}

	// Return the handler...
	return(pxHandler);
}

/// End of function 'Asura_StaticCorona_System::CreateHandler'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_StaticCorona_System::DestroyHandler
// Author       : Anthony Gregson
// Date         : 23-Mar-2006
//
// Description  : Attempts to destroy the static corona system handler with the specified
//				  unique hash-ID.
//
// Parameters   : The hash-ID of the handler to destroy.
//
// Return value : "true" if destroyed, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_StaticCorona_System::DestroyHandler(const Asura_Hash_ID uHash)
{
	// Is this the current handler?
	if(s_pxCurrentHandler == s_xHandlerTree.GetDataByHash(uHash))
	{
		s_pxCurrentHandler	= NULL;
	}

	// Call the delete function...
	return(s_xHandlerTree.DeleteNodeByHash(uHash));
}

/// End of function 'Asura_StaticCorona_System::DestroyHandler'. ///
#endif //#ifdef ASURA_USE_STREAMING

///////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------**
** Asura_StaticCorona_Handler **
**----------------------------*/

void Asura_StaticCorona_Handler::Initialise()
{
	m_pxCoronas = NULL;
	m_iArraySize = 0;
	m_iNumberOfCoronas = 0;
}

void Asura_StaticCorona_Handler::Shutdown()
{
	for(int i=0;i<m_iArraySize;i++)
	{
		if(m_pxCoronas[i].m_bNotAllocated) continue;
		m_pxCoronas[i].DeleteCorona();
	}

	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_CORONA);
	{
		Asura_ResourceDeallocateArray(m_pxCoronas);
	}
	Asura_ResourcePopType();
}

void Asura_StaticCorona_Handler::Reset()
{
	Shutdown();
	Initialise();
}


#if 1
int Asura_StaticCorona_Handler::AllocateOnLoad(int iNumberOfCoronas)
{
	int iRetVal = m_iArraySize;
	IncreaseAllocation(iNumberOfCoronas);

	m_iNumberOfCoronas += iNumberOfCoronas;

	for(int i=iRetVal;i<m_iArraySize;i++)
	{
		m_pxCoronas[i].m_bNotAllocated = 0;
	}
	return iRetVal;
}

bool Asura_StaticCorona_Handler::IncreaseAllocation(int iAmount)
{
	//allocate an enlarged array
	int iNewArraySize = m_iArraySize+iAmount;

	Asura_StaticCorona* pxNewCoronas = NULL;

	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_CORONA);
	{
		Asura_ResourceAllocateArray(pxNewCoronas, iNewArraySize);
	}
	Asura_ResourcePopType();

	if(!pxNewCoronas)
	{
		Asura_Assert(pxNewCoronas,"Failed to allocate coronas");
		return false;
	}

	//copy the old ones
	for(int i=0;i<m_iArraySize;i++)
	{
		
		pxNewCoronas[i] = m_pxCoronas[i];
		pxNewCoronas[i].m_iCoronaHandle = m_pxCoronas[i].m_iCoronaHandle;
#ifdef ASURA_TOOLS
		pxNewCoronas[i].m_uID = m_pxCoronas[i].m_uID;
		pxNewCoronas[i].m_uTemplateGuid = m_pxCoronas[i].m_uTemplateGuid;
		pxNewCoronas[i].m_iTemplateVersion = m_pxCoronas[i].m_iTemplateVersion;
#endif // ASURA_TOOLS
	}
	//initialise the new ones
	for(int i=m_iArraySize;i<iNewArraySize;i++)
	{
		pxNewCoronas[i].Initialise();
		pxNewCoronas[i].m_bNotAllocated = 1;
	}

	//and replace corona array

	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_CORONA);
	{
		Asura_ResourceDeallocateArray(m_pxCoronas);
	}
	Asura_ResourcePopType();

	m_pxCoronas = pxNewCoronas;
	m_iArraySize = iNewArraySize;

	return true;
}

#else
void Asura_StaticCorona_Handler::AllocateOnLoad(int iNumberOfCoronas)
{
	Asura_Assert(!m_pxCoronas,"Coronas have already been allocated");

	m_iArraySize = iNumberOfCoronas;
	m_iNumberOfCoronas = iNumberOfCoronas;

	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_CORONA);
	{
		Asura_ResourceAllocateArray(m_pxCoronas, m_iArraySize);
	}
	Asura_ResourcePopType();

	if(m_pxCoronas)
	{
		for(int i=0;i<m_iArraySize;i++)
		{
			m_pxCoronas[i].Initialise();
		}
	}
	else
	{
		Asura_Assert(m_pxCoronas,"Failed to allocate coronas");
		m_iArraySize = 0;
		m_iNumberOfCoronas = 0;
	}
}
#endif

Asura_StaticCorona* Asura_StaticCorona_Handler::GetCorona(int iIndex)
{
	if(!m_pxCoronas)
	{
		Asura_Assert(m_pxCoronas,"");
		return 0;
	}
	if(!(iIndex>=0 && iIndex < m_iArraySize))
	{
		Asura_Assert(iIndex>=0 && iIndex < m_iArraySize,"");
		return 0;
	}

	if(m_pxCoronas[iIndex].m_bNotAllocated)
	{
		Asura_Assert(!m_pxCoronas[iIndex].m_bNotAllocated,"Trying to access invalid corona");
		return 0;
	}

	return &m_pxCoronas[iIndex];
}

#ifdef ASURA_TOOLS
int Asura_StaticCorona_Handler::AllocateCorona()
{
	//allocate more space if needed
	if(m_iNumberOfCoronas == m_iArraySize)
	{
		IncreaseAllocation(10);
	}

	//find an unused corona
	for(int i=0;i<m_iArraySize;i++)
	{
		if(m_pxCoronas[i].m_bNotAllocated)
		{
			m_pxCoronas[i].Initialise();
			m_iNumberOfCoronas++;
			return i;
		}
	}

	//hmm , not good to get here
	Asura_Assert(0,"Failed to find an unused corona slot.(Corona storage is corrupted)");
	return -1;
}


void Asura_StaticCorona_Handler::DeallocateCorona(int iIndex)
{
	Asura_StaticCorona* pxCorona = GetCorona(iIndex);
	if(pxCorona)
	{
		pxCorona->DeleteCorona();
		pxCorona->m_bNotAllocated = 1;
		m_iNumberOfCoronas--;
	}
}

bool Asura_StaticCorona_Handler::IsValidCorona(int iIndex)
{
	if(iIndex<0) return false;
	if(iIndex>=m_iArraySize) return false;
	return !m_pxCoronas[iIndex].m_bNotAllocated;
}

void Asura_StaticCorona_Handler::BuildTextureTree(Asura_NameTree* pxNameTree)
{
	Asura_Assert(pxNameTree,"");

	for(int i=0;i<m_iArraySize;i++)
	{
		if(IsValidCorona(i))
		{
			if(Tools_Level_Export::IsExport())
			{
				//ignore coronas that don't belong to this light set. RAR 01/06/2006
				if(!m_pxCoronas[i].IsMemberOfCurrentLightSet()) continue;
			}
			
			m_pxCoronas[i].BuildTextureTree(pxNameTree);
		}
	}
}

#ifdef ASURA_USE_MATERIAL_RESPONSE_SYSTEM
u_int Asura_StaticCorona_Handler::GetTextureUsage(u_int uTextureHash)
{
	for(int i=0;i<m_iArraySize;i++)
	{
		if(IsValidCorona(i))
		{
			if (Asura_Texture_Management::GetHashIDFromIndex( m_pxCoronas[i].m_iTextureID ) == uTextureHash)
				return Tools_MaterialResponse_System::TU_ALBEDO;
		}
	}
	return 0;
}
#endif


int Asura_StaticCorona_Handler::GetCoronaIndexFromID(u_int uID)
{
	for(int i=0;i<m_iArraySize;i++)
	{
		if(IsValidCorona(i))
		{
			if(m_pxCoronas[i].m_uID == uID)
			{
				return i;
			}
		}
	}
	return -1;
}

void Asura_StaticCorona_Handler::UpdateAllCoronas()
{
	for(int i=0;i<m_iArraySize;i++)
	{
		if(IsValidCorona(i))
		{
			m_pxCoronas[i].UpdateCorona();
		}
	}
}

#endif //ASURA_TOOLS

///////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------**
** Asura_StaticCorona **
**--------------------*/

#ifdef ASURA_TOOLS
u_int Asura_StaticCorona::s_uNextID = 1;
#endif

void Asura_StaticCorona::Initialise()
{
	m_fRed = 1.0f;
	m_fGreen = 1.0f;
	m_fBlue = 1.0f;
	m_fRadius = 0.5f;
	m_iTextureID = ASURA_TEXTURE_NONE;

	m_uFlags = 0;
	m_bUseDirectionEffect = true;

	m_fVolumetricDepthScale = Asura_Corona_System::GetDefaultVolumetricDepthScale();

	m_xPosition.Zero();
	m_xDirection.Zero();
	m_xDirection.z = 1.0f;

	m_iCoronaHandle = ASURA_INVALID_HANDLE;

	#ifdef ASURA_TOOLS
	m_uID = 0;
	m_uTemplateGuid = ASURA_GUID_UNREGISTERED;
	m_iTemplateVersion = 0;
	m_xName.MakeBlank();
	m_uLightSets = 0xffffffff;
	#endif

}

const Asura_StaticCorona& Asura_StaticCorona::operator=(const Asura_StaticCorona& xCorona)
{
	//copy everything but the corona handle
	m_fRed = xCorona.m_fRed;
	m_fGreen = xCorona.m_fGreen;
	m_fBlue = xCorona.m_fBlue;
	m_fRadius = xCorona.m_fRadius;
	m_iTextureID = xCorona.m_iTextureID;
	m_xPosition = xCorona.m_xPosition;
	m_xDirection = xCorona.m_xDirection;
	m_uFlags = xCorona.m_uFlags;
	m_fVolumetricDepthScale = xCorona.m_fVolumetricDepthScale;

	#ifdef ASURA_TOOLS
	m_xName = xCorona.m_xName;
	m_uLightSets = xCorona.m_uLightSets;
	#endif

	return *this;
}

void Asura_StaticCorona::UpdateCorona()
{
	if(m_iTextureID != ASURA_TEXTURE_NONE)
	{
		//allocate corona if needed
		if(m_iCoronaHandle == ASURA_INVALID_HANDLE)
		{
			m_iCoronaHandle = Asura_Corona_System::CreateCorona();
			if(m_iCoronaHandle == ASURA_INVALID_HANDLE) return;
		}

		Asura_Corona_System::SetPosition(m_iCoronaHandle,m_xPosition);
		Asura_Corona_System::SetDirection(m_iCoronaHandle,m_xDirection);
		Asura_Corona_System::SetRadius(m_iCoronaHandle,m_fRadius);
		Asura_Corona_System::SetTextureIndex(m_iCoronaHandle,m_iTextureID);
		Asura_Corona_System::SetColour(m_iCoronaHandle,m_fRed,m_fGreen,m_fBlue);
		Asura_Corona_System::SetFlag_UseDirectionEffect(m_iCoronaHandle,m_bUseDirectionEffect);
		Asura_Corona_System::SetFlag_DontFadeWithDistance(m_iCoronaHandle,m_bDontFadeWithDistance);
		Asura_Corona_System::SetFlag_DontFadeNear(m_iCoronaHandle,m_bDontFadeNear);
		Asura_Corona_System::SetFlag_UseModuleLinking(m_iCoronaHandle,m_bUseModuleLinking);
		Asura_Corona_System::SetVolumetricDepthScale(m_iCoronaHandle,m_fVolumetricDepthScale);
		#ifdef ASURA_TOOLS
		Asura_Corona_System::SetFlag_Hidden(m_iCoronaHandle,!IsMemberOfCurrentLightSet());
		#endif


	}
	else if(m_iCoronaHandle != ASURA_INVALID_HANDLE)
	{
		DeleteCorona();		
	}
}

void Asura_StaticCorona::DeleteCorona()
{
	if(m_iCoronaHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Corona_System::DestroyCorona(m_iCoronaHandle);
		m_iCoronaHandle = ASURA_INVALID_HANDLE;
	}
}

#ifdef ASURA_TOOLS
void Asura_StaticCorona::BuildTextureTree(Asura_NameTree* pxNameTree)
{
	Asura_Assert(pxNameTree,"");

	if(m_iTextureID == ASURA_TEXTURE_NONE)
	{
		m_iTextureIDForSave = ASURA_TEXTURE_NONE;
	}
	else
	{
		//get the texture name
		const Asura_String* pxName = Asura_Texture_Management::GetTextureName(m_iTextureID);
		Asura_Assert(pxName,"");

		//Add to the name tree for saving if needed , and get the saving index
		pxNameTree->FindNodeIndexByName(pxName->GetString(),true,m_iTextureIDForSave);
	}
	
}
#endif //ASURA_TOOLS


#ifdef ASURA_TOOLS
void Asura_StaticCorona::ApplyChangesForTemplateInstance(class Tools_EntityTemplateInstance* pxInstance)
{
	m_xPosition = pxInstance->TransformPosition(m_xPosition);
	m_xDirection = m_xDirection * pxInstance->GetRelativeMatrix();
	
	UpdateCorona();	
}
#endif // ASURA_TOOLS


#ifdef ASURA_TOOLS
bool Asura_StaticCorona::IsMemberOfCurrentLightSet() const
{
	return Tools_LightSets::IsMemberOfCurrentLightSets(m_uLightSets);
}
#endif //ASURA_TOOLS
