//
// Tools_Overlay_ImportMaterialResponse.cpp
//
// Created by Richard Rice - 18/09/2007
// 
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Materials/Tools_Overlay_ImportMaterialResponse.h"

#ifdef ASURA_USE_MATERIAL_RESPONSE_SYSTEM

#include "Core/Asura_HashTree.h"
#include "Core/Asura_TextureManagement.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Gadget.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Mesh/Asura_Mesh.h"
#include "Rendering/Asura_Module.h"

#include "Tools_Overlays.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Materials/Tools_Overlay_ViewMaterialResponse.h"
#include "Materials/Tools_Overlay_PickMaterialResponse.h"
#include "Materials/Tools_MaterialResponse_System.h"
#include "Texturing/Tools_Texturing_MaterialResponse.h"
#include "Texturing/Tools_Mode_Texturing.h"
#include "Texturing/Tools_Texturing_Find.h"


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Tools_PolygonFind_ImportMaterial : public Tools_PolygonFind
{
public :
	Tools_PolygonFind_ImportMaterial(Tools_MaterialResponse_ImportData* pxImportData)
	{
		m_pxImportData = pxImportData;
		m_pxImportData->UpdatePolygonLists();
	}

	virtual bool IncludePolygon(int iModule,int iPolyIndex)
	{
		return m_pxImportData->m_pxModules[iModule].m_xPolygonList.contains(iPolyIndex);
	}

private :
	Tools_MaterialResponse_ImportData* m_pxImportData;	

};


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

Tools_Overlay_ImportMaterialResponse::Tools_Overlay_ImportMaterialResponse()
:Asura_Overlay("Import Materials", false)
{
	m_iMaterialNameListSize = 0;
	m_pszMaterialNameList = 0;
	m_iSelection = 0;
	m_bSelectionChanged = false;

	m_pxAlbedoImageDialog = 0;
	m_pxNormalMapImageDialog = 0;
	m_pxSpecularMapImageDialog = 0;

	m_pxAlbedoNameDialog = 0;
	m_pxNormalMapNameDialog = 0;
	m_pxSpecularMapNameDialog = 0;
	m_pxPolygonsDialog = 0;

	m_pxMaterialToUseDialog = 0;
	
}

Tools_Overlay_ImportMaterialResponse::~Tools_Overlay_ImportMaterialResponse()
{
	while(m_xImportList.size())
	{
		delete m_xImportList.first_entry();
		m_xImportList.delete_first_entry();
	}

	for(int i=0; i<m_iMaterialNameListSize; i++)
	{
		delete [] m_pszMaterialNameList[i];
	}
	delete [] m_pszMaterialNameList;
}

void Tools_Overlay_ImportMaterialResponse::CreateOverlay()
{
	//overlay already open?
	if(Asura_Overlay_System::DoesOverlayExist(TOOLS_OVERLAY_IMPORTMATERIALRESPONSE))
	{
		Asura_Overlay_System::BringOverlayToTop(TOOLS_OVERLAY_IMPORTMATERIALRESPONSE);
		return;
	}

	//create the overlay then
	Tools_Overlay_ImportMaterialResponse* pxOverlay = new Tools_Overlay_ImportMaterialResponse();

	pxOverlay->FindMaterialsToImport();
	pxOverlay->InitialiseOverlay();

	pxOverlay->m_uID = TOOLS_OVERLAY_IMPORTMATERIALRESPONSE;
	Asura_Overlay_System::SubmitNewOverlay( pxOverlay);

	pxOverlay->UpdateDialogsForCurrentSelection();
}

void Tools_Overlay_ImportMaterialResponse::CloseOverlay()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_IMPORTMATERIALRESPONSE);	
}

bool Tools_Overlay_ImportMaterialResponse::OverlayExists()
{
	return Asura_Overlay_System::DoesOverlayExist(TOOLS_OVERLAY_IMPORTMATERIALRESPONSE);
}


void Tools_Overlay_ImportMaterialResponse::InitialiseOverlay()
{
	Asura_Vector_2 xOffset(1.0f,3.0f);
	Asura_Dialog* pxDialog;

	BuildMaterialNameList();
	m_iSelection = 0;
	m_bSelectionChanged = false;

	//start with a list of all the materials being imported
	pxDialog = new Asura_Dialog("Old Materials :",xOffset);
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();


	pxDialog = new Asura_List_Dialog(xOffset, m_pszMaterialNameList, &m_iSelection, 12, m_iMaterialNameListSize, NULL, &m_bSelectionChanged);
	pxDialog->SetHelpText("List of old materials to import");
	AddDialog(pxDialog);
	xOffset.y = pxDialog->m_xMaxCoord.y + 1.0f;

	//add thumbnails for the textures used by the selected material
	pxDialog = m_pxAlbedoImageDialog = new Asura_Image_Dialog(xOffset, ASURA_TEXTURE_NONE, 128.0f, 128.0f);
	m_pxAlbedoImageDialog->SetTranslucent(false);
	pxDialog->SetHelpText("Albedo");
	AddDialog(pxDialog);
	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;

	pxDialog = m_pxNormalMapImageDialog = new Asura_Image_Dialog(xOffset, ASURA_TEXTURE_NONE, 128.0f, 128.0f);
	m_pxNormalMapImageDialog->SetTranslucent(false);
	pxDialog->SetHelpText("Normal Map");
	AddDialog(pxDialog);
	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;

	pxDialog = m_pxSpecularMapImageDialog = new Asura_Image_Dialog(xOffset, ASURA_TEXTURE_NONE, 128.0f, 128.0f);
	m_pxSpecularMapImageDialog->SetTranslucent(false);
	pxDialog->SetHelpText("Specular");
	AddDialog(pxDialog);



	xOffset.x = 1.0f;
	xOffset.y = pxDialog->m_xMaxCoord.y + 1.0f;

	pxDialog = m_pxPolygonsDialog = new Asura_Dialog("Polygons",xOffset);
	pxDialog->SetHelpText("The number of polygons using this material");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	//labels for the names of the textures used
	pxDialog = m_pxAlbedoNameDialog = new Asura_Dialog("Albedo",xOffset);
	pxDialog->SetHelpText("The albedo texture for the old material");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = m_pxNormalMapNameDialog = new Asura_Dialog("Normal Map",xOffset);
	pxDialog->SetHelpText("The normal map texture for the old material");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
	
	pxDialog = m_pxSpecularMapNameDialog = new Asura_Dialog("Specular",xOffset);
	pxDialog->SetHelpText("The specular map texture for the old material");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();



	pxDialog = m_pxMaterialToUseDialog = new Asura_Dialog("New Material", xOffset);
	pxDialog->SetHelpText("The material response that has currently been assigned as a replacement.");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	xOffset.y += 0.5f;
	pxDialog = new Asura_Divider_Dialog(xOffset);
	AddDialog(pxDialog);
	xOffset.y += 0.5f;


	
	float fTopStoredY = xOffset.y;

	//options for use on the current selection
	pxDialog = new Asura_Function_Dialog("View Assigned Material",xOffset,ViewAssignedMaterial,(int)this);
	pxDialog->SetIsEnabledFunction(IsMaterialAssigned,(int)this);
	pxDialog->SetHelpText("Opens the property overlay for the material response currently assigned for this.");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Select Material From Env",xOffset,SelectMaterial,(int)this);
	pxDialog->SetIsEnabledFunction(IsMaterialSelected,(int)this);
	pxDialog->SetHelpText("Select an existing material response to use as a replacement.\nSelection is from thumbnails of materials already used by the environment");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Select Material From Tree",xOffset,SelectMaterialFromTree,(int)this);
	pxDialog->SetIsEnabledFunction(IsMaterialSelected,(int)this);
	pxDialog->SetHelpText("Select an existing material response to use as a replacement.\nSelection is from treeview of all materials");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();


	pxDialog = new Asura_Function_Dialog("Create Material",xOffset,CreateMaterial,(int)this);
	pxDialog->SetIsEnabledFunction(IsMaterialSelected,(int)this);
	pxDialog->SetHelpText("Create a new material response to use for this material");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Function_Dialog("Apply Material",xOffset,ApplyMaterial,(int)this);
	pxDialog->SetIsEnabledFunction(IsMaterialSelected,(int)this);
	pxDialog->SetHelpText("Apply the selected material now.");
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	float fBottomStoredY = xOffset.y;

	xOffset.x = 18.0f;
	xOffset.y = fTopStoredY;

	pxDialog = new Asura_Function_Dialog("Find Polygons",xOffset,FindPolygons,(int)this);
	pxDialog->SetIsEnabledFunction(CanFindPolygons,(int)this);
	pxDialog->SetHelpText("Finds all the polygons that will be updated by this material.\nThese will be added to the list managed by the find polygon overlay.");
	AddDialog(pxDialog);

	xOffset.x = 1.0f;
	xOffset.y = fBottomStoredY;

	xOffset.y += 0.5f;
	pxDialog = new Asura_Divider_Dialog(xOffset);
	AddDialog(pxDialog);
	xOffset.y += 0.5f;


	pxDialog = new Asura_Function_Dialog("Apply All",xOffset,ApplyAll,(int)this);
	pxDialog->SetHelpText("Apply the changes for all of the materials.");
	AddDialog(pxDialog);

	xOffset.x = 18.0f;
	pxDialog = new Asura_Function_Dialog("Force Import",xOffset,ForceImport,(int)this);
	pxDialog->SetHelpText("Forces all the materials to be reassigned from the old materials , regardless of whether material responses have been applied yet.");
	AddDialog(pxDialog);

}

void Tools_Overlay_ImportMaterialResponse::FindMaterialsToImport()
{
	//Assuming that the number of textures for each slot in the old materials is the same
	//as the number of material responses being applied to each polygon
	ASURA_STATIC_ASSERT(ASURA_MATERIAL_MAXTEXTURES==TOOLS_NUM_MATERIALRESPONSE_PER_POLY);

	//search for polygons that have an old-style material , but no material response	
	for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
	{
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(i);
		if(!pxMesh->TextureDataPtr) continue;

		for(int j=0;j<pxMesh->NumberOfPolygons;j++)
		{
			if(pxMesh->IsMergedObjectPolygon(j)) continue;

			Asura_Mesh_TextureInfo* pxTex = &pxMesh->TextureDataPtr[j];

			if(pxTex->m_iMaterialID == ASURA_MATERIAL_NONE) continue;

			const Asura_Material* pxMaterial = Asura_Material_System::GetMaterial(pxTex->m_iMaterialID);
			if(!pxMaterial) continue;

			for(int iSlot=0; iSlot<ASURA_MATERIAL_MAXTEXTURES; iSlot++)
			{
				if(pxMaterial->m_iTextureID[iSlot] == ASURA_TEXTURE_NONE) continue;
				if(pxTex->m_auMaterialResponseHashID[iSlot] != 0) continue;

				//found a material slot that needs replacing
				//find/create an entry in the import list
				AddEntryToImportList(pxTex->m_iMaterialID,iSlot);
			}
		}
	}

	//build a list of material responses that are currently in use by the environment.
	//these will be given higher priority when it comes down to determining the best material to use
	Asura_HashTree xEnvMaterialTree;
	List<Tools_MaterialResponse*> xUsedMaterialList;
	for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
	{
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(i);
		if(!pxMesh->TextureDataPtr) continue;

		for(int j=0;j<pxMesh->NumberOfPolygons;j++)
		{
			if(pxMesh->IsMergedObjectPolygon(j)) continue;
			
			Asura_Mesh_TextureInfo* pxTex = &pxMesh->TextureDataPtr[j];
				
			for(int iSlot=0; iSlot<ASURA_MATERIAL_MAXTEXTURES; iSlot++)
			{
				if(pxTex->m_auMaterialResponseHashID[iSlot])
				{
					int iJunkIndex;
					if(!xEnvMaterialTree.FindNodeIndexByHash(pxTex->m_auMaterialResponseHashID[iSlot], true, iJunkIndex))
					{
						Tools_MaterialResponse* pxMaterialResponse = Tools_MaterialResponse_System::GetMaterialByHashID(pxTex->m_auMaterialResponseHashID[iSlot]);
						if(pxMaterialResponse)
						{
							xUsedMaterialList.add_entry(pxMaterialResponse);
						}
					}
				}
			}
		}
	}


	//Get the default path for materials in this level
	const Asura_String xLoadedFile = Tools_SaveAsura::GetRealFilename();

	Asura_String xPath, xFilename;
	xLoadedFile.SplitFilename( &xPath, &xFilename, NULL );

	Asura_String xMaterialPath = Asura_FileAccess::PlatformRemoveNetworkOrExecutableDir( xPath.GetString() );
	xMaterialPath += "\\";
	xMaterialPath += xFilename;
	xMaterialPath += "\\";

	//scoring for finding how well materials match
	const u_int uScore_AlbedoMatches = (1<<0);
	const u_int uScore_NewMaterialHasNoTextureWhereOldMaterialHasNoTexture = (1<<1);
	const u_int uScore_MaterialInCorrectDirectory = (1<<4);
	const u_int uScore_MaterialDirectoryAlreadyInUse = (1<<4);
	const u_int uScore_MaterialAlreadyInUse = (1<<16);

	
	//Make a best guess as to what material to use for each of the old materials
	for(LIF<Tools_MaterialResponse_ImportData*> xImportLIF(&m_xImportList); !xImportLIF.done(); xImportLIF.next())
	{
		Tools_MaterialResponse_ImportData* pxImport = xImportLIF();

		const Asura_Material* pxMaterial = Asura_Material_System::GetMaterial(pxImport->m_iMaterialID);
		Asura_Assert(pxMaterial, "");

		//get the filenames of each of the textures used
		const Asura_String* pxString = Asura_Texture_Management::GetTextureName(pxMaterial->m_iTextureID[pxImport->m_iSlot]);
		const char* szAlbedo = pxString ? pxString->GetString() : 0;
		Asura_Assert(szAlbedo,"Expecting to have an albedo texture filename at this point");
	
		pxString = Asura_Texture_Management::GetTextureName(pxMaterial->m_iNormalMapID[pxImport->m_iSlot]);
		const char* szNormalMap = pxString ? pxString->GetString() : 0;

		pxString = Asura_Texture_Management::GetTextureName(pxMaterial->m_iSpecularMapID[pxImport->m_iSlot]);
		const char* szSpecularMap = pxString ? pxString->GetString() : 0;

		Asura_Hash_ID uBestMaterial = 0;
		u_int uBestScore = 0;

		Tools_MaterialResponse_System::EnsureAllMaterialsAreLoaded();

		//Search for the best matching material
		for( u_int u = 0; u < Tools_MaterialResponse_System::GetNumMaterials(); ++u )
		{
			Tools_MaterialResponse* pxMaterialResponse = Tools_MaterialResponse_System::GetMaterialByIndex(u);
			u_int uScore = 0;

			//the albedo texture must match
			const char* szTextureName = pxMaterialResponse->GetTextureFilename(Asura_MaterialResponse::ALBEDO_TEXTURE);
			if(!szTextureName) continue;
			if(stricmp(szTextureName, szAlbedo)) continue;

			uScore += uScore_AlbedoMatches;

			//the normal map texture should match if the old material had one
			szTextureName = pxMaterialResponse->GetTextureFilename(Asura_MaterialResponse::NORMAL_MAP);
			if(szNormalMap)
			{
				if( ( szTextureName ) && ( stricmp( szTextureName, szNormalMap ) ) )
				{
					continue;
				}
			}
			else
			{
				//allow a match against a new material that has a normal map , but prefer ones that don't
				if(!szTextureName) uScore += uScore_NewMaterialHasNoTextureWhereOldMaterialHasNoTexture;
			}

			//similarly for the specular map
			szTextureName = pxMaterialResponse->GetTextureFilename(Asura_MaterialResponse::SPECULAR_MAP);
			if(szSpecularMap) 
			{
				if( ( szTextureName ) && ( stricmp(szTextureName, szSpecularMap) ) )
				{
					continue;
				}
			}
			else
			{
				if(!szTextureName) uScore += uScore_NewMaterialHasNoTextureWhereOldMaterialHasNoTexture;
			}

			//texture anim must match
			if(pxMaterial->m_uAnimHash != pxMaterialResponse->GetTextureAnim()) continue;
			

			//prefer materials that are in the directory associated with the environment
			if(!strnicmp(pxMaterialResponse->GetName().GetString(),xMaterialPath.GetString(),xMaterialPath.GetLength()))
			{
				uScore += uScore_MaterialInCorrectDirectory;
			}

			//check for the material being in the same directory as one that is already in use
			Asura_String xPath;
			pxMaterialResponse->GetName().SplitFilename(&xPath,0,0);
			if(xPath.IsNull()) xPath = "";

			for(LIF<Tools_MaterialResponse*> xUsedLIF(&xUsedMaterialList); !xUsedLIF.done(); xUsedLIF.next())
			{
				Asura_String xPath2;
				xUsedLIF()->GetName().SplitFilename(&xPath2,0,0);
				if(xPath2.IsNull()) xPath2 = "";

				if(!stricmp(xPath.GetString(),xPath2.GetString()))
				{
					uScore += uScore_MaterialDirectoryAlreadyInUse;
				}
			}

			//prefer materials that are already in use by the environment
			int iJunkIndex;
			if(xEnvMaterialTree.FindNodeIndexByHash(pxMaterialResponse->GetHash(), false, iJunkIndex))
			{
				uScore += uScore_MaterialAlreadyInUse;
			}


			if(uScore > uBestScore)
			{
				//best match so far
				uBestScore = uScore;
				uBestMaterial = pxMaterialResponse->GetHash();
			}
		}

		//Store the best material found
		//If its still zero ,then a new material will have to be created later on.
		pxImport->m_uMaterialResponse = uBestMaterial;

	}

}

void Tools_Overlay_ImportMaterialResponse::AddEntryToImportList(int iMaterialID, int iSlot)
{
	Tools_MaterialResponse_ImportData* pxImport = 0;
	//search the list for a matching entry
	for(LIF<Tools_MaterialResponse_ImportData*> xImportLIF(&m_xImportList); !xImportLIF.done(); xImportLIF.next())
	{
		if(xImportLIF()->m_iMaterialID == iMaterialID && xImportLIF()->m_iSlot == iSlot)
		{
			pxImport = xImportLIF();
			break;
		}
	}

	if(!pxImport)
	{
		//create a new entry then
		pxImport = new Tools_MaterialResponse_ImportData;
		pxImport->m_iMaterialID = iMaterialID;
		pxImport->m_iSlot = iSlot;
		m_xImportList.add_entry(pxImport);
	}

	pxImport->m_uPolygons++;
}


void Tools_Overlay_ImportMaterialResponse::BuildMaterialNameList()
{
	char szText[200];

	//build list of strings for the list control on the overlay
	if(m_xImportList.size())
	{
		m_iMaterialNameListSize = m_xImportList.size();
		m_pszMaterialNameList = new char*[m_iMaterialNameListSize];

		for(int i=0; i<m_iMaterialNameListSize; i++)
		{
			Tools_MaterialResponse_ImportData* pxImport = m_xImportList[i];

			const Asura_Material* pxMaterial = Asura_Material_System::GetMaterial(pxImport->m_iMaterialID);
			Asura_Assert(pxMaterial, "");

			const char* szPrefix = pxImport->m_iSlot ? "Sec" : "Pri";

			if(pxMaterial->m_xMaterialName.IsBlank())
			{
				Asura_PrintfToBfr(szText,"%s: Material %d",sizeof(szText),true,szPrefix,pxImport->m_iMaterialID);
			}
			else
			{
				Asura_PrintfToBfr(szText,"%s: %s",sizeof(szText),true,szPrefix,pxMaterial->m_xMaterialName.GetString());
			}

			m_pszMaterialNameList[i] = new char[strlen(szText)+1];
			strcpy(m_pszMaterialNameList[i], szText);
			
		}
	}
	else
	{
		//nothing to import
		m_iMaterialNameListSize = 1;
		m_pszMaterialNameList = new char*[1];
		m_pszMaterialNameList[0] = new char[50];//overkill ,but I can't be bothered to count
		strcpy(m_pszMaterialNameList[0], "*Nothing to import*");

	}
}

void Tools_Overlay_ImportMaterialResponse::UpdateDialogsForCurrentSelection()
{
	if(!m_xImportList.size()) return;
	Asura_Assert(m_iSelection>=0 && m_iSelection < m_xImportList.size(),"");

	Tools_MaterialResponse_ImportData* pxImport = m_xImportList[m_iSelection];
	const Asura_Material* pxMaterial = Asura_Material_System::GetMaterial(pxImport->m_iMaterialID);

	char szText[200];

	//replace the textures used by the thumbnails

	//albedo
	int iTextureID = pxMaterial ? pxMaterial->m_iTextureID[pxImport->m_iSlot] : ASURA_TEXTURE_NONE;
	m_pxAlbedoImageDialog->SetTexture(iTextureID);

	if(iTextureID != ASURA_TEXTURE_NONE)
	{
		const Asura_String* pxTextureName = Asura_Texture_Management::GetTextureName(iTextureID);
		const char* szTextureName = pxTextureName ? pxTextureName->GetString() : "*Not Found*";
		Asura_PrintfToBfr(szText,"Albedo : %s",sizeof(szText),true,szTextureName);
		
	}
	else
	{
		Asura_PrintfToBfr(szText,"Albedo : None",sizeof(szText),true);
	}
	m_pxAlbedoNameDialog->UpdateLabel(szText);

	//normal map
	iTextureID = pxMaterial ? pxMaterial->m_iNormalMapID[pxImport->m_iSlot] : ASURA_TEXTURE_NONE;
	m_pxNormalMapImageDialog->SetTexture(iTextureID);

	if(iTextureID != ASURA_TEXTURE_NONE)
	{
		const Asura_String* pxTextureName = Asura_Texture_Management::GetTextureName(iTextureID);
		const char* szTextureName = pxTextureName ? pxTextureName->GetString() : "*Not Found*";
		Asura_PrintfToBfr(szText,"Normal Map : %s",sizeof(szText),true,szTextureName);
		
	}
	else
	{
		Asura_PrintfToBfr(szText,"Normal Map : None",sizeof(szText),true);
	}
	m_pxNormalMapNameDialog->UpdateLabel(szText);


	//specular map
	iTextureID = pxMaterial ? pxMaterial->m_iSpecularMapID[pxImport->m_iSlot] : ASURA_TEXTURE_NONE;
	m_pxSpecularMapImageDialog->SetTexture(iTextureID);

	if(iTextureID != ASURA_TEXTURE_NONE)
	{
		const Asura_String* pxTextureName = Asura_Texture_Management::GetTextureName(iTextureID);
		const char* szTextureName = pxTextureName ? pxTextureName->GetString() : "*Not Found*";
		Asura_PrintfToBfr(szText,"Specular : %s",sizeof(szText),true,szTextureName);
		
	}
	else
	{
		Asura_PrintfToBfr(szText,"Specular : None",sizeof(szText),true);
	}
	m_pxSpecularMapNameDialog->UpdateLabel(szText);


	//Set the name of the current material response
	if(pxImport->m_uMaterialResponse)
	{
		Tools_MaterialResponse* pxMaterialResponse = Tools_MaterialResponse_System::GetMaterialByHashID(pxImport->m_uMaterialResponse);
		if(pxMaterialResponse)
		{
			Asura_PrintfToBfr(szText,"New Material : %s",sizeof(szText),true,pxMaterialResponse->GetName().GetString());
		}
		else
		{
			Asura_PrintfToBfr(szText,"New Material : No longer exists??",sizeof(szText),true);
		}
	}
	else
	{
		strcpy(szText,"New Material : None assigned (will create a new material).");
	}
	m_pxMaterialToUseDialog->UpdateLabel(szText);

	//set the polygon count label
	Asura_PrintfToBfr(szText,"Polygons : %d",sizeof(szText),true,pxImport->m_uPolygons);
	m_pxPolygonsDialog->UpdateLabel(szText);


	
	ResizeOverlay();
}

void Tools_Overlay_ImportMaterialResponse::PerFrameUpdate()
{
	Asura_Overlay::PerFrameUpdate();

	if(m_bSelectionChanged)
	{
		m_bSelectionChanged = false;
		UpdateDialogsForCurrentSelection();
	}
}

void Tools_Overlay_ImportMaterialResponse::ViewAssignedMaterial()
{
	if(!m_xImportList.size()) return;
	Asura_Hash_ID uMaterialID = m_xImportList[m_iSelection]->m_uMaterialResponse;
	if(!uMaterialID) return;

	//open the material properties overlay for the assigned material
	Tools_Texturing_MaterialResponse::EditMaterial(uMaterialID);	
}

void Tools_Overlay_ImportMaterialResponse::SelectMaterial()
{
	if(!m_xImportList.size()) return;
	Tools_MaterialResponse_ImportData* pxImport = m_xImportList[m_iSelection];

	//ask for a new material to use
	Asura_Hash_ID uMaterial = Tools_Overlay_ViewMaterialResponse::PickMaterial("Select an existing material to use",pxImport->m_uMaterialResponse);

	if(!Tools_Overlay_ViewMaterialResponse::WasCancelled())
	{
		pxImport->m_uMaterialResponse = uMaterial;
		pxImport->m_bDone = false;
		UpdateDialogsForCurrentSelection();
	}
}

void Tools_Overlay_ImportMaterialResponse::SelectMaterialFromTree()
{
	if(!m_xImportList.size()) return;
	Tools_MaterialResponse_ImportData* pxImport = m_xImportList[m_iSelection];

	//ask for a new material to use
	Tools_Overlay_PickMaterialResponse xOverlay("Select an existing material to use");
	if(xOverlay.DoModal(pxImport->m_uMaterialResponse))
	{
		if(xOverlay.GetSelection())
		{
			pxImport->m_uMaterialResponse = xOverlay.GetSelection();
			pxImport->m_bDone = false;
			UpdateDialogsForCurrentSelection();
		}
	}
}


void Tools_Overlay_ImportMaterialResponse::CreateMaterial()
{
	if(!m_xImportList.size()) return;
	Tools_MaterialResponse_ImportData* pxImport = m_xImportList[m_iSelection];

	Tools_MaterialResponse* pxMaterial = Tools_Texturing_MaterialResponse::CreateMaterialResponseFromOldMaterial(pxImport->m_iMaterialID, pxImport->m_iSlot,false);
	if(pxMaterial)
	{
		pxImport->m_uMaterialResponse = pxMaterial->GetHash();
		pxImport->m_bDone = false;
		UpdateDialogsForCurrentSelection();
	}
}

void Tools_Overlay_ImportMaterialResponse::ApplyMaterial()
{
	if(!m_xImportList.size()) return;
	Tools_MaterialResponse_ImportData* pxImport = m_xImportList[m_iSelection];

	ApplyMaterial(pxImport);
}

void Tools_Overlay_ImportMaterialResponse::ApplyAll()
{
	//apply the changes for any materials that haven't been done yet
	for(LIF<Tools_MaterialResponse_ImportData*> xImportLIF(&m_xImportList); !xImportLIF.done(); xImportLIF.next())
	{
		if(!xImportLIF()->m_bDone)
		{
			ApplyMaterial(xImportLIF());			
		}
	}
}

void Tools_Overlay_ImportMaterialResponse::ApplyMaterial(Tools_MaterialResponse_ImportData* pxImport)
{
	if(!pxImport->m_uMaterialResponse)
	{
		//No material has been assigned yet , so go for an automatically generated one
		Tools_MaterialResponse* pxMaterial = Tools_Texturing_MaterialResponse::CreateMaterialResponseFromOldMaterial(pxImport->m_iMaterialID, pxImport->m_iSlot,true);
		if(pxMaterial)
		{
			pxImport->m_uMaterialResponse = pxMaterial->GetHash();
			
			//If this was the selected material , then the overlay will need updating
			if(pxImport == m_xImportList[m_iSelection])
			{
				UpdateDialogsForCurrentSelection();
			}
		}
		else
		{
			return;
		}
	}

	//Find the polygons to alter
	pxImport->UpdatePolygonLists();

	//apply the changes
	for( int i = 0; i < Asura_Module_System::GetNumberOfModules(); ++i )
	{
		Asura_Mesh* const pxMesh = Asura_Module_System::GetMeshForModule(i);
		if( !pxMesh->TextureDataValid )
		{
			Asura_Assertf( false, ("Mesh '%s' has invalid texture data.", pxMesh->Name.GetString()) );
			continue;
		}

		Tools_MaterialResponse_ModuleImportData* pxModuleImportData = &pxImport->m_pxModules[i];
		if(!pxModuleImportData->m_xPolygonList.size()) continue;
		
		for(LIF<int> xPolyLIF(&pxModuleImportData->m_xPolygonList); !xPolyLIF.done(); xPolyLIF.next())
		{
			Tools_Texturing_MaterialResponse::LogImportMaterial( pxMesh->Index, xPolyLIF());

			pxMesh->TextureDataPtr[xPolyLIF()].m_auMaterialResponseHashID[pxImport->m_iSlot] = pxImport->m_uMaterialResponse;
		}		


		Tools_UpdateTexturingOnSurfaces( pxMesh );
		Tools_Texturing_MaterialResponse::SetMeshModified( pxMesh, true );
	}

	pxImport->m_bDone = true;
}

void Tools_Overlay_ImportMaterialResponse::FindPolygons()
{
	if(!m_xImportList.size()) return;
	Tools_MaterialResponse_ImportData* pxImport = m_xImportList[m_iSelection];

	Tools_PolygonFind_ImportMaterial xFind(pxImport);
	Tools_Texturing_Find::FindPolygons(&xFind);

}

bool Tools_Overlay_ImportMaterialResponse::IsMaterialSelected()
{
	return m_xImportList.size() != 0;
}

bool Tools_Overlay_ImportMaterialResponse::IsMaterialAssigned()
{
	if(m_xImportList.size() == 0) return false;
	return m_xImportList[m_iSelection]->m_uMaterialResponse != 0;
}

bool Tools_Overlay_ImportMaterialResponse::CanFindPolygons()
{
	return IsMaterialSelected() && Tools_Polygon_Selection::IsActive();
}


void Tools_Overlay_ImportMaterialResponse::ForceImport()
{
	bool bConfirm = ShowYesNoOverlay("Force Import.\nThis will ditch all the existing material response assignments, and apply some (probably newly created) materials over the top instead.\nAre you sure you want to do this?");
	if(!bConfirm) return;

	//blat all the existing material assignments
	for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
	{
		Asura_Mesh* const pxMesh = Asura_Module_System::GetMeshForModule(i);
		if( !pxMesh->TextureDataValid )
		{
			Asura_Assertf( false, ("Mesh '%s' has invalid texture data.", pxMesh->Name.GetString()) );
			continue;
		}

		for(int j=0; j<pxMesh->NumberOfPolygons; j++)
		{
			if(pxMesh->TextureDataPtr[j].m_iMaterialID == ASURA_MATERIAL_NONE) continue;
			if(pxMesh->IsMergedObjectPolygon(j)) continue;

			bool bLoggedForUndo = false;
			
			for(int iMatSlot=0;iMatSlot<2;iMatSlot++)
			{
				Tools_MaterialResponse* pxMaterialResponse = Tools_Texturing_MaterialResponse::CreateMaterialResponseFromOldMaterial(pxMesh->TextureDataPtr[j].m_iMaterialID, iMatSlot,true);
				if(pxMaterialResponse)
				{
					if(!bLoggedForUndo)
					{
						Tools_Texturing_MaterialResponse::LogImportMaterial(pxMesh->Index, j);
						
						bLoggedForUndo = true;
					}
					
					pxMesh->TextureDataPtr[j].m_auMaterialResponseHashID[iMatSlot] = pxMaterialResponse->GetHash();
				}
			}
		}

		Tools_UpdateTexturingOnSurfaces( pxMesh );
		Tools_Texturing_MaterialResponse::SetMeshModified( pxMesh, true );
	}

	CloseOverlay();
}


void Tools_MaterialResponse_ImportData::UpdatePolygonLists()
{
	//allocate module data , if it hasn't been done yet ,or if the number of modules has changed
	int iNumberOfModules = Asura_Module_System::GetNumberOfModules();
	if(!m_pxModules || m_iNumberOfModules != iNumberOfModules)
	{
		delete [] m_pxModules;

		m_iNumberOfModules = iNumberOfModules;
		m_pxModules = new Tools_MaterialResponse_ModuleImportData[m_iNumberOfModules];
	}

	for(int i=0;i<iNumberOfModules;i++)
	{
		Asura_Mesh* const pxMesh = Asura_Module_System::GetMeshForModule(i);
		if( !pxMesh->TextureDataValid )
		{
			Asura_Assertf( false, ("Mesh '%s' has invalid texture data.", pxMesh->Name.GetString()) );
			continue;
		}

		Tools_MaterialResponse_ModuleImportData* pxModuleImportData = &m_pxModules[i];

		//If the number of polygons hasn't changed ,then assume the old polygon lists are valid
		if(pxMesh->NumberOfPolygons == pxModuleImportData->m_iNumberOfPolygons) continue;
		pxModuleImportData->m_iNumberOfPolygons = pxMesh->NumberOfPolygons;

		while(pxModuleImportData->m_xPolygonList.size()) pxModuleImportData->m_xPolygonList.delete_first_entry();

		//search for all polygons that use the old material , and haven't been assigned a material response yet
		for(int j=0; j<pxMesh->NumberOfPolygons; j++)
		{
			if(pxMesh->TextureDataPtr[j].m_iMaterialID != m_iMaterialID) continue;
			if(pxMesh->TextureDataPtr[j].m_auMaterialResponseHashID[m_iSlot] != 0) continue;

			if(pxMesh->IsMergedObjectPolygon(j)) continue;

			pxModuleImportData->m_xPolygonList.add_entry(j);
		}

	}
}



#endif //ASURA_USE_MATERIAL_RESPONSE_SYSTEM