//
// Asura_Chunk_GlobalIllumination.cpp
//
// Created by KJFL - 2004-06-09 12:33:49
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#ifdef ASURA_TOOLS // this chunk is only used in Tools builds

// This file's header.
#include "ChunkSystem/Asura_Chunk_GlobalIllumination.h"

#include "ChunkSystem/Asura_Chunk_GI_Solution.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_ModuleLinking.h"//for module index conversion
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "ChunkSystem/Asura_Chunk_Stream_WindowsFile.h"
#include "ChunkSystem/Asura_SaveBlock.h"
#include "Core/Memory/Asura_Mem.h"

#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_Logfile.h"
#include "FileAccess/Asura_FileAccess.h"

#include "Lighting/Tools_PhotonMapping_LightSource.h"
#include "Lighting/Tools_PhotonMapping_System.h"
#include "Lighting/Tools_PhotonMapping_System_Internal.h"
#include "Lighting/Tools_LightSets.h"
#include "Load&Save/Tools_SaveAsura.h"
//#include "Mesh/Asura_Mesh.h"
#include "Rendering/Asura_Module.h"

#include "Load&Save/Tools_Zlib.h"

//////////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------------**
** Asura_Chunk_GlobalIllumination **
**--------------------------------*/

struct Asura_LightSourceChunk_V0
{
	float				m_fAngle;
	Asura_Vector_3		m_xPosition;
	Asura_Matrix_3x3	m_xOrientation;
	Asura_Vector_3		m_xColour;
	float				m_fPower;
	u_int				m_uAccuracy;
	u_int				m_bPhotonMapValid :1,
						m_bParallel :1,	
						m_bBleached :1,  
						m_bHasPhotonMap : 1,
						m_bShortRange : 1,
						m_bUseRange : 1;//valid from version 9
						
};

struct Asura_LightSourceChunk_V8 : Asura_LightSourceChunk_V0
{
	u_int				m_uPhotonMapValidity;
};

struct Asura_LightSourceChunk_V9 : Asura_LightSourceChunk_V8
{
	float				m_fInnerRange;
	float				m_fOuterRange;
};

struct Asura_LightSourceChunk_V11 : Asura_LightSourceChunk_V9
{
	float				m_fReflectionChance;
};
struct Asura_LightSourceChunk_V12 : Asura_LightSourceChunk_V11
{
	u_int				m_uLightSets;
};


bool Asura_Chunk_GlobalIllumination::Process( Asura_Handle hFile ) const
{	
	// first check the validity of the chunk 

	// Deja vu?
//	if ( Tools_EnvironmentalLightTransport::IsEnvironmentProcessed() )
//	{
//		Asura_Assert( false, "ELT data loaded twice?");
//		return false;
//	}
	
	// Too new?
    if( Version > CHUNK_VERSION )
    {
        Asura_Assert( false, "Chunk version is too new. You need a new executable." );
        return false;
    }
	
	// Too old?
    if( Version < CHUNK_VERSION ) 
    {
        Asura_OutputToDebugger(( "INFO: Loading chunk v%i (current version is v%i.)\n", Version, CHUNK_VERSION ));
		// don't need to exit but we may need to touch up older data
    }


	if(Version>1)
	{
		//read next ID
		u_int uNextID; 
		ASURA_CHUNK_FILEEXTRACT(hFile,uNextID,u_int);
		uNextID &= TOOLS_PHOTONMAPPING_IDMASK;
		Tools_PhotonMapping_TemplateData::s_uNextID = Asura_Maths::Max(Tools_PhotonMapping_TemplateData::s_uNextID,uNextID);
	}

	//Determine how many light sets worth of global ambience is saved in this version
	if(Version>13)
	{
		u_int uNumSavedGlobalAmbience;
		ASURA_CHUNK_FILEEXTRACT(hFile, uNumSavedGlobalAmbience, u_int);

		for(u_int i=0;i<uNumSavedGlobalAmbience;i++)
		{
			u_int uSets;
			ASURA_CHUNK_FILEEXTRACT(hFile, uSets, u_int);

			Asura_RGBA xGlobalAmbience;
			ASURA_CHUNK_FILEEXTRACT(hFile,xGlobalAmbience,Asura_RGBA);
			Tools_PhotonMapping_System::SetGlobalAmbience(uSets,xGlobalAmbience);
		}
	}
	else
	{
		u_int uNumSavedGlobalAmbience = 0;
		if(Version>11)
		{
			uNumSavedGlobalAmbience = TOOLS_MAX_LIGHT_SETS;
		}
		else if(Version>5)
		{
			uNumSavedGlobalAmbience = 1;
		}

	
		for(u_int i=0;i<uNumSavedGlobalAmbience;i++)
		{
			Asura_RGBA xGlobalAmbience;
			ASURA_CHUNK_FILEEXTRACT(hFile,xGlobalAmbience,Asura_RGBA);
			Tools_PhotonMapping_System::SetGlobalAmbience(1<<i,xGlobalAmbience);
		}
	}

	//check number of modules with the module index conversion thing
	Asura_ModuleIndex_Conversion::CheckNumberOfModules(m_uNumberOfModules,Asura_Module_System::GetNumberOfModules());

	
	for (u_int u=0; u<m_uNumberOfLights; ++u)
	{
		Asura_Handle iHandle = Tools_PhotonMapping_System::CreateLightSource();

		Tools_PhotonMapping_LightData* pxLightData = Tools_PhotonMapping_System::GetLightDataTrackingHandlePool()->GetPointer( iHandle );

		Asura_Assert( pxLightData, "invaiid pointer" );

		
		//read inthe lightsource chunk data
		Asura_LightSourceChunk_V12 xChunkData;
		u_int uSize;

		if(Version>=12) uSize = sizeof(Asura_LightSourceChunk_V12);
		else if(Version>=11) uSize = sizeof(Asura_LightSourceChunk_V11);
		else if(Version>=9) uSize = sizeof(Asura_LightSourceChunk_V9);
		else if(Version>=8) uSize = sizeof(Asura_LightSourceChunk_V8);
		else uSize = sizeof(Asura_LightSourceChunk_V0);

		Asura_FileAccess::ReadData
		(
			hFile,
			&(xChunkData),
			uSize
		);

		pxLightData->m_xLightSource.m_fAngle = xChunkData.m_fAngle;			
		pxLightData->m_xLightSource.m_xPosition = xChunkData.m_xPosition;			
		pxLightData->m_xLightSource.m_xOrientation = xChunkData.m_xOrientation;
		pxLightData->m_xLightSource.m_xColour = xChunkData.m_xColour;
		pxLightData->m_xLightSource.m_fPower = xChunkData.m_fPower;
		pxLightData->m_xLightSource.m_uAccuracy = xChunkData.m_uAccuracy;
		pxLightData->m_xLightSource.m_bParallel = xChunkData.m_bParallel;
		pxLightData->m_xLightSource.m_bBleached = xChunkData.m_bBleached;
		pxLightData->m_xLightSource.m_bShortRange = xChunkData.m_bShortRange;
		pxLightData->m_xLightSource.m_bHasPhotonMap = xChunkData.m_bHasPhotonMap;
		if(Version >= 9)
		{
			pxLightData->m_xLightSource.m_bUseRange = xChunkData.m_bUseRange;
			pxLightData->m_xLightSource.m_fInnerRange = xChunkData.m_fInnerRange;
			pxLightData->m_xLightSource.m_fOuterRange = xChunkData.m_fOuterRange;
		}
		if(Version >= 11)
		{
			pxLightData->m_xLightSource.m_fReflectionChance = xChunkData.m_fReflectionChance;
		}
		if(Version >= 12)
		{
			pxLightData->m_xLightSource.m_uLightSets = xChunkData.m_uLightSets;
		}

		if(Version<8)
		{
			//for earlier versions ,assume that if valid it was calculated at the lights given accuracy
			pxLightData->m_xLightSource.m_uPhotonMapValidity = xChunkData.m_bPhotonMapValid ? xChunkData.m_uAccuracy : 0;
		}
		else
		{
			pxLightData->m_xLightSource.m_uPhotonMapValidity = xChunkData.m_uPhotonMapValidity;
		}

		if(Version < 4)
		{
			//In earlier versions m_fAngle is junk , so set to default
			pxLightData->m_xLightSource.m_fAngle = 360.0f;
		}
		if(Version < 5)
		{
			pxLightData->m_xLightSource.m_bShortRange = false;
		}

		if(Version > 1)
		{
			Asura_FileAccess::ReadData
			(
				hFile,
				&(pxLightData->m_xTemplateData),
				sizeof( Tools_PhotonMapping_TemplateData )
			);
		}

		if (Version == 0)
		{
			pxLightData->m_xLightSource.SetPower( pxLightData->m_xLightSource.GetPower() * 10.0f );
		}
		if( Version < 2)
		{
			pxLightData->m_xTemplateData.GenerateID();
		}

		Asura_Assert(!pxLightData->m_xTemplateData.IsMissionLight() || Tools_SaveAsura::IsUsingMissionGlobalIllumination(),"Found a light flagged as belonging to a mission while not editing a mission file (or while editing a mission that uses the common GI file).\nThis will be lost when the file is next saved.");

		if(Version < 3)
		{
			//need to set the HasPhotonMap flag
			pxLightData->m_xLightSource.SetHasPhotonMap(!(pxLightData->m_xLightSource.IsParallel()) && pxLightData->m_xLightSource.IsPhotonMapPartiallyValid());
		}

		if(Version >= 10)
		{
			ASURA_CHUNK_FILEEXTRACT_STRING(hFile,pxLightData->m_xLightSource.m_xName);
		}

		
		
		
		if (pxLightData->m_xLightSource.HasPhotonMap())
		{
			Tools_PhotonMap_DirectEstimation& xMap = pxLightData->m_xDirectEstimationPhotonMap;

			u_int uPhotonMapVersion = (Version<7) ? 0 : 1;
			if(!xMap.ProcessFromChunk(hFile,m_uNumberOfModules,uPhotonMapVersion))
			{
				pxLightData->m_xLightSource.SetPhotonMapValidity(0);
			}
		}
	}

	if(Version >= 12)
	{
		u_int uUsedLightSets;
		ASURA_CHUNK_FILEEXTRACT(hFile,uUsedLightSets,u_int);
		Tools_LightSets::SetUsedLightSets(uUsedLightSets);
	}

	if(Version >= 13)
	{
		float fZerothScale;
		ASURA_CHUNK_FILEEXTRACT(hFile,fZerothScale,float);
		Tools_PhotonMapping_System::SetSHZerothOrderResampleScale( fZerothScale );

		float fFirstScale;
		ASURA_CHUNK_FILEEXTRACT(hFile,fFirstScale,float);
		Tools_PhotonMapping_System::SetSHFirstOrderResampleScale( fFirstScale );
	}
	else
	{
		Tools_PhotonMapping_System::SetSHZerothOrderResampleScale( 1.0f );
		Tools_PhotonMapping_System::SetSHFirstOrderResampleScale( 1.0f );
	}


	// success
	return true;

}

Asura_Handle Asura_Chunk_GlobalIllumination::ImportFromChunkBuffer(const char* pcChunkBuffer,u_int uSavedID,u_int uNewID) const
{
	// Too new?
    if( Version > CHUNK_VERSION )
    {
        Asura_Assert( false, "Chunk version is too new. You need a new executable." );
        return ASURA_INVALID_HANDLE;
    }

	if(Version < 2)
	{
		//Shouldn't be possible for template files to have such an old chunk in them
		Asura_Assert(false,"Not expecting such an old light chunk in a template file");
        return ASURA_INVALID_HANDLE;
	}
	

	// KJFL - 2004-07-01 - if there are no lights, then we are done
	if (m_uNumberOfLights == 0) return ASURA_INVALID_HANDLE;


	if(Version>1)
	{
		//skip past next ID
		pcChunkBuffer += sizeof(u_int);
	}

	//Determine how many light sets worth of global ambience is saved in this version
	u_int uNumSavedGlobalAmbience = 0;
	u_int uSizePerGlobalAmbient = sizeof(Asura_RGBA);
	if(Version > 13)
	{
		ASURA_CHUNK_EXTRACT(pcChunkBuffer, uNumSavedGlobalAmbience, u_int);
		uSizePerGlobalAmbient += sizeof(u_int);
	}
	else if(Version > 11)
	{
		uNumSavedGlobalAmbience = TOOLS_MAX_LIGHT_SETS;
	}
	else if(Version>5)
	{
		uNumSavedGlobalAmbience = 1;
	}
	
	//skip ambience
	pcChunkBuffer += uSizePerGlobalAmbient*uNumSavedGlobalAmbience;
	
	for (u_int u=0; u<m_uNumberOfLights; ++u)
	{


		Tools_PhotonMapping_LightSource xLightSource;
		Tools_PhotonMapping_TemplateData xTemplateData;
		
		
		//need to read in old light structure and copy the entries
		Asura_LightSourceChunk_V12 xChunkData;
		if(Version>=12)
		{
			ASURA_CHUNK_EXTRACT(pcChunkBuffer,xChunkData,Asura_LightSourceChunk_V12);
		}
		else if(Version>=11)
		{
			ASURA_CHUNK_EXTRACT(pcChunkBuffer,*(Asura_LightSourceChunk_V11*)&xChunkData,Asura_LightSourceChunk_V11);
		}
		else if(Version>=9)
		{
			ASURA_CHUNK_EXTRACT(pcChunkBuffer,*(Asura_LightSourceChunk_V9*)&xChunkData,Asura_LightSourceChunk_V9);
		}
		else if(Version>=8)
		{
			ASURA_CHUNK_EXTRACT(pcChunkBuffer,*(Asura_LightSourceChunk_V8*)&xChunkData,Asura_LightSourceChunk_V8);
		}
		else
		{
			ASURA_CHUNK_EXTRACT(pcChunkBuffer,*(Asura_LightSourceChunk_V0*)&xChunkData,Asura_LightSourceChunk_V0);
		}


		xLightSource.m_fAngle = xChunkData.m_fAngle;
		xLightSource.m_xPosition = xChunkData.m_xPosition;			
		xLightSource.m_xOrientation = xChunkData.m_xOrientation;
		xLightSource.m_xColour = xChunkData.m_xColour;
		xLightSource.m_fPower = xChunkData.m_fPower;
		xLightSource.m_uAccuracy = xChunkData.m_uAccuracy;
		xLightSource.m_bParallel = xChunkData.m_bParallel;
		xLightSource.m_bBleached = xChunkData.m_bBleached;
		xLightSource.m_bShortRange = xChunkData.m_bShortRange;
		xLightSource.m_bHasPhotonMap = xChunkData.m_bHasPhotonMap;
		if(Version >= 9)
		{
			xLightSource.m_bUseRange = xChunkData.m_bUseRange;
			xLightSource.m_fInnerRange = xChunkData.m_fInnerRange;
			xLightSource.m_fOuterRange = xChunkData.m_fOuterRange;
		}
		if(Version >= 11)
		{
			xLightSource.m_fReflectionChance = xChunkData.m_fReflectionChance;
		}
		if(Version >= 12)
		{
			xLightSource.m_uLightSets = xChunkData.m_uLightSets;
		}

		if(Version<8)
		{
			//for earlier versions ,assume that if valid it was calculated at the lights given accuracy
			xLightSource.m_uPhotonMapValidity = xChunkData.m_bPhotonMapValid ? xChunkData.m_uAccuracy : 0;
		}
		else
		{
			xLightSource.m_uPhotonMapValidity = xChunkData.m_uPhotonMapValidity;
		}


		if(Version < 4)
		{
			//In earlier versions m_fAngle is junk , so set to default
			xLightSource.m_fAngle = 360.0f;
		}
		if(Version < 5)
		{
			xLightSource.m_bShortRange = false;
		}

		
		
		ASURA_CHUNK_EXTRACT(pcChunkBuffer,xTemplateData,Tools_PhotonMapping_TemplateData);
		
		if(Version < 3)
		{
			//need to set the HasPhotonMap flag
			xLightSource.SetHasPhotonMap(!(xLightSource.IsParallel()) && xLightSource.IsPhotonMapPartiallyValid());
		}

		if(Version >= 10)
		{
			ASURA_CHUNK_EXTRACT_STRING(pcChunkBuffer,xLightSource.m_xName);
		}
		
		
		//is this the light we were searching for?
		bool bWantThisLight= false;
		if(uSavedID)
		{
			bWantThisLight = (xTemplateData.m_uID == uSavedID);
		}
		else
		{
			//importing all lights... but need to ignore those that are members of templates
			bWantThisLight = xTemplateData.m_uTemplateGuid==ASURA_GUID_UNREGISTERED;
		}

		if(bWantThisLight)
		{
			//yep , allocate a new light , and copy the values
			Asura_Handle iHandle = Tools_PhotonMapping_System::CreateLightSource();
			Tools_PhotonMapping_LightData* pxLightData = Tools_PhotonMapping_System::GetLightDataTrackingHandlePool()->GetPointer( iHandle );

			Asura_Assert( pxLightData, "invaiid pointer" );

			pxLightData->m_xLightSource = xLightSource;
			pxLightData->m_xTemplateData = xTemplateData;
			//Invalidate photon map , since it will be for wrong environment
			pxLightData->m_xLightSource.SetPhotonMapValidity(0);
			pxLightData->m_xLightSource.SetHasPhotonMap(false);
			if(uNewID && uSavedID)
			{
				pxLightData->m_xTemplateData.m_uID = uNewID;
			}
			else
			{
				pxLightData->m_xTemplateData.GenerateID();
			}
			if(uSavedID)
			{
				return iHandle;
			}
		}	
		
		
		//Skip any remaining chunk data for light
		if (xLightSource.HasPhotonMap())
		{

			u_int uPhotonMapVersion = (Version<7) ? 0 : 1;
			Tools_PhotonMap_DirectEstimation::SkipFromChunkBuffer(pcChunkBuffer,m_uNumberOfModules,uPhotonMapVersion);
			
			
		}
	}

	// failed to find the light - or importing all lights
	return ASURA_INVALID_HANDLE;
}

bool Asura_Chunk_GlobalIllumination::Create( Asura_SaveBlock& xSaveBlock,bool bTemplateLights, bool bMissionLights )
{
	Asura_Chunk_SaveBlock_Stream xStream(&xSaveBlock);
	return Create(xStream, bTemplateLights, bMissionLights);
}

bool Asura_Chunk_GlobalIllumination::Create( Asura_Chunk_Stream& xStream,bool bTemplateLights, bool bMissionLights )
{
	u_int uChunkFlags = 0;
	xStream.WriteChunk_Start(ASURA_CHUNK_GLOBALILLUMINATION, CHUNK_VERSION, uChunkFlags);


	u_int uNumberOfLights = 0;
	u_int uNumberOfModules = Asura_Module_System::GetNumberOfModules();


	//Determine how many lights are being saved
	Asura_TrackingHandlePool<Tools_PhotonMapping_LightData>::Iterator xIterator = Tools_PhotonMapping_System::GetLightDataTrackingHandlePool()->GetIterator();

	for (xIterator.Begin(); !xIterator.End(); xIterator.Next())
	{
		Tools_PhotonMapping_LightData* pxLightData = xIterator.GetCurrentPointer();
		Asura_Assert( pxLightData, "invaiid pointer" );

		if(bTemplateLights != pxLightData->m_xTemplateData.IsTemplateLight()) continue;
		if(bMissionLights != pxLightData->m_xTemplateData.IsMissionLight()) continue;

		//Invalidate photon map if the number of modules is incorrect
		if(pxLightData->m_xLightSource.HasPhotonMap())
		{
			Tools_PhotonMap_DirectEstimation& xMap = pxLightData->m_xDirectEstimationPhotonMap;
			if ( xMap.m_uNumberOfModules != uNumberOfModules )
			{
				pxLightData->m_xLightSource.SetPhotonMapValidity(0);
				pxLightData->m_xLightSource.SetHasPhotonMap(false);
				xMap.Clear();
			}
		}
		++uNumberOfLights;
	}



	xStream << uNumberOfLights;
	xStream << uNumberOfModules;



	xStream << Tools_PhotonMapping_TemplateData::s_uNextID;

	//save the global ambience for each light set
	xStream << Tools_LightSets::GetNumUsedCombinations();

	for(Tools_UsedLightSetIterator xIt; !xIt.Done(); xIt.Next())
	{
		const u_int uSets = xIt.GetCurrent();
		xStream << uSets;
		xStream << Tools_PhotonMapping_System::GetGlobalAmbience(uSets);
	}

	// save out each light, and the associated photon map if any
	for (xIterator.Begin(); !xIterator.End(); xIterator.Next())
	{
		const Tools_PhotonMapping_LightData* pxLightData = xIterator.GetCurrentPointer();
		Asura_Assert( pxLightData, "invaiid pointer" );

		if(bTemplateLights != pxLightData->m_xTemplateData.IsTemplateLight()) continue;
		if(bMissionLights != pxLightData->m_xTemplateData.IsMissionLight()) continue;

		Asura_LightSourceChunk_V12 xChunkData;
		xChunkData.m_fAngle = pxLightData->m_xLightSource.m_fAngle;
		xChunkData.m_xPosition = pxLightData->m_xLightSource.m_xPosition;
		xChunkData.m_xOrientation = pxLightData->m_xLightSource.m_xOrientation;
		xChunkData.m_xColour = pxLightData->m_xLightSource.m_xColour;
		xChunkData.m_fPower = pxLightData->m_xLightSource.m_fPower;
		xChunkData.m_uAccuracy = pxLightData->m_xLightSource.m_uAccuracy;
		xChunkData.m_bPhotonMapValid = 0;//obsolete
		xChunkData.m_bParallel = pxLightData->m_xLightSource.m_bParallel;
		xChunkData.m_bBleached = pxLightData->m_xLightSource.m_bBleached;
		xChunkData.m_bShortRange = pxLightData->m_xLightSource.m_bShortRange;
		xChunkData.m_bHasPhotonMap = pxLightData->m_xLightSource.m_bHasPhotonMap;
		xChunkData.m_uPhotonMapValidity = pxLightData->m_xLightSource.m_uPhotonMapValidity;

		xChunkData.m_bUseRange = pxLightData->m_xLightSource.m_bUseRange;
		xChunkData.m_fInnerRange = pxLightData->m_xLightSource.m_fInnerRange;
		xChunkData.m_fOuterRange = pxLightData->m_xLightSource.m_fOuterRange;
		xChunkData.m_fReflectionChance = pxLightData->m_xLightSource.m_fReflectionChance;
		xChunkData.m_uLightSets = pxLightData->m_xLightSource.m_uLightSets;

		if(xChunkData.m_bParallel)
		{
			//We don't save photon maps for parallel lights even if they managed to get one somehow
			//(before being parallel). So , make sure they flags are set correctly. RAR 06/09/2004
			xChunkData.m_uPhotonMapValidity = false;
			xChunkData.m_bHasPhotonMap = false;
		}
		
		//This is only ever saved for tools , so just write out the structure in one go (without worrying about endianess)
		xStream.Write(sizeof(xChunkData), &xChunkData);
		xStream.Write(sizeof( Tools_PhotonMapping_TemplateData), &(pxLightData->m_xTemplateData));

		xStream << pxLightData->m_xLightSource.m_xName;

		if (!(pxLightData->m_xLightSource.IsParallel()) && pxLightData->m_xLightSource.HasPhotonMap())
		{
			const Tools_PhotonMap_DirectEstimation& xMap = pxLightData->m_xDirectEstimationPhotonMap;
			xMap.SaveToChunkStream(xStream);
		}

	}

	xStream << Tools_LightSets::GetUsedLightSets();
		
	xStream << Tools_PhotonMapping_System::GetSHZerothOrderResampleScale();
	xStream << Tools_PhotonMapping_System::GetSHFirstOrderResampleScale();

	xStream.WriteChunk_End();

    return true;

}

//////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Asura_Chunk_PolygonLights **
**---------------------------*/

struct Asura_PolygonLightSourceChunk_V0
{
		int					m_iModule;
		u_int				m_uLightID;
		float				m_fAngle;
		Asura_Vector_3		m_xPosition;
		Asura_Matrix_3x3	m_xOrientation;
		Asura_Vector_3		m_xColour;
		float				m_fPower;
		u_int				m_uAccuracy;
		u_int				m_bPhotonMapValid :1,
							m_bBleached :1,  
							m_bHasPhotonMap : 1,
							m_bShortRange : 1,
							m_bUseRange : 1;//valid from version 4
						
};

struct Asura_PolygonLightSourceChunk_V3 : Asura_PolygonLightSourceChunk_V0
{
	u_int				m_uPhotonMapValidity;
};

struct Asura_PolygonLightSourceChunk_V4 : Asura_PolygonLightSourceChunk_V3
{
	float				m_fInnerRange;
	float				m_fOuterRange;
};

struct Asura_PolygonLightSourceChunk_V5 : Asura_PolygonLightSourceChunk_V4
{
	float				m_fReflectionChance;
};

struct Asura_PolygonLightSourceChunk_V6 : Asura_PolygonLightSourceChunk_V5
{
	u_int				m_uLightSets;
};



bool Asura_Chunk_PolygonLights::Process( Asura_Handle hFile ) const
{	
	// first check the validity of the chunk 

	// Too new?
    if( Version > CHUNK_VERSION )
    {
        Asura_Assert( false, "Chunk version is too new. You need a new executable." );
        return false;
    }
	
	
	// Too old?
    if( Version < CHUNK_VERSION ) 
    {
        Asura_OutputToDebugger(( "INFO: Loading chunk v%i (current version is v%i.)\n", Version, CHUNK_VERSION ));
		// don't need to exit but we may need to touch up older data
    }


	// If there are no lights, then we are done
	if (m_uNumberOfLights == 0) return true;

	//check number of modules with the module index conversion thing
	Asura_ModuleIndex_Conversion::CheckNumberOfModules(m_uNumberOfModules,Asura_Module_System::GetNumberOfModules());


	
	for (u_int u=0; u<m_uNumberOfLights; ++u)
	{
		Asura_Handle iHandle = Tools_PhotonMapping_System::CreatePolygonLightSource();

		Tools_PhotonMapping_PolygonLightData* pxLightData = Tools_PhotonMapping_System::GetPolygonLightDataTrackingHandlePool()->GetPointer( iHandle );

		Asura_Assert( pxLightData, "invaiid pointer" );

		
		//read in the lightsource chunk data
		Asura_PolygonLightSourceChunk_V6 xChunkData;
		u_int uSize;

		if(Version>=6) uSize = sizeof(Asura_PolygonLightSourceChunk_V6);
		else if(Version>=5) uSize = sizeof(Asura_PolygonLightSourceChunk_V5);
		else if(Version>=4) uSize = sizeof(Asura_PolygonLightSourceChunk_V4);
		else if(Version>=3) uSize = sizeof(Asura_PolygonLightSourceChunk_V3);
		else uSize = sizeof(Asura_PolygonLightSourceChunk_V0);

		Asura_FileAccess::ReadData
		(
			hFile,
			&(xChunkData),
			uSize
		);

		pxLightData->m_xLightSource.m_iModule = xChunkData.m_iModule;
		pxLightData->m_xLightSource.m_uLightID = xChunkData.m_uLightID;
		pxLightData->m_xLightSource.m_fAngle = xChunkData.m_fAngle;			
		pxLightData->m_xLightSource.m_xPosition = xChunkData.m_xPosition;			
		pxLightData->m_xLightSource.m_xOrientation = xChunkData.m_xOrientation;
		pxLightData->m_xLightSource.m_xColour = xChunkData.m_xColour;
		pxLightData->m_xLightSource.m_fPower = xChunkData.m_fPower;
		pxLightData->m_xLightSource.m_uAccuracy = xChunkData.m_uAccuracy;
		pxLightData->m_xLightSource.m_bBleached = xChunkData.m_bBleached;
		pxLightData->m_xLightSource.m_bHasPhotonMap = xChunkData.m_bHasPhotonMap;
		pxLightData->m_xLightSource.m_bShortRange = xChunkData.m_bShortRange;
		if(Version>=4)
		{
			pxLightData->m_xLightSource.m_bUseRange = xChunkData.m_bUseRange;
			pxLightData->m_xLightSource.m_fInnerRange = xChunkData.m_fInnerRange;
			pxLightData->m_xLightSource.m_fOuterRange = xChunkData.m_fOuterRange;
		}
		if(Version>=5)
		{
			pxLightData->m_xLightSource.m_fReflectionChance = xChunkData.m_fReflectionChance;
		}
		if(Version>=6)
		{
			pxLightData->m_xLightSource.m_uLightSets = xChunkData.m_uLightSets;
		}

		if(Version<3)
		{
			//for earlier versions ,assume that if valid it was calculated at the lights given accuracy
			pxLightData->m_xLightSource.m_uPhotonMapValidity = xChunkData.m_bPhotonMapValid ? xChunkData.m_uAccuracy : 0;
		}
		else
		{
			pxLightData->m_xLightSource.m_uPhotonMapValidity = xChunkData.m_uPhotonMapValidity;
		}


		if(Version < 1)
		{
			pxLightData->m_xLightSource.m_bShortRange = false;
		}	
		
		if (pxLightData->m_xLightSource.HasPhotonMap())
		{
			Tools_PhotonMap_DirectEstimation& xMap = pxLightData->m_xDirectEstimationPhotonMap;

			u_int uPhotonMapVersion = (Version<2) ? 0 : 1;
			if(!xMap.ProcessFromChunk(hFile,m_uNumberOfModules,uPhotonMapVersion))
			{
				pxLightData->m_xLightSource.SetPhotonMapValidity(0);
			}
		}
	}

	// success
	return true;

}


bool Asura_Chunk_PolygonLights::Create( Asura_Chunk_Stream& xStream,bool bTemplateLights )
{
	if(bTemplateLights) return false;//for now ,save none to template file

	u_int uChunkFlags = 0;
	xStream.WriteChunk_Start(ASURA_CHUNK_POLYGONLIGHTS, CHUNK_VERSION, uChunkFlags);

	u_int uNumberOfLights = 0;
	u_int uNumberOfModules = Asura_Module_System::GetNumberOfModules();


	//Determine how many lights are being saved
	Asura_TrackingHandlePool<Tools_PhotonMapping_PolygonLightData>::Iterator xIterator = Tools_PhotonMapping_System::GetPolygonLightDataTrackingHandlePool()->GetIterator();

	for (xIterator.Begin(); !xIterator.End(); xIterator.Next())
	{
		Tools_PhotonMapping_PolygonLightData* pxLightData = xIterator.GetCurrentPointer();
		Asura_Assert( pxLightData, "invaiid pointer" );

		//if(bTemplateLights != pxLightData->m_xTemplateData.IsTemplateLight()) continue;


		//Invalidate photon map if the number of modules is incorrect
		if(pxLightData->m_xLightSource.HasPhotonMap())
		{
			Tools_PhotonMap_DirectEstimation& xMap = pxLightData->m_xDirectEstimationPhotonMap;
			if ( xMap.m_uNumberOfModules != uNumberOfModules )
			{
				pxLightData->m_xLightSource.SetPhotonMapValidity(0);
				pxLightData->m_xLightSource.SetHasPhotonMap(false);
				xMap.Clear();
			}
		}

		++uNumberOfLights;
	}



	xStream << uNumberOfLights;
	xStream << uNumberOfModules;

	// save out each light, and the associated photon map if any
	{
		for (xIterator.Begin(); !xIterator.End(); xIterator.Next())
		{
			const Tools_PhotonMapping_PolygonLightData* pxLightData = xIterator.GetCurrentPointer();
			Asura_Assert( pxLightData, "invaiid pointer" );

			//if(bTemplateLights != pxLightData->m_xTemplateData.IsTemplateLight()) continue;

			Asura_PolygonLightSourceChunk_V6 xChunkData;
			xChunkData.m_iModule = pxLightData->m_xLightSource.m_iModule;
			xChunkData.m_uLightID = pxLightData->m_xLightSource.m_uLightID;
			xChunkData.m_fAngle = pxLightData->m_xLightSource.m_fAngle;
			xChunkData.m_xPosition = pxLightData->m_xLightSource.m_xPosition;
			xChunkData.m_xOrientation = pxLightData->m_xLightSource.m_xOrientation;
			xChunkData.m_xColour = pxLightData->m_xLightSource.m_xColour;
			xChunkData.m_fPower = pxLightData->m_xLightSource.m_fPower;
			xChunkData.m_uAccuracy = pxLightData->m_xLightSource.m_uAccuracy;
			xChunkData.m_bPhotonMapValid = 0;//obsolete
			xChunkData.m_bBleached = pxLightData->m_xLightSource.m_bBleached;
			xChunkData.m_bHasPhotonMap = pxLightData->m_xLightSource.m_bHasPhotonMap;
			xChunkData.m_bShortRange = pxLightData->m_xLightSource.m_bShortRange;
			xChunkData.m_uPhotonMapValidity = pxLightData->m_xLightSource.m_uPhotonMapValidity;

			xChunkData.m_bUseRange = pxLightData->m_xLightSource.m_bUseRange;
			xChunkData.m_fInnerRange = pxLightData->m_xLightSource.m_fInnerRange;
			xChunkData.m_fOuterRange = pxLightData->m_xLightSource.m_fOuterRange;
			xChunkData.m_fReflectionChance = pxLightData->m_xLightSource.m_fReflectionChance;
			xChunkData.m_uLightSets = pxLightData->m_xLightSource.m_uLightSets;

		

			xStream.Write(sizeof( xChunkData ), &(xChunkData));

			if (pxLightData->m_xLightSource.HasPhotonMap())
			{
				const Tools_PhotonMap_DirectEstimation& xMap = pxLightData->m_xDirectEstimationPhotonMap;
				xMap.SaveToChunkStream(xStream);
			}

		}
	}
	
	xStream.WriteChunk_End();

    return true;

}
//////////////////////////////////////////////////////////////////////////////////////////////////
bool Tools_PhotonMap_DirectEstimation::ProcessFromChunk(Asura_Handle hFile,u_int uNumberOfSavedModules,u_int uPhotonMapVersion)
{
	bool bValid = true;

	if(uPhotonMapVersion == 0)
	{
		m_uNumberOfModules = uNumberOfSavedModules;
		m_pxModules = new Tools_PhotonMap_DirectEstimation_Module[m_uNumberOfModules];

		//read in the number of samples for each
		Tools_PhotonMap_DirectEstimation_Module* pxModule = m_pxModules;
		for(u_int u=0;u<m_uNumberOfModules;u++,pxModule++)
		{
			u_int uNumberOfSamples = 0;
			ASURA_CHUNK_FILEEXTRACT(hFile,uNumberOfSamples,u_int);
			pxModule->m_uNumberOfSamples = uNumberOfSamples;
			Asura_FileAccess::SkipData(hFile,4);//skip m_pxSamples
		}

		//read in the per-module sample data
		pxModule = m_pxModules;
		for(u_int u=0;u<m_uNumberOfModules;u++,pxModule++)
		{
			if (!pxModule->m_uNumberOfSamples) continue;
			
			pxModule->m_pxSamples = new Tools_PhotonMap_DirectEstimation_Sample[pxModule->m_uNumberOfSamples];
			Asura_FileAccess::ReadData
			(
				hFile,
				pxModule->m_pxSamples,
				pxModule->m_uNumberOfSamples * sizeof ( Tools_PhotonMap_DirectEstimation_Sample )
			);
			
		}
		//count up the number of non-zero samples , and determine indeces for them
		pxModule = m_pxModules;
		for(u_int u=0;u<m_uNumberOfModules;u++,pxModule++)
		{
			if(!pxModule->m_uNumberOfSamples) continue;
			
			pxModule->m_puSampleIndices = new u_int[pxModule->m_uNumberOfSamples];
			memset(pxModule->m_puSampleIndices,0xffffffff,sizeof(u_int)*pxModule->m_uNumberOfSamples);

			for(u_int v=0;v<pxModule->m_uNumberOfSamples;v++)
			{
				if(!pxModule->m_pxSamples[v].m_xIrradiance.IsZero())
				{
					pxModule->m_puSampleIndices[v] = m_uNumberOfSamples++;
				}
			}
			
		}

		//and finally copy the samples into the photon map
		TransferSamplesFromModules();
	}
	else if (uPhotonMapVersion == 1)
	{
		//allocate storage for the modules based on the current number of modules
		m_uNumberOfModules = Asura_Module_System::GetNumberOfModules();
		m_pxModules = new Tools_PhotonMap_DirectEstimation_Module[m_uNumberOfModules];

		if(uNumberOfSavedModules != m_uNumberOfModules) bValid = false;

		//also need temporary storage for the number of samples used by each of the saved modules
		u_int* puNumModuleSamples = new u_int[uNumberOfSavedModules];

		//get total number of samples
		ASURA_CHUNK_FILEEXTRACT(hFile,m_uNumberOfSamples,u_int);
		if(m_uNumberOfSamples)
		{
			m_pxSamples = new Tools_PhotonMap_DirectEstimation_Sample[m_uNumberOfSamples];
	 	

			//and read them in
			Asura_FileAccess::ReadData
			(
				hFile,
				m_pxSamples,
				m_uNumberOfSamples * sizeof ( Tools_PhotonMap_DirectEstimation_Sample )
			);
		}		


		//read in the module data		
#if !defined( _WIN64)
		ASURA_STATIC_ASSERT( (sizeof(Tools_PhotonMap_DirectEstimation_Module) == 0xC));
#else
		ASURA_STATIC_ASSERT( (sizeof(Tools_PhotonMap_DirectEstimation_Module) == 0x18));
#endif
		for( u_int uModuleIndex = 0; uModuleIndex < uNumberOfSavedModules; uModuleIndex += 1)
		{
			//read the number of samples into the temporary store
			Asura_FileAccess::ReadData( hFile, &puNumModuleSamples[uModuleIndex], sizeof( u_int));
			//skip the pointers
			uint32 uDummyPointer;
			Asura_FileAccess::ReadData( hFile, &uDummyPointer, sizeof( uDummyPointer));
			Asura_FileAccess::ReadData( hFile, &uDummyPointer, sizeof( uDummyPointer));
			
			
			//copy the number of samples into the module data , if for a valid module
			int iDestModuleIndex = Asura_ModuleIndex_Conversion::ConvertIndex(uModuleIndex);
			
			if(iDestModuleIndex >= 0)
			{
				m_pxModules[ iDestModuleIndex].m_uNumberOfSamples = puNumModuleSamples[uModuleIndex];
			}
			else
			{
				//One of the saved modules no longer exists , so the lighting data will
				//be at least partially invalid
				bValid = false;
			}
		}

		//and the sample indeces
		for(u_int u=0;u<uNumberOfSavedModules;u++)
		{
			if(!puNumModuleSamples[u]) continue;

			int iDestModuleIndex = Asura_ModuleIndex_Conversion::ConvertIndex(u);
			if(iDestModuleIndex >= 0)
			{
				Tools_PhotonMap_DirectEstimation_Module* pxModule = &m_pxModules[iDestModuleIndex];
				pxModule->m_puSampleIndices = new u_int[pxModule->m_uNumberOfSamples];
				Asura_FileAccess::ReadData
				(
					hFile,
					pxModule->m_puSampleIndices,
					pxModule->m_uNumberOfSamples * sizeof ( u_int )
				);
			}
			else
			{
				//skip the sample indeces for the missing module
				Asura_FileAccess::SkipData( hFile, puNumModuleSamples[u] * sizeof ( u_int ));
			}
		}
		delete [] puNumModuleSamples;

	}
	else
	{
		Asura_Assert(0,"Unexpected photon map version");
		bValid = false;
	}

	return bValid;
}

void Tools_PhotonMap_DirectEstimation::SkipFromChunkBuffer(const char*& pcChunkBuffer,u_int uNumberOfModules,u_int uPhotonMapVersion)
{
	if(uPhotonMapVersion == 0)
	{
		//add up the number of samples in each module
		u_int uSamples = 0;
		for(u_int uModule=0;uModule<uNumberOfModules;uModule++)
		{
			u_int uSamplesForModule;
			ASURA_CHUNK_EXTRACT(pcChunkBuffer,uSamplesForModule,u_int);
			uSamples += uSamplesForModule;

			pcChunkBuffer += 4;//skip m_pxSamples
		}
		

		//skip past the samples
		pcChunkBuffer += uSamples * sizeof ( Tools_PhotonMap_DirectEstimation_Sample );
	}
	else if(uPhotonMapVersion == 1)
	{
		u_int uTotalSamples;
		ASURA_CHUNK_EXTRACT(pcChunkBuffer,uTotalSamples,u_int);

		//skip past the samples
		pcChunkBuffer += uTotalSamples * sizeof ( Tools_PhotonMap_DirectEstimation_Sample );

		u_int uSampleIndices = 0;
		for(u_int uModule=0;uModule<uNumberOfModules;uModule++)
		{
			u_int uSamplesForModule;
			ASURA_CHUNK_EXTRACT(pcChunkBuffer,uSamplesForModule,u_int);
			uSampleIndices += uSamplesForModule;

			pcChunkBuffer += 8;//skip past rest of module data
		}

		//skip past the sample indeces
		pcChunkBuffer += uSampleIndices * sizeof ( u_int );
			
	
	}
	else
	{
		Asura_Assert(0,"Unexpected photon map version");
	}
}


void Tools_PhotonMap_DirectEstimation::SaveToChunkStream(Asura_Chunk_Stream& xStream) const
{
	xStream << m_uNumberOfSamples;

	//samples
	xStream.Write(m_uNumberOfSamples * sizeof ( Tools_PhotonMap_DirectEstimation_Sample), m_pxSamples);

	//modules
	//Asura_Mem::Copy(pcChunkBuffer,m_pxModules,m_uNumberOfModules * sizeof ( Tools_PhotonMap_DirectEstimation_Module ));
#if !defined( _WIN64)
	ASURA_STATIC_ASSERT( (sizeof(Tools_PhotonMap_DirectEstimation_Module) == 0xC));
#else
	ASURA_STATIC_ASSERT( (sizeof(Tools_PhotonMap_DirectEstimation_Module) == 0x18));
#endif
	for( u_int uModuleIndex = 0; uModuleIndex < this->m_uNumberOfModules; uModuleIndex += 1)
	{
		Tools_PhotonMap_DirectEstimation_Module& xModule = this->m_pxModules[ uModuleIndex];
		xStream << xModule.m_uNumberOfSamples;
	
		uint32 uDummyPointer32 = NULL;
		xStream << uDummyPointer32;
		xStream << uDummyPointer32;
	}
			
	//sample indeces	
	for (u_int u=0; u<m_uNumberOfModules; u++)
	{
		if (m_pxModules[u].m_uNumberOfSamples)
		{
			xStream.Write(m_pxModules[u].m_uNumberOfSamples * sizeof ( u_int ), m_pxModules[u].m_puSampleIndices);
		}
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////

bool Asura_Chunk_GlobalIllumination::SaveFile( const char* szFilename, bool bMissionFile )
{
	Asura_Chunk_Stream_WindowsFile xStream;
	if(!xStream.CreateFile(szFilename, true))
	{
		return false;
	}

    //Don't include the lights in the GI file while saving for level export (since they aren't needed).
	//This is to prevent the computer ceasing up while exporting levels with large
	//amounts of light data. RAR 06/12/2004
    if(!Tools_Level_Export::IsExport())
	{
    	Asura_Chunk_ModuleNames::Create(xStream);
    	
    	if( !Create(xStream, false, bMissionFile) )
		{
			return false;
		}

		if(!bMissionFile)
		{
    		Asura_Chunk_PolygonLights::Create(xStream, false);
		}
	}

	
	if(Tools_Level_Export::IsExport())
	{
		//just save current light set
		Asura_Chunk_GI_Solution::Create( xStream );
	}
	else
	{
		//create a chunk for each used light set combination
		u_int uInitialLightSets = Tools_LightSets::GetCurrentLightSets();
		for(Tools_UsedLightSetIterator xIt; !xIt.Done(); xIt.Next())
		{
			Tools_LightSets::SetCurrentLightSets(xIt.GetCurrent(),false);
			Asura_Chunk_GI_Solution::Create( xStream );
		}
		Tools_LightSets::SetCurrentLightSets(uInitialLightSets,false);
	}
	
	if(xStream.CloseFile())
	{
		#ifdef ASURA_WRITE_ZLIB
		Tools_Zlib::CompressFile(szFilename);
		#endif
		return true;
	}
	else
	{
		return false;
	}

}

#endif // #ifdef ASURA_TOOLS
