#include "ElementProxyHeaders.hpp"

#include <MLR\MLRTexture.hpp>
#include <MLR\MLRTexturePool.hpp>
#include <GameOS\ToolOS.hpp>

//
//############################################################################
//##########################    MLRTextureProxy    ############################
//############################################################################
//

MemoryBlock*
	MLRTextureProxy::AllocatedMemory = NULL;
MLRTextureProxy::ClassData*
	MLRTextureProxy::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::InitializeClass()
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new MemoryBlock(
			sizeof(MLRTextureProxy),
			10,
			10,
			"MLRTextureProxy"
		);
	Register_Object(AllocatedMemory);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MLRTextureProxyClassID,
			"MLRTextureProxy",
			TextureProxy::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::Destroy()
{
	Check_Object(this);
	Verify(referenceCount == 1);
	STOP(("Not implemented"));
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTextureProxy::MLRTextureProxy(
	MLRTexturePoolProxy *library,
	MLRTexture *texture
):
	TextureProxy(DefaultData, library),
	mlrTexture(texture)
{
	Check_Object(this);
	textureSize.x = 0;
	textureSize.y = 0;
	redDepth = 0;
	greenDepth = 0;
	blueDepth = 0;
	alphaDepth = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTextureProxy::~MLRTextureProxy()
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	MLRTextureProxy::UseNextTextureProxyInLibrary()
{
	Check_Object(this);
	MLRTexturePoolProxy *library = GetTextureLibrary();
	Check_Object(library);
	Check_Object(mlrTexture);
	return library->UseTextureProxy(mlrTexture->GetProxyNumber()+1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	MLRTextureProxy::UsePreviousTextureProxyInLibrary()
{
	Check_Object(this);
	MLRTexturePoolProxy *library = GetTextureLibrary();
	Check_Object(library);
	Check_Object(mlrTexture);
	return library->UseTextureProxy(mlrTexture->GetProxyNumber()-1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MLRTextureProxy::GetName(MString *filename)
{
	Check_Object(this);
	Check_Pointer(filename);

	//
	//------------------------
	// Set the name and return
	//------------------------
	//
	Check_Object(mlrTexture);
	*filename = mlrTexture->GetTextureName();
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::SetName(const char* filename)
{
	Check_Object(this);
	Check_Pointer(filename);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::GetImageSize(Vector2DOf<int> *size)
{
	Check_Object(this);
	Check_Pointer(size);
	if (!redDepth)
		ReadTexture();
	*size = textureSize;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::GetChannelDepth(
		unsigned *red_depth,
		unsigned *blue_depth,
		unsigned *green_depth,
		unsigned *alpha_depth
	)
{
	Check_Object(this);
	Check_Pointer(red_depth);
	Check_Pointer(blue_depth);
	Check_Pointer(green_depth);
	Check_Pointer(alpha_depth);

	//
	//--------------------------------------------------------
	// Get the texture attribute, and find out if we got alpha
	//--------------------------------------------------------
	//
	if (!redDepth)
		ReadTexture();
	*red_depth = redDepth;
	*blue_depth = blueDepth;
	*green_depth = greenDepth;
	*alpha_depth = alphaDepth;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::GetChannels(
		Stuff::DynamicArrayOf<BYTE> *red,
		Stuff::DynamicArrayOf<BYTE> *green,
		Stuff::DynamicArrayOf<BYTE> *blue,
		Stuff::DynamicArrayOf<BYTE> *alpha
	)
{
	Check_Object(this);
	Check_Object(red);
	Check_Object(green);
	Check_Object(blue);

	//
	//----------------------------------------------------
	// Read the channels if they haven't already been read
	//----------------------------------------------------
	//
	if (!redDepth)
		ReadTexture();

	//
	//---------------------
	// Now copy the channel
	//---------------------
	//
	*red = redChannel;
	*green = greenChannel;
	*blue = blueChannel;
	if (alpha)
	{
		Check_Object(alpha);
		*alpha = alphaChannel;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::SetChannels(
		const DynamicArrayOf<BYTE> *red,
		const DynamicArrayOf<BYTE> *green,
		const DynamicArrayOf<BYTE> *blue,
		const DynamicArrayOf<BYTE> *alpha
	)
{
	Check_Object(this);
	Verify(GetClassID() == TextureProxyClassID);
	Check_Object(red);
	Check_Object(green);
	Check_Object(blue);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTextureProxy::ReadTexture()
{
	Check_Object(this);

	//
	//----------------------------------------------------
	// Read the channels if they haven't already been read
	//----------------------------------------------------
	//
	Verify(!redDepth);
	MString file_name = GetTextureLibrary()->GetTexturePath();
	file_name += mlrTexture->GetTextureName();
	MString name1 = file_name+".png";
	if (::gos_DoesFileExist(name1))
	{
		ReadPNG(
			name1,
			&redDepth,
			&greenDepth,
			&blueDepth,
			&alphaDepth,
			&textureSize,
			&redChannel,
			&greenChannel,
			&blueChannel,
			&alphaChannel
		);
	}
	else
		{
			name1 = file_name+".tga";
			if (::gos_DoesFileExist(name1))
			{
				ReadTarga(
					name1,
					&redDepth,
					&greenDepth,
					&blueDepth,
					&alphaDepth,
					&textureSize,
					&redChannel,
					&greenChannel,
					&blueChannel,
					&alphaChannel
				);
			}
			else
			{
				file_name = MString("Cannot Find Texture: ")+file_name;
				STOP((file_name));
			}
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool MLRTextureProxy::GetFormat(Proxies::TextureProxy::FormatType* format)
{
	Check_Object(this);	
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MLRTextureProxy:: SetFormat(Proxies::TextureProxy::FormatType format)
{
	Check_Object(this);	
}
	
//
//############################################################################
//#########################    MLRTexturePoolProxy    ###########################
//############################################################################
//

MLRTexturePoolProxy::ClassData
	*MLRTexturePoolProxy::DefaultData = NULL;
MLRTexturePoolProxy
	*MLRTexturePoolProxy::Instance = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePoolProxy::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MLRTexturePoolProxyClassID,
			"MLRTexturePoolProxy",
			TextureLibrary::DefaultData
		);
	Register_Object(DefaultData);
	Verify(!Instance);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePoolProxy::TerminateClass()
{
	if (Instance)
	{
		Instance->CloseLibrary();
		Verify(Instance->GetReferenceCount() == 1);
		Instance->DetachReference();
		Instance = NULL;
	}
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePoolProxy::Destroy()
{
	Check_Object(this);
	Verify(referenceCount == 1);
	Verify(activeTextureProxies.IsEmpty());
	STOP(("Not implemented"));
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexturePoolProxy::MLRTexturePoolProxy(
	MLRStatePoolProxy *states,
	const char* path
):
	TextureLibrary(DefaultData, states)
{
	Check_Object(this);
	Verify(!MLRTexturePool::Instance);
	Verify(!Instance);
	texturePath = path;
	gos_PushCurrentHeap(MidLevelRenderer::TexturePoolHeap);
	GOSImagePool *pool = new TGAFilePool(texturePath);
	Register_Object(pool);
	MLRTexturePool::Instance = new MLRTexturePool(pool);
	Register_Object(MLRTexturePool::Instance);
	gos_PopCurrentHeap();

	//
	//---------------------------------------------------------
	// Read the MLR library and make proxies out of what exists
	//---------------------------------------------------------
	//
	textureCount = MLRTexturePool::Instance->GetNumStoredTextures();
	int lastHandle = MLRTexturePool::Instance->GetLastHandle();

	textureProxies.SetLength(textureCount+10);
	int i, j, first, count, depth = MLRTexturePool::Instance->GetInstanceDepth();
	for (i=0,count=0; i<=lastHandle; ++i)
	{
		first = i<<depth;
		for(j=first;j<first+depth;j++)
		{
			MLRTexture *mlr_texture = (*MLRTexturePool::Instance)[j+1];
			if(mlr_texture)
			{
				Check_Object(mlr_texture);
		
				textureProxies[count] = MLRTextureProxy::MakeProxy(this, mlr_texture);
				Register_Object(textureProxies[count]);
				count++;
			}
		}
	}
	Verify(count == textureCount);
	Instance = this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexturePoolProxy::MLRTexturePoolProxy(MLRStatePoolProxy *states):
	TextureLibrary(DefaultData, states)
{
	Check_Object(this);
	Check_Object(MLRTexturePool::Instance);
	Verify(!Instance);

	MLRTexturePool::Instance->GetGOSImagePool()->GetTexturePath(&texturePath);
	//
	//---------------------------------------------------------
	// Read the MLR library and make proxies out of what exists
	//---------------------------------------------------------
	//
	textureCount = MLRTexturePool::Instance->GetNumStoredTextures();
	int lastHandle = MLRTexturePool::Instance->GetLastHandle();

	textureProxies.SetLength(textureCount+10);
	int i, j, first, count, depth = MLRTexturePool::Instance->GetInstanceDepth();
	for (i=0,count=0; i<=lastHandle; ++i)
	{
		first = i<<depth;
		for(j=first;j<first+depth;j++)
		{
			MLRTexture *mlr_texture = (*MLRTexturePool::Instance)[j+1];
			if(mlr_texture)
			{
				Check_Object(mlr_texture);
		
				textureProxies[count] = MLRTextureProxy::MakeProxy(this, mlr_texture);
				Register_Object(textureProxies[count]);
				count++;
			}
		}
	}
	Verify(count == textureCount);
	Instance = this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTexturePoolProxy::~MLRTexturePoolProxy()
{
	Check_Object(this);
	Verify(activeTextureProxies.IsEmpty());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePoolProxy::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	MLRTexturePoolProxy::GetTextureCount()
{
	Check_Object(this);
	return textureCount;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	MLRTexturePoolProxy::UseNewTextureProxy()
{
	Check_Object(this);
	STOP(("Not supported"));
	return NULL;
}

bool g_bSaveTextureAsPng = false;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	MLRTexturePoolProxy::UseMatchingTextureProxy(TextureProxy *texture)
{
	Check_Object(this);
	Check_Object(texture);

	//
	//------------------------------------------
	// See if the texture name is in our library
	//------------------------------------------
	//
	MString name;
	texture->GetName(&name);
	Check_Object(MLRTexturePool::Instance);
	MLRTexture *mlr_texture = (*MLRTexturePool::Instance)(name,0);

	//
	//-----------------------------------
	// If the handle exists, make a proxy
	//-----------------------------------
	//
	MLRTextureProxy *proxy;
	if (mlr_texture)
	{
		Check_Object(mlr_texture);
		proxy = UseTextureProxy(mlr_texture->GetProxyNumber());
		Check_Object(proxy);
	}

	//
	//-------------------------------------------------------------------------
	// The handle doesn't exist, so add it to the pool and then write the image
	// where the proxy tells us to
	//-------------------------------------------------------------------------
	//
	else
	{
		mlr_texture = MLRTexturePool::Instance->Add(name);
		Check_Object(mlr_texture);
		proxy = MLRTextureProxy::MakeProxy(this, mlr_texture);
		Register_Object(proxy);
		MString filename = GetTexturePath();
		filename += mlr_texture->GetTextureName();
		if (!g_bSaveTextureAsPng) {
			filename += ".tga";
			texture->SaveAsTarga(filename);
		}
		else {
			filename += ".png";
			texture->SaveAsPng(filename);
		}

		//
		//---------------
		// Bump the array
		//---------------
		//
		if (textureCount == textureProxies.GetLength())
			textureProxies.SetLength(textureCount+10);
		mlr_texture->SetProxyNumber(textureCount);
		textureProxies[textureCount++] = proxy;
		proxy->AttachReference();
	}
	return proxy;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	MLRTexturePoolProxy::UseTextureProxy(const char* name)
{
	Check_Object(this);
	Check_Pointer(name);

	//
	//------------------------------------------
	// See if the texture name is in our library
	//------------------------------------------
	//
	Check_Object(MLRTexturePool::Instance);
	MLRTexture *mlr_texture = (*MLRTexturePool::Instance)(name,0);

	//
	//-----------------------------------
	// If the handle exists, make a proxy
	//-----------------------------------
	//
	MLRTextureProxy *proxy;
	if (mlr_texture)
	{
		Check_Object(mlr_texture);
		proxy = UseTextureProxy(mlr_texture->GetProxyNumber());
		Check_Object(proxy);
	}

	//
	//------------------------------------------------
	// The handle doesn't exist, so add it to the pool
	//------------------------------------------------
	//
	else
	{
		mlr_texture = MLRTexturePool::Instance->Add(name);
		Check_Object(mlr_texture);
		proxy = MLRTextureProxy::MakeProxy(this, mlr_texture);
		Register_Object(proxy);

		//
		//---------------
		// Bump the array
		//---------------
		//
		if (textureCount == textureProxies.GetLength())
			textureProxies.SetLength(textureCount+10);
		mlr_texture->SetProxyNumber(textureCount);
		textureProxies[textureCount++] = proxy;
		proxy->AttachReference();
	}
	return proxy;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	MLRTexturePoolProxy::UseFirstTextureProxy()
{
	Check_Object(this);
	if (GetTextureCount() > 0)
	{
		textureProxies[0]->AttachReference();
		return textureProxies[0];
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	MLRTexturePoolProxy::UseLastTextureProxy()
{
	Check_Object(this);
	unsigned count = GetTextureCount();
	if (count > 0)
	{
		textureProxies[count-1]->AttachReference();
		return textureProxies[count-1];
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRTexturePoolProxy::CloseLibrary(bool flush_mlr)
{
	Check_Object(this);
	unsigned count = GetTextureCount();
	for (unsigned i=0; i<count; ++i)
	{
		Verify(textureProxies[i]->GetReferenceCount() == 1);
		if (flush_mlr)
		{
			MLRTexture *texture = textureProxies[i]->GetMLRTexture();
			Check_Object(texture);
			MLRTexturePool::Instance->Remove(texture);
		}
		textureProxies[i]->DetachReference();
	}
	textureCount = 0;
	Verify(GetReferenceCount() == 1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRTextureProxy*
	MLRTexturePoolProxy::UseTextureProxy(unsigned handle)
{
	Check_Object(this);

	//
	//-------------------------------------------------------
	// If we just looked off the end of the list, return NULL
	//-------------------------------------------------------
	//
	if (handle >= GetTextureCount())
		return NULL;

	//
	//-------------------
	// Return a new proxy
	//-------------------
	//
	textureProxies[handle]->AttachReference();
	return textureProxies[handle];
}
