#include "AdeptHeaders.hpp"

#include "ResourceImagePool.hpp"
#include "Application.hpp"
#include "Tool.hpp"
#include "VideoRenderer.hpp"
#include <Compost\Compost.hpp>
#include <MLR\GOSImage.hpp>

using MidLevelRenderer::GOSImage;

#define SUPPORT_TGA
#define SUPPORT_PNG

#if 1
#define TEXTURE_LOAD(string) LOG_BLOCK("Texture Load::" string)
#else
#define TEXTURE_LOAD(string)
#endif



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Compost::FetchTextureData(Compost::Feature_Texture *tex, int index, int type)
{
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Compost::FetchTextureData(Compost::Feature_Texture *tex, const char *name, int type)
{
	Verify(gos_GetCurrentHeap() == Heap);
	MString fname(name);
	fname += ".bid";

	fname.ToLower();

	FileStream hFile(fname);
	int size =	tex->RecalculateSize();
	tex->data = new BYTE [size];
	tex->dataSize = size;

	if(size != hFile.GetBytesRemaining())
	{
		STOP(("Length of BID-file %s incorrect", name));
	}
	
	hFile.ReadBytes(tex->data, size);
	
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Compost::DestroyTextureData(Compost::Feature_Texture *tex)
{
	delete [] tex->data;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DWORD ResourceImagePool::FindTexture(
	const char* tex_name,
	int hint
)
{
	Check_Pointer(tex_name);
	TEXTURE_LOAD("Find Texture");

	//
	//----------------------------------------------
	// Put the texture directory on the texture name
	//----------------------------------------------
	//
	MString file_name = "content\\textures\\";
	file_name += tex_name;
	file_name += ".tga";

	//
	//---------------------------------------------
	// Loop through testing for each extension type
	//---------------------------------------------
	//
#if defined(SUPPORT_PNG)
	char* ext_pointer = static_cast<char*>(file_name) + file_name.GetLength()-3;
	do
	{
#endif
		//
		//-------------------------------------------------------------------
		// If we can find a texture matching the name, check to see if it has
		// explicit mipmaps.  If so, we need to secretly reroute to a texture
		// given by the bias
		//-------------------------------------------------------------------
		//
		bool exists;
		{
			Resource texture_stream(file_name);
			exists = texture_stream.DoesResourceExist();
		}
		if (exists)
		{
			int hint_flags;
			Resource hint_stream(MString(file_name)+"{hint}");
			Verify(hint_stream.DoesResourceExist());
			hint_stream.LoadData();
			hint_stream >> hint_flags;
			if (Environment.bitDepth == 32)
				hint_flags |= gosHint_Try32bpp;
			hint_stream.UnloadData();
			if (hint_flags & gosHint_UserMipMaps)
			{
				int bias = hint_flags >> 24;
				bias = VideoRenderer::s_MipBias + bias;
				Min_Clamp(bias, 0);
				int len = strlen(file_name)-5;
				file_name[len] = static_cast<char>(file_name[len] + bias);
				Max_Clamp(file_name[len], '5');
			}
			char format = static_cast<char>(hint_flags>>16);
			hint_flags &= 0xffff;
			DWORD texture_handle =
				gos_NewTextureFromFile(
					static_cast<gos_TextureFormat>(format),
					file_name,
					hint_flags|hint
				);

			//
			//--------------------------------------------
			// If we want this in video memory, preload it
			//--------------------------------------------
			//
			if ((hint_flags & (gosHint_AGPMemory|gosHint_VideoMemory)) == gosHint_VideoMemory)
				gos_PreloadTexture(texture_handle);
			return texture_handle;
		}

		//
		//--------------------------------------------------------------------
		// We couldn't find the resource, so try the actual file.  If the hint
		// says to use explicit mipmaps, bump the number by the bias
		//--------------------------------------------------------------------
		//
		else
		{
			if (hint & gosHint_UserMipMaps)
			{
				int len = strlen(file_name)-5;
				Verify(file_name[len] == '0');
				file_name[len] =
					static_cast<char>(file_name[len] + VideoRenderer::s_MipBias);
			}

			//
			//-----------------------------
			// See if the file can be found
			//-----------------------------
			//
			if (gos_DoesFileExist(file_name))
			{
#ifdef LAB_ONLY
				PAUSE(("[%s] is NOT in resources but found on disc.", tex_name));
#endif
				if (FileStream::IsRedirected)
				{
					FileStream::IsRedirected = false;
					return
						gos_NewTextureFromFile(
							gos_Texture_Detect,
							FileStream::RedirectedName,
							hint
						);
				}
				else
				{
					return
						gos_NewTextureFromFile(
							gos_Texture_Detect,
							file_name,
							hint
						);
				}
			}
#ifdef LAB_ONLY
			else
			{
				PAUSE(("[%s] is NOT in resources and NOT on disc.", tex_name));
			}
#endif

			Verify(!FileStream::IsRedirected);
		}

		//
		//----------------------------------------------------------------
		// Stop the loop if the png doesn't exist or if we found our image
		//----------------------------------------------------------------
		//
#if defined(SUPPORT_PNG)
		if (ext_pointer[0]=='p')
			break;

		//
		//---------------------------
		// roll to the next extension
		//---------------------------
		//
		ext_pointer[0]='p';
		ext_pointer[1]='n';
		ext_pointer[2]='g';
		Verify(!ext_pointer[3]);
	} while (true);
#endif

	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ResourceImagePool::LoadImageGOS(GOSImage *image, int hint)
{
	//
	//----------------------------------------
	// If the image is already loaded, we done
	//----------------------------------------
	//
	TEXTURE_LOAD("Load Image");
	if ( (image->flags & GOSImage::Loaded) != 0)
		return true;

	//
	//-----------------------------------------------------------------
	// if the image name starts with an underscore, it requires special
	// treatment.  Two underscores mean no skin, so just remove the
	// first underscore
	//-----------------------------------------------------------------
	//
	const char* tex_name = image->imageName;
	if (tex_name[1] == g_SkinSignal)
	{
		if (*tex_name == g_SkinSignal)
			++tex_name;

		//
		//-------------------------------------------------------------------
		// If we get here, then we must synthesize this new texture rather
		// than using something directly from the resources.  We will have to
		// open up both the specified file and a skin file and mix them
		//-------------------------------------------------------------------
		//
		else
		{
			TEXTURE_LOAD("Load Image::Composite Skin");
			static char skin[7] = "skin 0";
			skin[4] = *tex_name;

			//
			//---------------------------------------------------------------
			// We've got the names of both textures now, next we need to have
			// gos open them up for us
			//---------------------------------------------------------------
			//
			DWORD skin_handle = FindTexture(skin, gosHint_DisableMipmap|gosHint_DontShrink);
			if(skin_handle==0)
			{
				STOP((0, "Skin: %s not found !", skin));
			}
			DWORD detail_handle = FindTexture(tex_name+1, gosHint_DisableMipmap|gosHint_DontShrink);
			if(detail_handle==0)
			{
				STOP((0, "Skindetail: %s not found !", tex_name+1));
			}
			image->imageHandle = SkinThis(tex_name, gos_Texture_Solid, skin_handle, detail_handle);

			gos_DestroyTexture(detail_handle);
			gos_DestroyTexture(skin_handle);
			image->flags |= (image->imageHandle ? GOSImage::Loaded : 0);
			return ( (image->flags & GOSImage::Loaded) != 0);
		}
	}

	//
	//--------------------------
	// Just open the texture now
	//--------------------------
	//
	image->imageHandle = FindTexture(tex_name, hint|gosHint_ReloadFromDisk);

	//
	//----------------------------------
	// See if the image loaded correctly
	//----------------------------------
	//
	image->flags |= (image->imageHandle ? GOSImage::Loaded : 0);
	return ( (image->flags & GOSImage::Loaded) != 0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ResourceImagePool::BuildTexturePool(NotationFile *hint_file)
{
	//
	//-------------------------------
	// Open up the texture build file
	//-------------------------------
	//
	Check_Object(Tool::Instance);
	NotationFile::PageIterator *pages = hint_file->MakePageIterator();
	Register_Object(pages);

	//
	//---------------------------------------------------------
	// Go through the textures file and add each listed texture
	//---------------------------------------------------------
	//
	Check_Object(Tool::Instance);
	Page *page;
	Verify(!Resource::ParentFileDependencies);
	while ((page = pages->ReadAndNext()) != NULL)
	{
		Check_Object(page);
		const char* texture = page->GetName();
		if (!texture)
			continue;
		Check_Pointer(texture);

		//
		//------------------------------------------------------------
		// See if the resource file is aliased, if so replace the name
		//------------------------------------------------------------
		//
		page->GetEntry("alias", &texture);

		//
		//--------------------------------------------
		// If we don't resourcify the texture, move on
		//--------------------------------------------
		//
		bool resourcify = true;
		page->GetEntry("resourcify", &resourcify);
		if (!resourcify)
			continue;

		//
		//------------------------------------------------
		// See if we can open up a resource with this name
		//------------------------------------------------
		//
		MString file_name = "content\\textures\\";
		file_name += texture;
		file_name += ".tga";

		//
		//--------------------------
		// Find the file on the path
		//--------------------------
		//
		Verify(!FileStream::IsRedirected);

#ifdef LAB_ONLY
	strcpy(MWGameInfo::g_LastFileOpenRequest, "BuildTexturePool:   ");
	strcpy(MWGameInfo::g_LastFileOpenRequest + 20, file_name);
#endif


		if (!gos_DoesFileExist(file_name))
		{
#if defined(SUPPORT_PNG)
			Verify(!FileStream::IsRedirected);
			char *extension = (char*)file_name + file_name.GetLength()-3;
			extension[0] = 'p';
			extension[1] = 'n';
			extension[2] = 'g';
			Verify(!extension[3]);
			if (!gos_DoesFileExist(file_name))
#endif
			{
#ifdef LAB_ONLY
				file_name = "content\\textures\\01AACA1.tga";	/* [editor degraded-mode] LAB builds (editor/Profile) substitute a placeholder for missing WIP textures instead of fatal STOP, so downstream gets a valid handle */
#else
				STOP(("Texture %s could not be found!", (const char *)file_name));	/* Release: fail loud, ship no placeholder */
#endif
			}
		}

		//
		//--------------------------------------------------------
		// If not, or it's not up to date, put it in the resources
		//--------------------------------------------------------
		//
		Resource resource((FileStream::IsRedirected)?FileStream::RedirectedName:file_name);
		FileStream::IsRedirected = false;
		if (!resource.DoesResourceExist() || !resource.IsResourceUpToDate())
		{

			//
			//--------------------
			// Read the hint flags
			//--------------------
			//
			int hints = ReadHint(page) & ~gosHint_ReloadFromDisk;

			//
			//-------------------
			// Read in the format
			//-------------------
			//
			const char* format;
			if (page->GetEntry("format", &format))
			{
				if (!strcmp(format, "solid"))
					hints |= static_cast<BYTE>(gos_Texture_Solid) << 16;
				else if (!strcmp(format, "keyed"))
					hints |= static_cast<BYTE>(gos_Texture_Keyed) << 16;
				else if (!strcmp(format, "alpha"))
					hints |= static_cast<BYTE>(gos_Texture_Alpha) << 16;
				else if (!strcmp(format, "bump"))
					hints |= static_cast<BYTE>(gos_Texture_Bump) << 16;
				else if (!strcmp(format, "normal"))
					hints |= static_cast<BYTE>(gos_Texture_Normal) << 16;
				else
					hints |= static_cast<BYTE>(gos_Texture_Detect) << 16;
			}
			else
				hints |= static_cast<BYTE>(gos_Texture_Detect) << 16;

			//
			//--------------------------------------------------
			// Look to see if we want a one level mip-bias delay
			//--------------------------------------------------
			//
			int bias=0;
			page->GetEntry("bias", &bias);
			Clamp(bias, -5, 5);
			hints |= bias << 24;

			//
			//---------------------------------
			// Open the file as a memory stream
			//---------------------------------
			//
			FileStream texture_file(file_name);
			FileDependencies texture_dependencies;
			texture_dependencies.AddDependency(&texture_file);
			texture_dependencies.AddDependencies(hint_file->GetFileDependencies());
			resource.Save(&texture_file, &texture_dependencies);

			//
			//---------------------------------
			// Open the file as a memory stream
			//---------------------------------
			//
			MString hint_name = resource.GetName();
			hint_name += "{hint}";
			Resource hint(hint_name);
			MemoryStream hint_stream(&hints, sizeof(hints));
			hint.Save(&hint_stream, hint_file->GetFileDependencies());
		}
	}

	Unregister_Object(pages);
	delete pages;
}

