#include "CompostHeaders.hpp"
#include <GameOS\ToolOS.hpp>

TexturePool *TexturePool::Instance=NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TexturePool::TexturePool() 
:	imageTable(
			   NULL,
			   true
			   )
			   
{
	texturePath = NULL;
	changed = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TexturePool::~TexturePool()
{
	Stuff::TableIteratorOf<Feature_Texture*, int> images(&imageTable);
	Feature_Texture *fettext=NULL;
	
	while(NULL!=(fettext=images.ReadAndNext()))
	{
		if(fettext->GetLoadedMode())
		{
			Compost::DestroyTextureData(fettext);
		}
	}
	
	images.DeletePlugs();
	
	
	if(texturePath)
	{
		Unregister_Pointer(texturePath);
		delete texturePath;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
TexturePool::SetTexturePath(const char *pName)
{
	Verify(gos_GetCurrentHeap() == Heap);
	Check_Pointer(this);
	
	if(pName==NULL)
	{
		return;
	}
	
	if(texturePath)
	{
		Unregister_Pointer(texturePath);
		delete texturePath;
	}
	
	texturePath = new char [strlen(pName)+1];
	Register_Pointer(texturePath);
	
	strcpy(texturePath, pName);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
TexturePool::SetTexturePath(const char *pName, const char *tcf_filename)
{
	Verify(gos_GetCurrentHeap() == Heap);
	Check_Pointer(this);
	
	if(pName==NULL)
	{
		return;
	}
	
	if(texturePath)
	{
		Unregister_Pointer(texturePath);
		delete texturePath;
	}
	
	texturePath = new char [strlen(pName)+1];
	Register_Pointer(texturePath);
	
	strcpy(texturePath, pName);
	
	LoadIndex(tcf_filename);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
TexturePool::GetMask(int value)
{
	int i, first=-1;
	int mask=1;
	
	for(i=0;i<8*sizeof(int);i++)
	{
		if(value & mask)
		{
			if(first<0)
			{
				first = i;
			}
			else
			{
				return 0xffffffff;
			}
		}
		mask <<= 1;
	}
	
	if(first<0)
	{
		return 0;
	}
	
	mask = 1;
	
	for(i=1;i<first;i++)
	{
		mask <<= 1;
		mask |= 1;
	}
	
	return mask;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
TexturePool::Add(
				 Stuff::MString name,
				 int width,
				 int height,
				 Feature_Texture::TextureMode type
				 )
{
	Verify(gos_GetCurrentHeap() == Heap);
	Check_Object(this);
	
	Verify(name.GetLength() > 0);
	
	name.ToLower();

	int index = name.GetHashValue();
	Feature_Texture *image;
	if ((image = imageTable.Find(index)) == NULL)
	{
		image = new Tool_Feature_Texture();
		Check_Object(image);
		
		((Tool_Feature_Texture *)image)->name = name;
		image->SetTextureMode(type);
		
		switch(type)
		{
		case Feature_Texture::TextureMode::FTT_555:
			image->SetTextureDepth(2);
			break;
		case Feature_Texture::TextureMode::FTT_4444:
			image->SetTextureDepth(2);
			break;
		case Feature_Texture::TextureMode::FTT_1:
			image->SetTextureDepth(1);
			break;
		case Feature_Texture::TextureMode::FTT_1_1:
			image->SetTextureDepth(1);
			break;
		case Feature_Texture::TextureMode::FTT_1_M:
			image->SetTextureDepth(1);
			break;
		}
		
		image->width = width;
		image->height = height;
		
		image->width_mask = GetMask(width);
		image->height_mask = GetMask(height);
		
		imageTable.AddValue(image, index);

		changed = true;

		return name.GetHashValue();
	}
	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
TexturePool::Add(
				 int index,
				 int width,
				 int height,
				 Feature_Texture::TextureMode type
				 )
{
	Verify(gos_GetCurrentHeap() == Heap);
	Check_Object(this);
	
	Feature_Texture *image;
	if ((image = imageTable.Find(index)) == NULL)
	{
		image = new Feature_Texture();
		Check_Object(image);
		
		image->SetTextureMode(type);
		
		switch(type)
		{
		case Feature_Texture::TextureMode::FTT_555:
			image->SetTextureDepth(2);
			break;
		case Feature_Texture::TextureMode::FTT_4444:
			image->SetTextureDepth(2);
			break;
		case Feature_Texture::TextureMode::FTT_1:
			image->SetTextureDepth(1);
			break;
		case Feature_Texture::TextureMode::FTT_1_1:
			image->SetTextureDepth(1);
			break;
		case Feature_Texture::TextureMode::FTT_1_M:
			image->SetTextureDepth(1);
			break;
		}
		
		image->width = width;
		image->height = height;
		
		image->width_mask = GetMask(width);
		image->height_mask = GetMask(height);
		
		imageTable.AddValue(image, index);

		changed = true;

		return index;
	}
	else
	{
		return 0;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Feature_Texture*
TexturePool::Get(const char* image_name)
{
	Check_Object(this);
	
	Stuff::MString imageName = image_name;
	Verify(imageName.GetLength() > 0);
	
	//
	//---------------------------
	// Get the image for the name
	//---------------------------
	//
	MString name(image_name);
	name.ToLower();
	
	return Get(name.GetHashValue());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Feature_Texture*
TexturePool::Get(int index)
{
	Check_Pointer(this);
	
	Feature_Texture *image;
	
	if ((image = imageTable.Find(index)) == NULL)
	{
		return NULL;
	}
	Check_Pointer(image);
	
	return image;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
TexturePool::GetIndex(Feature_Texture *ft)
{
	if(imageTable.IsEmpty() || ft==NULL)
	{
		return 0;
	}
	TableIteratorOf<Feature_Texture*, int> imageTableIterator(&imageTable);
	
	Feature_Texture *featureTexture;
	
	do
	{
		featureTexture = imageTableIterator.GetCurrent();
		if(featureTexture==NULL)
		{
			break;
		}
		
		if(ft==featureTexture)
		{
			return imageTableIterator.GetValue();
		}
	} while(NULL!=imageTableIterator.ReadAndNext());
	
	return 0;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TexturePool::Remove(Feature_Texture *image)
{
	Check_Pointer(this);
	Unregister_Pointer(image);
	delete image;

	changed = true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TexturePool::Remove(const char* name)
{
	Check_Pointer(this);

	Remove(Get(name));

	changed = true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
TexturePool::Load(const char* , int)
{
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
TexturePool::Load(int index , int type)
{
	if(index==0)
	{
		return false;
	}
	
	Feature_Texture *tex = Get(index);
	if(tex->GetLoadedMode())
	{
		return true;
	}
	
	MString *name = tex->GetName();
	
	if(name)
	{
		MString fileName(texturePath);
		fileName += "\\";
		fileName += *name;
		FetchTextureData(tex, fileName, type);
	}
	else
	{
		FetchTextureData(tex, index, type);
	}
	
	if(tex->data)
	{
		tex->SetLoadedOn();
		return true;
	}
	
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
TexturePool::Load(Feature_Texture *tex)
{
	if(tex==NULL)
	{
		return false;
	}
	
	if(tex->GetLoadedMode())
	{
		return true;
	}
	
	MString *name = tex->GetName();
	
	if(name)
	{
		MString fileName(texturePath);
		fileName += "\\";
		fileName += *name;
		FetchTextureData(tex, fileName, tex->flags);
	}
	else
	{
		//		FetchTexture(tex, index, tex->type);
	}
	
	if(tex->data)
	{
		tex->SetLoadedOn();
		return true;
	}
	
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
TexturePool::LoadIndex()
{
	TableIteratorOf<Feature_Texture*, int> imageTableIterator(&imageTable);
	imageTableIterator.DeletePlugs();
	
	MString files(texturePath);
	files += "\\*.tcf";
	
	char *tmpfile_name=gos_FindFiles(files);
	
		while(tmpfile_name)
		{
			MString fileName(texturePath);
			fileName +="\\";
			fileName +=tmpfile_name;
			
			
			if(!gos_DoesFileExist(fileName))
			{
				return;
			}
			
			NotationFile index(fileName);
			
			const char *textureName;
			int width, height, type;
			
			NotationFile::PageIterator *page_list = index.MakePageIterator();
			Page *tex_page;
			
			while((tex_page = page_list->ReadAndNext()) != NULL)
			{
				Check_Object(tex_page);
				
				if( tex_page->GetEntry("Type", &type) &&
					tex_page->GetEntry("Width", &width) &&
					tex_page->GetEntry("Height", &height)
					)
					
				{
					if(!tex_page->GetEntry("Name", &textureName))
					{
						int number;
						tex_page->GetEntry("Index", &number, true);
						Add(
							number, 
							static_cast<unsigned short>(width), 
							static_cast<unsigned short>(height), 
							static_cast<Feature_Texture::TextureMode>(type)
							);
					}
					else
					{
						Add(
							textureName, 
							static_cast<unsigned short>(width), 
							static_cast<unsigned short>(height), 
							static_cast<Feature_Texture::TextureMode>(type)
							);
					}
				}
			}
			delete page_list;
			tmpfile_name=gos_FindFilesNext();
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TexturePool::LoadIndex(const char *tcf_filename)
{
	TableIteratorOf<Feature_Texture*, int> imageTableIterator(&imageTable);
	imageTableIterator.DeletePlugs();
	
	if(!gos_DoesFileExist(tcf_filename))
	{
		return;
	}
	
	NotationFile index(tcf_filename);
	
	const char *textureName;
	int width, height, type;
	
	NotationFile::PageIterator *page_list = index.MakePageIterator();
	Page *tex_page;
	
	while((tex_page = page_list->ReadAndNext()) != NULL)
	{
		Check_Object(tex_page);
		
		if( tex_page->GetEntry("Type", &type) &&
			tex_page->GetEntry("Width", &width) &&
			tex_page->GetEntry("Height", &height)
			)
			
		{
			if(!tex_page->GetEntry("Name", &textureName))
			{
				int number;
				tex_page->GetEntry("Index", &number, true);
				Add(
					number, 
					static_cast<unsigned short>(width), 
					static_cast<unsigned short>(height), 
					static_cast<Feature_Texture::TextureMode>(type)
					);
			}
			else
			{
				Add(
					textureName, 
					static_cast<unsigned short>(width), 
					static_cast<unsigned short>(height), 
					static_cast<Feature_Texture::TextureMode>(type)
					);
			}
		}
	}
	delete page_list;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
TexturePool::LoadIndex(NotationFile *index)
{
	TableIteratorOf<Feature_Texture*, int> imageTableIterator(&imageTable);
	imageTableIterator.DeletePlugs();

	const char *textureName;
	int width, height, type;
	
	NotationFile::PageIterator *page_list = index->MakePageIterator();
	Page *tex_page;
	
	while((tex_page = page_list->ReadAndNext()) != NULL)
	{
		Check_Object(tex_page);
		
		if( tex_page->GetEntry("Type", &type) &&
			tex_page->GetEntry("Width", &width) &&
			tex_page->GetEntry("Height", &height)
			)
			
		{
			if(!tex_page->GetEntry("Name", &textureName))
			{
				int number;
				tex_page->GetEntry("Index", &number, true);
				Add(
					number, 
					static_cast<unsigned short>(width), 
					static_cast<unsigned short>(height), 
					static_cast<Feature_Texture::TextureMode>(type)
					);
			}
			else
			{
				Add(
					textureName, 
					static_cast<unsigned short>(width), 
					static_cast<unsigned short>(height), 
					static_cast<Feature_Texture::TextureMode>(type)
					);
			}
		}
	}
	delete page_list;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
TexturePool::SaveIndex(const char*)
{
	if(imageTable.IsEmpty())
	{
		changed = false;
		return true;
	}

	TableIteratorOf<Feature_Texture*, int> imageTableIterator(&imageTable);
	
	MString fileName(texturePath);
	fileName += "\\index.tcf";
	
	if(gos_FileReadOnly(fileName)) return false;
	NotationFile index;
	
	char numberString[8];
	const char *str;
	int number = 0;
	MString *name;
	
	Feature_Texture *featureTexture;
	
	str = numberString;
	
	do
	{
		featureTexture = imageTableIterator.GetCurrent();
		if(featureTexture==NULL)
		{
			break;
		}
		
		sprintf(numberString, "%d", number);
		Page *page = index.AddPage(str);
		Check_Object(page);
		
		name = featureTexture->GetName();
		
		if(name)
		{
			page->SetEntry("Name", *name);
		}
		else
		{
			int value = imageTableIterator.GetValue();
			page->SetEntry("Index", value);
		}
		
		page->SetEntry("Type", featureTexture->GetTextureMode());
		page->SetEntry("Width", featureTexture->width);
		page->SetEntry("Height", featureTexture->height);
		
		number++;
	} while(NULL!=imageTableIterator.ReadAndNext());
	
	index.SaveAs(fileName);
	changed = false;
	return true;
}
