#include "MAXProxyHeaders.hpp"
#include <ElementRenderer\StateChange.hpp>

//
//############################################################################
//#############################    MAXTexture    ##############################
//############################################################################
//

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

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

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MAXTextureClassID,
			"MAXTexture",
			TextureProxy::DefaultData
		);
	Register_Object(DefaultData);
}

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXTexture::MAXTexture(
	MAXTextureLibrary *library,
	MAXScene *scene,
	int texture_index
):
	TextureProxy(DefaultData, library),
	proxiedScene(scene),
	textureIndex(texture_index)
{
	Check_Object(this);
	textureName = NULL;
	origtextureName = NULL;
	textureSize.x = 0;
	textureSize.y = 0;
	redDepth = 0;
	greenDepth = 0;
	blueDepth = 0;
	alphaDepth = 0;
	formatHint = 0;
	textureFormat = Proxies::TextureProxy::AlphaTexture;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXTexture::~MAXTexture()
{
	Check_Object(this);

	if (textureName)
	{
		Unregister_Pointer(textureName);
		delete[] textureName;
	}
	if (origtextureName)
	{
		Unregister_Pointer(origtextureName);
		delete[] origtextureName;
	}
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::TextureProxy*
	MAXTexture::UseNextTextureProxyInLibrary()
{
	Check_Object(this);
	MAXTextureLibrary *library = GetTextureLibrary();
	Check_Object(library);
	Verify(textureIndex != 0xFFFFFFFF);
	return library->UseTextureProxy(textureIndex+1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::TextureProxy*
	MAXTexture::UsePreviousTextureProxyInLibrary()
{
	Check_Object(this);
	MAXTextureLibrary *library = GetTextureLibrary();
	Check_Object(library);
	Verify(textureIndex != 0xFFFFFFFF);
	if (!textureIndex)
		return NULL;
	return library->UseTextureProxy(textureIndex-1);
}

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

//	if (NULL == textureName)
//	{
		BitmapTex *bitmap = proxiedScene->bmpList.GetBitmap(textureIndex);
		if (NULL == bitmap)
			return false;

		Check_Pointer(bitmap);

		TCHAR *string = (char *)bitmap->GetMapName();
		
		int len = strlen(string);
		MString name;
		if (len != 0)
		{
			name = string;
		}
		Verify(name);
		name.StripDirectory().StripExtension();
		

    //
    // If we already stored this one...
    //
    if (origtextureName == NULL || name != origtextureName)
    {
	    if (textureName)
	    {
		    Unregister_Pointer(textureName);
		    delete[] textureName;
        textureName = NULL;
	    }
	    if (origtextureName)
	    {
		    Unregister_Pointer(origtextureName);
		    delete[] origtextureName;
        origtextureName = NULL;
	    }
      origtextureName = strdup(name);

		  Page *page = proxiedScene->FindHint(name);
		  if (page)
		  {
			  const char *entry;
			  if (page->GetEntry("Alias",&entry))
			  {
				  name = entry;
			  }
		  }
		  else
		  {
			  int x=1;
			  Verify(false);
		  }

		  textureName = strdup(name);
		  Register_Pointer(textureName);
    }
	//}

	*filename = textureName;
	//Unregister_Pointer(textureName);
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXTexture::SetName(const char* filename)
{
	Check_Object(this);
	Check_Pointer(filename);
	Check_Pointer(proxiedScene);

	BitmapTex *bitmap = proxiedScene->bmpList.GetBitmap(textureIndex);
	Check_Pointer(bitmap);

	if (textureName)
	{
		Unregister_Pointer(textureName);
		delete[] textureName;
	}
	if (origtextureName)
	{
		Unregister_Pointer(origtextureName);
		delete[] origtextureName;
	}
	origtextureName = strdup(filename);
	textureName = strdup(filename);
	Register_Pointer(textureName);
	Register_Pointer(origtextureName);

	TCHAR *string = (TCHAR *)textureName;
	bitmap->SetMapName(string);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXTexture::GetImageSize(Vector2DOf<int> *size)
{
	Check_Object(this);
	Check_Pointer(size);
	Check_Pointer(proxiedScene);
	BitmapTex *bitmap = proxiedScene->bmpList.GetBitmap(textureIndex);
	Check_Pointer(bitmap);

	Bitmap *bitmapinfo = bitmap->GetBitmap(proxiedScene->GetTime());
	if (bitmapinfo)
	{
		Check_Pointer(bitmapinfo);

		size->x = bitmapinfo->Width();
		size->y = bitmapinfo->Height();

		textureSize.x = size->x;
		textureSize.y = size->y;
	}
	else
	{
		textureSize.x = 0;
		textureSize.y = 0;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXTexture::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);
	Check_Pointer(proxiedScene);

	BitmapTex *bitmap = proxiedScene->bmpList.GetBitmap(textureIndex);
	Check_Pointer(bitmap);
	Bitmap *bitmapinfo = bitmap->GetBitmap(proxiedScene->GetTime());

	if (bitmapinfo)
	{
		Check_Pointer(bitmapinfo);
		*red_depth = *blue_depth = *green_depth = 8;

		Bitmap *map = bitmap->GetBitmap(proxiedScene->GetTime());
		if (map->HasAlpha())
		{
			*alpha_depth = 8;	
		}
		else
		{
			*alpha_depth = 0;	
		}
	
	}
	else
	{
		*red_depth = *blue_depth = *green_depth = *alpha_depth = 0;
	}
	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXTexture::GetChannels(
		Stuff::DynamicArrayOf<BYTE> *red,
		Stuff::DynamicArrayOf<BYTE> *green,
		Stuff::DynamicArrayOf<BYTE> *blue,
		Stuff::DynamicArrayOf<BYTE> *alpha
	)
{
	Check_Object(this);
	Verify(GetClassID() == MAXTextureClassID);
	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
	MAXTexture::SetChannels(
		const DynamicArrayOf<BYTE> *red,
		const DynamicArrayOf<BYTE> *green,
		const DynamicArrayOf<BYTE> *blue,
		const DynamicArrayOf<BYTE> *alpha
	)
{
	Check_Object(this);
	Verify(GetClassID() == MAXTextureClassID);
	Check_Object(red);
	Check_Object(green);
	Check_Object(blue);

	//
	//---------------------
	// Now copy the channel
	//---------------------
	//
	Verify(red->GetLength() == textureSize.x*textureSize.y);
	redChannel = *red;
	redDepth = 8;

	Verify(green->GetLength() == textureSize.x*textureSize.y);
	greenChannel = *green;
	greenDepth = 8;

	Verify(blue->GetLength() == textureSize.x*textureSize.y);
	blueChannel = *blue;
	blueDepth = 8;

	if (alpha)
	{
		Check_Object(alpha);
		alphaChannel = *alpha;
		alphaDepth = 8;
	}
	else
	{
		alphaChannel.SetLength(0);
		alphaDepth = 0;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXTexture::ReadTexture()
{
	Check_Object(this);
	Check_Pointer(proxiedScene);
	BitmapTex *bitmap = proxiedScene->bmpList.GetBitmap(textureIndex);
	Check_Pointer(bitmap);


	Bitmap *bitmapinfo = bitmap->GetBitmap(proxiedScene->GetTime());
	if (!bitmapinfo) return;
	
	Check_Pointer(bitmapinfo);	
	int width = bitmapinfo->Width();
	int height = bitmapinfo->Height();
	int num_pixels = width*height;
	int current_pixel = 0;

	bool has_alpha = false;
	Bitmap *map = bitmap->GetBitmap(proxiedScene->GetTime());
	if (map->HasAlpha())
	{
		has_alpha = true;	
	}

	//
	// set the lenght of the arrays to hold the 
	// color pixels
	//
	redChannel.SetLength(num_pixels);
	greenChannel.SetLength(num_pixels);
	blueChannel.SetLength(num_pixels);
	if (has_alpha)
	{
		alphaChannel.SetLength(num_pixels);
	}
	else
	{
		alphaChannel.SetLength(0);
	}

	//
	// get the pixel data from the bitmap and copy the data
	// to the array
	//
	BMM_Color_32 pixel_32;
	BMM_Color_64 pixel_64;
	int start_height = height-1;
	int start_width  = width-1;
	for (int h=0;h<height;h++)
	{
		for (int w=0;w<width;w++)
		{
			int error = bitmapinfo->GetPixels(w,h,1,&pixel_64);

			pixel_32.r = (BYTE)(pixel_64.r >> 8);
			pixel_32.g = (BYTE)(pixel_64.g >> 8);
			pixel_32.b = (BYTE)(pixel_64.b >> 8);
			if (has_alpha)
			{
				pixel_32.a = (BYTE)(pixel_64.a >> 8);
			}
	
			redChannel[current_pixel]	= pixel_32.r;
			greenChannel[current_pixel]	= pixel_32.g;
			blueChannel[current_pixel]	= pixel_32.b;
			if (has_alpha)
			{
				alphaChannel[current_pixel]	= pixel_32.a;
			}

			current_pixel++;
		}
	}

	if (!redDepth)
	{
		GetChannelDepth(&redDepth,
						&blueDepth,
						&greenDepth,
						&alphaDepth
		);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool MAXTexture:: GetFormat(Proxies::TextureProxy::FormatType* format)
{
	Check_Object(this);

	if (0==formatHint)
	{
		formatHint=1;
		Stuff::MString name;
		GetName(&name);
		
		MAXTextureLibrary *library =
			Cast_Object(MAXTextureLibrary*, GetTextureLibrary());
		Check_Object(library);

		MAXStateLibrary *max_states = library->GetStateLibrary();
		Check_Object(max_states);
		MAXScene *scene = max_states->GetSceneProxy();

		Page *page = scene->FindHint(name);
		if (page)
		{
			const char *entry;
			if (page->GetEntry("Format",&entry))
			{
				if (stricmp(entry,"Alpha")==0)
				{
					textureFormat = AlphaTexture;
					formatHint++;
				}
				else if (stricmp(entry,"Keyed")==0)
				{
					textureFormat = KeyedTexture;
					formatHint++;
				}
			}
		}
	}

	if (formatHint)
	{
		if (formatHint>1)
		{
			*format = textureFormat;
			return true;
		}
	}

	return false;
}

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

//
//############################################################################
//############################    MAXTexture    ##############################
//############################################################################
//

MAXTextureLibrary::ClassData*
	MAXTextureLibrary::DefaultData = NULL;

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXTextureLibrary::MAXTextureLibrary(MAXStateLibrary *states):
	Proxies::TextureLibrary(DefaultData, states)
{
	Check_Pointer(this);

	MAXScene *scene = states->GetSceneProxy();
	Check_Pointer(scene);
	textureCount = scene->GetBitmapCount();
	textureProxies.SetLength(textureCount);
	lastTextureSearched = -1;
	MString name;
	for (unsigned i=0; i<textureCount; ++i)
	{
		textureProxies[i] = MAXTexture::MakeProxy(this, scene, i);
		Register_Object(textureProxies[i]);
	}
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXTextureLibrary::~MAXTextureLibrary()
{
	Check_Object(this);
	Verify(activeTextureProxies.IsEmpty());
	for (unsigned i=0; i<textureCount; i++)
	{
		Unregister_Object(textureProxies[i]);
		textureProxies[i]->Destroy();
	}
}

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::TextureProxy*
	MAXTextureLibrary::UseMatchingTextureProxy(Proxies::TextureProxy *texture)
{
	Check_Object(this);
	Check_Object(texture);
	//
	//--------------------------------------------------
	// Look to see if the last texture we found matches
	//--------------------------------------------------
	//
	MAXTexture *proxy;
	if (lastTextureSearched != -1)
	{
		proxy = textureProxies[lastTextureSearched];
		if (proxy->IsEqualTo(texture))
		{
Return_Proxy:
			proxy->AttachReference();
			return proxy;
		}
	}

	//
	//-------------------------------------------------------------------------
	// Spin through our textures and check each one for a match.  Skip the one
	// we have already checked
	//-------------------------------------------------------------------------
	//
	int i;
	unsigned count = GetTextureCount();
	for (i=0; i<count; ++i)
	{
		if (i == lastTextureSearched)
			continue;
		proxy = textureProxies[i];
		if (proxy->IsEqualTo(texture))
		{
			lastTextureSearched = i;
			goto Return_Proxy;
		}
	}
	// Didn't find the texture so we have to add it
	MAXScene *scene = GetStateLibrary()->GetSceneProxy();
	i = textureCount;
	textureCount++;
	textureProxies.SetLength(textureCount);
	lastTextureSearched = -1;
	MString name;
	proxy = textureProxies[i] = MAXTexture::MakeProxy(this, scene, i);

	Register_Object(textureProxies[i]);
	if (proxy)
		goto Return_Proxy;
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::TextureProxy*
	MAXTextureLibrary::UseTextureProxy(const char* texture_name)
{
	return NULL;
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXTextureLibrary::CloseLibrary()
{
	Check_Object(this);
	unsigned count = GetTextureCount();
	for (unsigned i=0; i<count; ++i)
	{
		Verify(textureProxies[i]->GetReferenceCount() == 1);
		textureProxies[i]->DetachReference();
	}
	Verify(GetReferenceCount() == 1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXTexture*
	MAXTextureLibrary::UseTextureProxy(int index)
{
	Check_Object(this);

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

	// if this is just the next texture in the list and we haven't created it yet, then create it
	if (index == textureCount)
	{
		MAXScene *scene = GetStateLibrary()->GetSceneProxy();
		textureCount++;
		textureProxies.SetLength(textureCount);
		lastTextureSearched = -1;
		textureProxies[index] = MAXTexture::MakeProxy(this, scene, index);
		Register_Object(textureProxies[index]);
	}

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

