//
// Asura_Chunk_Env.cpp
//
// Created by Kevin Lea - 12/06/2001 11:17:51
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#include "ChunkSystem/Asura_Chunk_Env.h"

#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "ChunkSystem/Asura_SaveBlock.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Mesh/Asura_CollisionMesh.h"
#include "Mesh/Asura_Env.h"
#include "Mesh/Asura_Env_System.h"
#ifdef ASURA_TOOLS
#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#endif
#include "Rendering/Asura_Module.h"
#if defined(ASURA_TOOLS) && defined(ASURA_MAIN)
#include "Shapes/Asura_Shapes.h"
#include "Shapes/Asura_Platform_Shape.h"
#include "Load&Save/Tools_AsrFileManager.h"
#endif // defined(ASURA_TOOLS) && defined(ASURA_MAIN)

#ifdef ASURA_TOOLS
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#endif


// Version history:
//
//     (KJL - 15.05.2001) - 2 - Added orientation.
//
//     (RAR - 16.10.2002) - 3 - Added region index.
//
//     (MP  - 18.10.2002) - 4 - Removed orientation.
//                              Added required region field.
//
//     (RAR - 25.10.2002) - 5 - Added module flags.
//
//     (MGH - 10.02.2003) - 6 - Added Asura_CollisionMesh chunk.
//
//     (RAR - 11.12.2007) - 7 - Added light sets bitfield.
//
#define ASURA_CHUNK_ENVIRONMENT_MODULELIST_VERSION (7)

/////////////
// Process //
/////////////
bool Asura_Chunk_Environment_ModuleList::Process(Asura_Handle fileHandle)
{
	Asura_Assert(Version<=ASURA_CHUNK_ENVIRONMENT_MODULELIST_VERSION,"Unknown chunk version");

#ifndef ASURA_TOOLS
    // (MP - 14.10.2004) No longer supporting versions < 6 outside tools.
    Asura_Assert( Version >= 6, "" );
#endif
  
// KJFL - 27 April 2006 - fixes compile error in retail builds. Will have a warning on some builds, but hey we can compile. 

//#if defined(_DEBUG) || (defined(ASURA_TOOLS) && defined(ASURA_MAIN))
	const Asura_Char* envname = Asura_FileAccess::ReadString(fileHandle);
//#endif	// defined(_DEBUG) || (defined(ASURA_TOOLS) && defined(ASURA_MAIN))

#if defined(ASURA_TOOLS) && defined(ASURA_MAIN)
// RMS - In order to be as backwards compatible as possible...some people may have some old "shape" files
// that are really environments and therefore contain module data. For that reason, I need to be able
// to force the code to ignore this data sometimes...
	if( false == Asura_Platform_Shape_RenderData::IsShapeLoading() )
	{
#endif // defined(ASURA_TOOLS) && defined(ASURA_MAIN)

		Asura_Environment*		pxEnvironment	= 0;
		Asura_Module_Handler&	xModuleHandler	= Asura_Module_System::GetCurrentHandler();

		#ifndef ASURA_USE_STREAMING
		//Added check for loading a second environment chunk in non-streaming builds , to check for 
		//skin files that have been exported as the wrong type.
		//RAR 30/08/2007
		if(Asura_Environment_System::GetNumEnvironments())
		{
			Asura_Assertf( false, ("Attempting to load an environment chunk when an envioronment already exists.\nOnly one environment is expected in non-streaming builds.\nThis may happen if a skin file has been exported as an environment by mistake.\nFile being loaded : %s",Asura_FileAccess::GetFilename(fileHandle)));
			return false;
		}
		#endif
		
		#if defined(ASURA_TOOLS) && defined (ASURA_MAIN)
		const bool bUsingMultipleAsrFiles = Tools_AsrFileManager::IsUsingMultipleAsrFiles();
		//If the environment is being loaded from multiple files , then the environment and modules
		//don't get setup properly until later when we know how many modules are going to be needde
		//RAR 18/10/2007
		if(bUsingMultipleAsrFiles)
		{
			Tools_AsrFileManager::AllocateModuleStorage(m_iNumberOfModules);
		}
		else
		#endif
		{

#if defined(ASURA_USE_STREAMING)
			Asura_Environment&		xEnvironment	= Asura_Environment_System::GetCurrentEnvironment();
			pxEnvironment = &xEnvironment;
#else
		
		
			pxEnvironment = Asura_Environment_System::CreateEnvironment(envname);
#endif	// defined(ASURA_USE_STREAMING)

	        if( !pxEnvironment )
	        {
#if _DEBUG || defined(ASURA_TOOLS)
	            Asura_Assertf( false, ("Failed to get environment \"%s\" when loading file \"%s\".", envname, Asura_FileAccess::GetFilename(fileHandle)));
#endif
	            return false;
    	    }

			// Set the current environment...
			Asura_Environment_System::SetCurrentEnvironment(pxEnvironment);

			pxEnvironment->CreateModules(m_iNumberOfModules);
	        xModuleHandler.AllocateModuleStorage( *pxEnvironment );

		}

		

		Asura_Environment_Module* pxDestEnvModule = 0;
		#if defined(ASURA_TOOLS) && defined (ASURA_MAIN)
		if(!bUsingMultipleAsrFiles)
		#endif
		{
			pxDestEnvModule = pxEnvironment->Modules;
		}

		for( int iModule = 0; iModule < m_iNumberOfModules; ++iModule )
		{
			#ifdef ASURA_TOOLS
			const Asura_Char* szMeshName = 
			#endif
			Asura_FileAccess::ReadString( fileHandle );

			Asura_Module* pxDestModule = 0;
			#if defined(ASURA_TOOLS) && defined (ASURA_MAIN)
			if(bUsingMultipleAsrFiles)
			{
				//load the module data into some temporary storage , instead of directly to the module system
				Tools_TempLoadModule* pxTempLoadModule = Tools_AsrFileManager::GetNextModule();
				pxDestModule = pxTempLoadModule->m_pxModule;
				pxDestEnvModule = pxTempLoadModule->m_pxEnvModule;
			}
			else
			#endif
			{
				pxDestModule = &xModuleHandler.m_pxModuleStorage[iModule];
			}

#ifdef ASURA_TOOLS
			pxDestModule->m_pxDefaultMesh = Asura_Mesh_System::GetMesh( szMeshName );
			pxDestModule->Mesh = pxDestModule->m_pxDefaultMesh;
#endif

			Asura_FileAccess::ReadData( fileHandle, &pxDestModule->Position, sizeof(Asura_Vector_3) );

#ifdef ASURA_TOOLS
            // Deal with orientation if this is an old version.
            if( Version < 4 )
            {
				Asura_Mesh* pxMesh = pxDestModule->Mesh;
				Asura_Assert(pxMesh,"");

				Asura_Matrix_3x3 xOrient; 
			    Asura_FileAccess::ReadData( fileHandle,&xOrient ,sizeof(Asura_Matrix_3x3) );
				//apply orientation to mesh
				for(int j=0;j<pxMesh->NumberOfVertices;j++)
				{
					pxMesh->VertexDataPtr[j].Position = pxMesh->VertexDataPtr[j].Position*xOrient;
				}

				if(pxMesh->AreNormalsValid())
				{
					for(int j=0;j<pxMesh->NumberOfPolygons;j++)
					{
						pxMesh->NormalsDataPtr[j].FaceNormal = pxMesh->NormalsDataPtr[j].FaceNormal*xOrient;
						for(int k=0;k<pxMesh->PolygonDataPtr[j].NumberOfVertices();k++)
						{
							pxMesh->NormalsDataPtr[j].VertexNormal[k] = pxMesh->NormalsDataPtr[j].VertexNormal[k]*xOrient;
						}
					}
				}
            }
#endif

#ifdef ASURA_TOOLS
            if( Version < 3 )
            {
				pxDestEnvModule->m_uRegion = 0;
			}
			else
#endif
			{
				Asura_FileAccess::ReadData( fileHandle,&pxDestEnvModule->m_uRegion,sizeof(unsigned));
			}

#ifdef ASURA_TOOLS
            if( Version < 4 )
            {
                pxDestEnvModule->m_uReqdRegions = (1 << pxDestEnvModule->m_uRegion);
            }
            else
#endif
            {
                Asura_FileAccess::ReadData( fileHandle, &pxDestEnvModule->m_uReqdRegions, sizeof(u_int) );
                Asura_Assert( pxDestEnvModule->m_uReqdRegions & (1u << pxDestEnvModule->m_uRegion), "" );
            }

#ifdef ASURA_TOOLS
            if( Version >= 5 )
#endif
            {
                Asura_FileAccess::ReadData( fileHandle, &pxDestEnvModule->m_uModuleFlags, sizeof(u_int) );
            }

			if(Version >= 7)
			{
#ifdef ASURA_TOOLS
                Asura_FileAccess::ReadData( fileHandle, &pxDestEnvModule->m_uLightSets, sizeof(u_int) );
#else
				Asura_FileAccess::SkipData( fileHandle, sizeof(u_int));
#endif
			}

			// Read in the collision mesh
#ifdef ASURA_TOOLS
			if ( Version >= 6 )
#endif
			{
				int iChunkSize = 0;
				Asura_FileAccess::ReadData( fileHandle, &iChunkSize, sizeof(int));
				if (iChunkSize > 0)
				{
					// OK, we have some collision mesh chunk data. Verify we
                    // haven't already encoutnered some for this module.
                    Asura_Assertf( !pxDestModule->m_pxCollisionMesh, ("Duplicate collision mesh for module %i.", iModule) );

					Asura_ResourcePushType(ASURA_RESOURCE_TEMPORARY);
					{
						char * acChunkBuffer;
						Asura_ResourceAllocateMemoryBlock(acChunkBuffer, iChunkSize, ASURA_ALIGN_4_BYTE);
						Asura_FileAccess::ReadData( fileHandle, acChunkBuffer, iChunkSize);

						const char * pcChunkBuffer = acChunkBuffer;
						pxDestModule->m_pxCollisionMesh = new Asura_CollisionMesh(pcChunkBuffer);
						Asura_Assert(int(pcChunkBuffer - acChunkBuffer) == iChunkSize, "Incorrect chunk size");

						Asura_ResourceDeallocateMemoryBlock(acChunkBuffer);
					}
					Asura_ResourcePopType();

#ifdef ASURA_TOOLS
					if(!pxDestModule->m_pxCollisionMesh->IsValid() || !pxDestModule->m_pxCollisionMesh->GetNumberOfPolys())
					{
						//Failed to load collision mesh correctly , so delete it, and
						//allow it to be calculated below. RAR 23/03/2004

						//There may be no polygons in the saved collision mesh if it has been saved out with the physics system turned on,
						//if that is the case , then we need to regenerate it for tools use. RAR 09/06/2006
												
						delete pxDestModule->m_pxCollisionMesh;
						pxDestModule->m_pxCollisionMesh = 0;

					}
#endif
				}
			}

#ifdef ASURA_TOOLS
			if( pxDestModule->Mesh && !pxDestModule->m_pxCollisionMesh)
			{	
				// Generate the Asura_CollisionMesh from the Asura_Mesh, on the fly.
				// Generally this only happens when loading old ASR or old level exports.
				// May wish to disable this for non-tools builds eventually.
				if( !pxDestModule->Mesh->BoundingBoxDataValid )
				{
					pxDestModule->Mesh->CalculateBoundingBox();
				}
				pxDestModule->m_pxCollisionMesh = new Asura_CollisionMesh( pxDestModule->Mesh );
			}
			Asura_Assert( pxDestModule->m_pxCollisionMesh, "No collision mesh for module." );
#else
			Asura_Assert( pxDestModule->m_pxCollisionMesh, "No collision mesh for module. Re-export the level." );
#endif

			#if defined(ASURA_TOOLS) && defined (ASURA_MAIN)
			if(!bUsingMultipleAsrFiles)
			#endif
			{
				pxDestEnvModule++;
			}

			Asura_ChunkLoading_System::YieldBackgroundProcessing();
		}

#if defined(ASURA_TOOLS) && defined(ASURA_MAIN)
	}
	else
	{
        Asura_Assertf( false, ("Shape %s contains pxEnvironment data. You need to re-export it from Max.", envname) );
		return false;

	}
#endif // defined(ASURA_TOOLS) && defined(ASURA_MAIN)

	return true;
}

////////////
// Create //
////////////
#ifdef ASURA_TOOLS
void Asura_Chunk_Environment_ModuleList::Create( const Asura_Environment* pxEnvironment, Asura_SaveBlock* saveBlock,const int* piMaterialIDToIndexConversion,const char* szMeshStrippedFilename)
{
	Asura_Chunk_SaveBlock_Stream xStream(saveBlock);
	Create (pxEnvironment, xStream, piMaterialIDToIndexConversion, szMeshStrippedFilename);
}


void Asura_Chunk_Environment_ModuleList::Create( const Asura_Environment* pxEnvironment, Asura_Chunk_Stream& xStream,const int* piMaterialIDToIndexConversion,const char* szMeshStrippedFilename)
{

	Asura_CollisionMesh** apxCMesh = new Asura_CollisionMesh* [pxEnvironment->m_iNumberOfModules+1];

	int iNumModulesToSave = 0;

	for (int i=0; i<pxEnvironment->m_iNumberOfModules; i++)
	{
        apxCMesh[i] = 0;
		
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule( i );

		//Check for modules that we aren't going to save
		if(szMeshStrippedFilename && stricmp(szMeshStrippedFilename,pxMesh->m_xStrippedFilename.GetString()))
		{
			continue;
		}
		iNumModulesToSave++;
		
		// Generate the Asura_CollisionMesh from the Asura_Mesh.
		if( !pxMesh->BoundingBoxDataValid )
		{
			pxMesh->CalculateBoundingBox();
		}

		apxCMesh[i] = new Asura_CollisionMesh (Asura_Module_System::GetCollisionMeshForModule(i));
		
		Asura_Assert(apxCMesh[i], "Warning: NULL collision mesh");		
		if (apxCMesh[i])
		{
			apxCMesh[i]->DestroyPolygons();
		}
	}

	u_int uVersionToSave = ASURA_CHUNK_ENVIRONMENT_MODULELIST_VERSION;
	//If no modules are using light sets , then save as the previous version
	//(Saves issues with plugins/tools being updated at different times)
	if(uVersionToSave == 7)
	{
		bool bUsedLightSets = false;
		for (int i=0; i<pxEnvironment->m_iNumberOfModules; i++)
		{
			if(pxEnvironment->GetLightSets(i) != 0xffffffff)
			{
				bUsedLightSets = true;
			}
		}
		if(!bUsedLightSets) uVersionToSave = 6;
	}
	
	u_int uChunkFlags = 0;
	xStream.WriteChunk_Start(ASURA_CHUNK_ENVIRONMENT_MODULELIST, uVersionToSave, uChunkFlags);

	xStream << iNumModulesToSave;

	
	xStream << pxEnvironment->Name;
	Asura_Environment_Module* pxSrc = pxEnvironment->Modules;

	for( int i=0; i<pxEnvironment->m_iNumberOfModules; i++,pxSrc++ )
	{
        Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule( i );

		//Check for modules that we aren't going to save
		if(szMeshStrippedFilename && stricmp(szMeshStrippedFilename,pxMesh->m_xStrippedFilename.GetString()))
		{
			continue;
		}
        
        
        const Asura_Module* pxModule = Asura_Module_System::GetModuleData( i );

		// add name
		xStream << pxMesh->Name;

        // add position
		xStream << pxModule->Position - Tools_Level_Export::GetExportCentre();

		xStream << pxSrc->m_uRegion;

        xStream << pxSrc->m_uReqdRegions;

        
        u_int uModuleFlags = pxSrc->m_uModuleFlags;
#if defined( ASURA_MAIN )
		if(!Tools_Level_Export::ShouldIncludeModuleInExport(i))
		{
			//Flag non-exported modules as scenery so that they won't intefere with module linking
			//RAR 25/02/2008
			uModuleFlags |= ASURA_MODULE_FLAG_SCENERY;
		}
#endif // defined( ASURA_MAIN )
        
        xStream << uModuleFlags;

		if(uVersionToSave >= 7)
		{
			xStream << pxSrc->m_uLightSets;
		}


		// The collision mesh chunk
		if(apxCMesh[i])
		{
			apxCMesh[i]->WriteToChunkStream(xStream, true, piMaterialIDToIndexConversion);
		}
		else
		{
			xStream << (u_int) 0;
		}
		
	}
	
	for (int i=0; i<pxEnvironment->m_iNumberOfModules; i++)
	{
		delete(apxCMesh[i]);
	}
	delete[] apxCMesh;

	xStream.WriteChunk_End();


}

#endif // #ifdef ASURA_TOOLS




