//
// Asura_Chunk_ResourceFile_PC_SmoothSkin.cpp
//
// Created by RJF - 08 August 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Asura_Chunk_ResourceFile_PC_SmoothSkin.h"

#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_Material_System.h"

#include "Rendering/Asura_Direct3D_SmoothSkinRenderer.h"

#ifdef ASURA_TOOLS
#include "Debugging/Asura_Logfile.h"
#include "Mesh/Asura_Mesh_System.h"
#endif

bool Asura_Chunk_ResourceFile_PC_SmoothSkin::Process( Asura_Handle hFile )
{
	Asura_Chunk_File_Stream xStream(hFile);
	bool bWarnIfMeshExists = true;
	bool bConvertMaterialIDs = true;
	return ProcessFromChunkStream( xStream, bWarnIfMeshExists, bConvertMaterialIDs ) != NULL;
}

Asura_Direct3D_SmoothSkin* Asura_Chunk_ResourceFile_PC_SmoothSkin::ProcessFromChunkStream(Asura_Chunk_Stream& xStream, bool bWarnIfMeshExists, bool bConvertMaterialIDs ) const
{
	Asura_String xTempString;
	xStream >> xTempString;

	const Asura_Char* szName = xTempString.GetString();

#ifdef ASURA_TOOLS
	//Warn if a mesh already exists with the same name RAR 23/06/2005
	if (bWarnIfMeshExists)
	{
		Asura_Mesh* pxMesh = Asura_Mesh_System::GetMesh(szName);
		if(pxMesh)
		{
#if ASURA_LOG_FILE_CONTENTS
			Asura_SimpleLogfileOutput((ASURA_FILE_CONTENTS_LOGFILE,"Stripified Skin : %s - Mesh with same name has been loaded alrady!\n",szName));
#endif
			Asura_Assertf(0,("Found a mesh named '%s' while loading a stripified skin with the same name.\nIf the mesh is used by the environment or a shape file , then it will need to be renamed.\nCheck Logfiles/file_contents.txt to find which file the mesh came from.",szName));
		}
	}
#endif
	if(Version >= 2)
	{
		const bool bMaterialResponse = (Flags & uASURA_CHUNK_PC_SMOOTHSKIN_FLAG_MATERIALRESPONSE) != 0;
		#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
		if(!bMaterialResponse)
		{
			Asura_Assert(0,"Attempting to load a non material response skin into a material response build.\nThis will be skipped");
			return 0;
		}
		#else
		if(bMaterialResponse)
		{
			Asura_Assert(0,"Attempting to load a material response skin into a non material response build.\nThis will be skipped");
			return 0;
		}
		#endif

	}


	u_int uNumStrips;

	xStream.Read(sizeof(u_int), &uNumStrips);

	Asura_Direct3D_SmoothSkin* pxSkin = Asura_Direct3D_SmoothSkinRenderer::CreateSmoothSkin(Asura_GetHashID(szName), uNumStrips);

	if( !pxSkin )
	{
#if !defined(ASURA_DONT_ASSERT_ON_DUPLICATE_RESOURCE_DATA)
		Asura_Assertf( false, ("Smooth skin '%s' has already been loaded.", xTempString.GetString()) );
#endif	//!defined(ASURA_DONT_ASSERT_ON_DUPLICATE_RESOURCE_DATA)
		return NULL;
	}

	if(Version>=4)
	{
		u_int uNumBoneMaps;
		xStream.Read(sizeof(u_int), &uNumBoneMaps);
		pxSkin->SetNumBoneMaps(uNumBoneMaps);
	} else {
		pxSkin->SetNumBoneMaps(0);
	}

	u_int uNumIndices = 0;
	for(u_int uStripIndex = 0; uStripIndex < uNumStrips; ++uStripIndex)
	{
		Asura_Direct3D_SmoothSkin::Strip& xStrip(pxSkin->GetStrip(uStripIndex));

		#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
			xStream.Read( sizeof( Asura_Hash_ID ), &xStrip.m_auMaterialResponseHashID[0] );
			if(Version>=2)
			{
				xStream.Read( sizeof( Asura_Hash_ID ), &xStrip.m_auMaterialResponseHashID[1] );
			}
			else
			{
				xStrip.m_auMaterialResponseHashID[1] = 0;
			}
		#else
			xStream.Read(sizeof(int),   &xStrip.iMaterialID);
		#endif

		xStream.Read(sizeof(u_int), &xStrip.uNumIndices);	
		
		if (Version>=1)
		{
			xStream.Read(sizeof(int),   &xStrip.iElementID);
		}
		else
		{
			xStrip.iElementID = 0;
		}
		if (pxSkin->GetNumBoneMaps() > 0)
		{
			xStream.Read(sizeof(u_int),   &xStrip.uBoneMap);
		}

		#if !defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
			if(bConvertMaterialIDs)
			{
				xStrip.iMaterialID = Asura_Material_System::ConvertIndex(xStrip.iMaterialID);
			}
		#endif

		uNumIndices += xStrip.uNumIndices;
	}

	u_int uNumVertices;
	xStream.Read(sizeof(u_int), &uNumVertices);

	Direct3D_VertexBuffer* pxVertices;
	Asura_Direct3D::D3DDevice->CreateVertexBuffer(uNumVertices * sizeof(Asura_Direct3D_SmoothSkin::Vertex),
												  D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &pxVertices, 0);

	if( !pxVertices )
	{
		Asura_Assert( false, "Failed to create smooth skin vertex buffer." );
		return NULL;
	}

	Asura_Direct3D_SmoothSkin::Vertex* pxVertData;
	if (SUCCEEDED(pxVertices->Lock(0, 0, (void**)&pxVertData, 0)))
	{
		xStream.Read(uNumVertices * sizeof( Asura_Direct3D_SmoothSkin::Vertex), pxVertData);
		pxVertices->Unlock();
	} else {
		Asura_Assert(0, "failed to lock vertex buffer");
		xStream.Skip(uNumVertices * sizeof(Asura_Direct3D_SmoothSkin::Vertex));
	}

	Direct3D_IndexBuffer* pxIndices;
	Asura_Direct3D::D3DDevice->CreateIndexBuffer(uNumIndices * sizeof(unsigned short),
		D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &pxIndices, 0);

	if( !pxIndices )
	{
		Asura_Assert( false, "Failed to create smooth skin index buffer." );
		pxVertices->Release();

		return NULL;
	}

	unsigned short* pxIndexData;
	if (SUCCEEDED(pxIndices->Lock(0, 0, (void**)&pxIndexData, 0)))
	{
		xStream.Read(uNumIndices * sizeof(unsigned short), pxIndexData);
		pxIndices->Unlock();
	} else {
		Asura_Assert(0, "failed to lock index buffer");
		xStream.Skip(uNumIndices * sizeof(unsigned short));
	}

	for(u_int uBoneIndex = 0; uBoneIndex < pxSkin->GetNumBoneMaps(); ++uBoneIndex)
	{
		Asura_Direct3D_SmoothSkin::BoneMap &xBoneMap = pxSkin->GetBoneMap(uBoneIndex);
		xStream.Read(sizeof(u_int), &xBoneMap.m_uSize);
		xBoneMap.m_puEntries = new u_int[xBoneMap.m_uSize];
		xStream.Read(sizeof(u_int) * xBoneMap.m_uSize, xBoneMap.m_puEntries);
	}

	D3DPRIMITIVETYPE ePrimitiveType = (Flags & uASURA_CHUNK_PC_SMOOTHSKIN_FLAG_TRILIST) ? D3DPT_TRIANGLELIST : D3DPT_TRIANGLESTRIP;
	pxSkin->SetGeometry(uNumVertices, pxVertices, pxIndices, ePrimitiveType);

	return pxSkin;
}