#include "MLRHeaders.hpp"
#include "MLRMovieTexture.hpp"

MLRTexturePool *MLRTexturePool::Instance;

MLRTexturePool::ClassData*
	MLRTexturePool::DefaultData = NULL;

DECLARE_TIMER(static, Texture_Loading);

//#############################################################################
//############################    MLRTexture    ###############################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePool::InitializeClass()
{
	Verify(!DefaultData);
	Verify(gos_GetCurrentHeap() == StaticHeap);
	DefaultData =
		new ClassData(
			MLRTexturePoolClassID,
			"MidLevelRenderer::MLRTexturePool",
			RegisteredClass::DefaultData
		);
	Register_Object(DefaultData);

	Initialize_Timer(Texture_Loading, "Texture Loading");

	MLRTexturePool::Instance = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePool::TerminateClass()
{
	if (MLRTexturePool::Instance)
	{
		Unregister_Object(MLRTexturePool::Instance);
		delete MLRTexturePool::Instance;
		MLRTexturePool::Instance = NULL;
	}

	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexturePool::MLRTexturePool(MemoryStream *stream):
	RegisteredClass(DefaultData),
	unloadedTextures(NULL)
{
	Verify(gos_GetCurrentHeap() == TexturePoolHeap);

	theSkinner = NULL;

	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexturePool::MLRTexturePool(GOSImagePool *image_pool, int insDep):
	RegisteredClass(DefaultData),
	unloadedTextures(NULL)
{
	Check_Object(image_pool);
	Verify(gos_GetCurrentHeap() == TexturePoolHeap);

	instanceDepth = insDep;
	instanceMax = 1<<insDep;

	handleDepth = MLRState::TextureNumberBits - insDep;
	handleMax = 1<<handleDepth;

	freeHandle = new int [handleMax];
	Register_Pointer(freeHandle);
	lastHandle = 0;
	firstFreeHandle = 0;
	lastFreeHandle = 0;

	storedTextures = 0;

	imagePool = image_pool;

	for(int i=0;i<MLRState::TextureMask+1;i++)
	{
		textureArray[i] = NULL;
	}

	theSkinner = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexturePool::~MLRTexturePool()
{
	int i;

	for(i=0;i<MLRState::TextureMask;i++)
	{
		if(textureArray[i] != NULL)
		{
			Unregister_Object(textureArray[i]);

			delete textureArray[i];

			textureArray[i] = NULL;
		}
	}
	Unregister_Object(imagePool);
	delete imagePool;

	Unregister_Pointer(freeHandle);
	delete [] freeHandle;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexturePool*
	MLRTexturePool::Make(MemoryStream *stream)
{
	Check_Object(stream);

	gos_PushCurrentHeap(TexturePoolHeap);
	MLRTexturePool *pool = new MLRTexturePool(stream);
	gos_PopCurrentHeap();

	return pool;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePool::Save(MemoryStream *stream)
{
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexture*
	MLRTexturePool::Add(const char *tn, int instance)
{
	MString textureName;
		
	if(theSkinner != NULL)	
	{
		textureName = (*theSkinner)(tn);
	}
	else
	{
		textureName = tn;
	}

	int i, j, textureNameHashValue = textureName.GetHashValue();

	for(i=0;i<lastHandle;i++)
	{
		int first = i<<instanceDepth;
		bool yo = false;

		for(j=first;j<first+instanceMax;j++)
		{
			if(	textureArray[j] && 
				textureArray[j]->textureNameHashValue == textureNameHashValue )
			{
				yo = 1;
			}
		}

		if(yo == false)
		{
			continue;
		}

		for(j=first;j<first+instanceMax;j++)
		{
			if(	textureArray[j] && 
				textureArray[j]->instance == instance)
			{
				return textureArray[j];
			}
		}

		for(j=first;j<first+instanceMax;j++)
		{
			if(!textureArray[j])
			{
				gos_PushCurrentHeap(TexturePoolHeap);
				textureArray[j] = 
					new MLRTexture(
						this, 
						textureName,
						instance,
						j+1
					);
#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
				SPEWALWAYS(("micgaert", "New Texture \"%s\" added: 0x%x", (const char *)textureName, j+1));
#endif
				Register_Object(textureArray[j]);
				gos_PopCurrentHeap();
				unloadedTextures.Add(textureArray[j]);

				storedTextures++;

				return textureArray[j];
			}
		}
		STOP(("Asked for too much image instances !"));
	}

	int newHandle;

	gos_PushCurrentHeap(TexturePoolHeap);
	if(firstFreeHandle < lastFreeHandle)
	{
		newHandle = (freeHandle[firstFreeHandle&(handleMax-1)])<<instanceDepth;

		gos_PushCurrentHeap(TexturePoolHeap);
		textureArray[newHandle] = 
			new MLRTexture(
				this, 
				textureName,
				instance,
				newHandle+1
			);
#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
		SPEWALWAYS(("micgaert", "New Texture \"%s\" added: 0x%x", (const char *)textureName, newHandle+1));
#endif
		Register_Object(textureArray[newHandle]);
		gos_PopCurrentHeap();

		unloadedTextures.Add(textureArray[newHandle]);

		storedTextures++;

		firstFreeHandle++;
	}
	else
	{
		Verify( ((lastHandle<<instanceDepth)+1) < MLRState::TextureMask );

		newHandle = lastHandle<<instanceDepth;

		gos_PushCurrentHeap(TexturePoolHeap);
		textureArray[newHandle] = 
			new MLRTexture(
				this, 
				textureName,
				instance,
				newHandle+1
			);
#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
		SPEWALWAYS(("micgaert", "New Texture \"%s\" added: 0x%x", (const char *)textureName, newHandle+1));
#endif
		Register_Object(textureArray[newHandle]);
		gos_PopCurrentHeap();
		unloadedTextures.Add(textureArray[newHandle]);
	
		storedTextures++;

		lastHandle++;
	}

	Register_Object(textureArray[newHandle]);
	gos_PopCurrentHeap();

	return textureArray[newHandle];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexture*
	MLRTexturePool::AddMovie(const char *tn, int nrOfFrames, Scalar frameTime)
{
	MString textureName;
		
	if(theSkinner != NULL)	
	{
		textureName = (*theSkinner)(tn);
	}
	else
	{
		textureName = tn;
	}

	int i, j, textureNameHashValue = textureName.GetHashValue();

	for(i=0;i<lastHandle;i++)
	{
		int first = i<<instanceDepth;

		for(j=first;j<first+instanceMax;j++)
		{
			if(	textureArray[j] && 
				textureArray[j]->textureNameHashValue == textureNameHashValue )
			{
				return textureArray[j];
			}
		}

	}

	int newHandle;

	gos_PushCurrentHeap(TexturePoolHeap);
	if(firstFreeHandle < lastFreeHandle)
	{
		newHandle = (freeHandle[firstFreeHandle&(handleMax-1)])<<instanceDepth;

		gos_PushCurrentHeap(TexturePoolHeap);
		textureArray[newHandle] = 
			new MLRMovieTexture(
				this, 
				textureName,
				nrOfFrames,
				frameTime,
				newHandle+1
			);
#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
		SPEWALWAYS(("micgaert", "New Movie Texture \"%s\" added: 0x%x", (const char *)textureName, newHandle+1));
#endif
		Register_Object(textureArray[newHandle]);
		gos_PopCurrentHeap();

		unloadedTextures.Add(textureArray[newHandle]);

		storedTextures++;

		firstFreeHandle++;
	}
	else
	{
		Verify( ((lastHandle<<instanceDepth)+1) < MLRState::TextureMask );

		newHandle = lastHandle<<instanceDepth;

		gos_PushCurrentHeap(TexturePoolHeap);
		textureArray[newHandle] = 
			new MLRMovieTexture(
				this, 
				textureName,
				nrOfFrames,
				frameTime,
				newHandle+1
			);
#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
		SPEWALWAYS(("micgaert", "New Movie Texture \"%s\" added: 0x%x", (const char *)textureName, newHandle+1));
#endif
		Register_Object(textureArray[newHandle]);
		gos_PopCurrentHeap();
		unloadedTextures.Add(textureArray[newHandle]);
	
		storedTextures++;

		lastHandle++;
	}

	Register_Object(Cast_Object(MLRMovieTexture*, textureArray[newHandle]));
	gos_PopCurrentHeap();

	return textureArray[newHandle];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexture*
	MLRTexturePool::Add(GOSImage *image)
{
	MString textureName;

	textureName = image->GetName();

	int i, j, textureNameHashValue = textureName.GetHashValue();

	for(i=0;i<lastHandle;i++)
	{
		int first = i<<instanceDepth;

		for(j=first;j<first+instanceMax;j++)
		{
			if(	textureArray[j] && 
				textureArray[j]->textureNameHashValue == textureNameHashValue )
			{
				Verify(image == textureArray[j]->GetImage());
				return textureArray[j];
//				STOP(("Image allready in texture pool !"));
			}
		}
	}

	int newHandle;

	gos_PushCurrentHeap(TexturePoolHeap);
	if(firstFreeHandle < lastFreeHandle)
	{
		newHandle = (freeHandle[firstFreeHandle&(handleMax-1)])<<instanceDepth;

		gos_PushCurrentHeap(TexturePoolHeap);
		textureArray[newHandle] = 
			new MLRTexture(
				this, 
				image,
				newHandle+1
			);
#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
		SPEWALWAYS(("micgaert", "New Texture \"%s\" added: 0x%x", (const char *)textureName, newHandle+1));
#endif
		Register_Object(textureArray[newHandle]);
		gos_PopCurrentHeap();

		storedTextures++;

		firstFreeHandle++;
	}
	else
	{
		Verify( ((lastHandle<<instanceDepth)+1) < MLRState::TextureMask );

		newHandle = lastHandle<<instanceDepth;

		gos_PushCurrentHeap(TexturePoolHeap);
		textureArray[newHandle] = 
			new MLRTexture(
				this, 
				image,
				newHandle+1
			);
#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
		SPEWALWAYS(("micgaert", "New Texture \"%s\" added: 0x%x", (const char *)textureName, newHandle+1));
#endif
		Register_Object(textureArray[newHandle]);
		gos_PopCurrentHeap();
	
		storedTextures++;

		lastHandle++;
	}

	Register_Object(textureArray[newHandle]);
	gos_PopCurrentHeap();

	return textureArray[newHandle];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexture*
	MLRTexturePool::Add(const char* imageName, gos_TextureFormat format, int size, gos_TextureHints hints)
{
	if(theSkinner==NULL)
	{
		return Add(imagePool->GetImage(imageName, format, size, hints) );
	}
	return Add(imagePool->GetImage((*theSkinner)(imageName), format, size, hints) );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePool::Remove(MLRTexture *tex)
{
	textureArray[tex->textureHandle-1] = NULL;
	storedTextures--;

#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
		SPEWALWAYS(("micgaert", "Texture \"%s\" removed: 0x%x", (const char *)tex->textureName, tex->textureHandle));
#endif

	int i, first = (tex->textureHandle-1) & ~(instanceMax-1);

	for(i=first;i<first+instanceMax;i++)
	{
		if(textureArray[i] != NULL)
		{
			break;
		}
	}

	if(i >= first+instanceMax)
	{
		if(tex->image!=NULL)
		{
			imagePool->RemoveImage(tex->image);

			tex->image = NULL;
		}

		freeHandle[lastFreeHandle&(handleMax-1)] = (tex->textureHandle-1) >> instanceDepth;

		lastFreeHandle++;
	}

	tex->textureHandle = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexture*
	MLRTexturePool::operator()(const char *tn, int instance)
{
	Check_Object(this);

	MString textureName = tn;

	int i, j, textureNameHashValue = textureName.GetHashValue();

	for(i=0;i<lastHandle;i++)
	{
		int first = i<<instanceDepth;

		for(j=first;j<first+instanceMax;j++)
		{
			if(	textureArray[j] )
			{
				if(	textureArray[j]->textureNameHashValue == textureNameHashValue )
				{
					if (textureArray[j]->instance == instance )
					{
						return textureArray[j];
					}
				}
				else
				{
					break;
				}
			}
		}
	}

#if defined(TEXTURE_HUNT) && defined(LAB_ONLY)
	SPEWALWAYS(("micgaert", "Couldnt find Texture \"%s\"", tn));
#endif
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePool::LoadImageGOS(MLRTexture *texture)
{
	Check_Object(texture);

	texture->LoadImageGOS(imagePool);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	MLRTexturePool::LoadImages()
{
	Check_Object(imagePool);

	gos_PushCurrentHeap(TexturePoolHeap);
	ChainIteratorOf<MLRTexture *> textures(&unloadedTextures);
	MLRTexture *texture;
	while ((texture = textures.GetCurrent()) != NULL)
	{
		Check_Object(texture);
		LoadImageGOS(texture);
		textures.Remove();
	}
	gos_PopCurrentHeap();
//
// End timing function
//

	return lastHandle;
}
