#include "ProxyHeaders.hpp"
#include <imagelib\image.h>
#include <GameOs\PNG\png.h>
#include <GameOS\ToolOS.hpp>

//
//############################################################################
//###########################    TextureProxy    ############################
//############################################################################
//

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

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

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			TextureProxyClassID,
			"TextureProxy",
			GenericProxy::DefaultData
		);
	Register_Object(DefaultData);
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy::TextureProxy(
	ClassData *class_data,
	TextureLibrary *library
):
	GenericProxy(class_data),
	libraryProxy(library)
{
	Check_Pointer(this);
	Check_Object(libraryProxy);
	libraryProxy->AttachTextureProxy(this);
	textureSize.x = textureSize.y = 0;
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy::TextureProxy(
	const char* name,
	const Vector2DOf<int> &size,
	TextureLibrary *library
):
	GenericProxy(DefaultData),
	libraryProxy(library),
	textureName(name),
	textureSize(size)
{
	Check_Pointer(this);
	Verify(GetClassID() == TextureProxyClassID);
	redDepth = greenDepth = blueDepth = alphaDepth = 0;
	libraryProxy->AttachReference();
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy::~TextureProxy()
{
	Check_Object(this);
	if (libraryProxy)
	{
		Check_Object(libraryProxy);
		libraryProxy->DetachReference();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::Destroy()
{
	Check_Object(this);
	Verify(referenceCount == 1);
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	TextureProxy::IsEqualTo(TextureProxy *texture)
{
	Check_Object(this);
	Check_Object(texture);

	//
	//-----------------------------------------
	// Make sure we aren't comparing to ourself
	//-----------------------------------------
	//
	if (this == texture)
		return true;

	//
	//------------------
	// Compare the names
	//------------------
	//
	MString filename_1;
	if (GetName(&filename_1))
	{
		MString filename_2;
		if (texture->GetName(&filename_2))
			return filename_1 == filename_2;
		return false;
	}
	return !texture->GetName(&filename_1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	TextureProxy::UseNextTextureProxyInLibrary()
{
	Check_Object(this);
	Verify(GetClassID() == TextureProxyClassID);
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureProxy*
	TextureProxy::UsePreviousTextureProxyInLibrary()
{
	Check_Object(this);
	Verify(GetClassID() == TextureProxyClassID);
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	TextureProxy::GetName(MString *filename)
{
	Check_Object(this);
	Check_Pointer(filename);
	Verify(GetClassID() == TextureProxyClassID);

	//
	//------------------------
	// Set the name and return
	//------------------------
	//
	*filename = textureName;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::SetName(const char* filename)
{
	Check_Object(this);
	Check_Pointer(filename);
	Verify(GetClassID() == TextureProxyClassID);
	textureName = filename;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::GetImageSize(Vector2DOf<int> *size)
{
	Check_Object(this);
	Check_Pointer(size);
	Verify(GetClassID() == TextureProxyClassID);
	*size = textureSize;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::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);
	Verify(GetClassID() == TextureProxyClassID);

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::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);
	Verify(GetClassID() == TextureProxyClassID);

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::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);

	//
	//---------------------
	// 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
	TextureProxy::SaveAsTarga(const char* filename)
{
	Check_Object(this);
	Check_Pointer(filename);

	//
	//---------------------------------------------
	// Get the size of the image we will be copying
	//---------------------------------------------
	//
	Vector2DOf<int> size;
	GetImageSize(&size);
	Verify(size.x > 0 && size.y > 0);

	//
	//----------------------------
	// Analyze the type of texture
	//----------------------------
	//
	unsigned
		red_depth,
		green_depth,
		blue_depth,
		alpha_depth;
	GetChannelDepth(
		&red_depth,
		&green_depth,
		&blue_depth,
		&alpha_depth
	);
	Verify(red_depth == 8 && green_depth == 8 && blue_depth == 8);
	Verify(!alpha_depth || alpha_depth == 8);
	unsigned channel_size = size.x * size.y;

	if (::gos_FileReadOnly(filename))
	{
		MString str;
		str = MString(filename) + " is READ-ONLY.  Remove the Read-Only permission and save?";
		if (IDNO == MessageBox(NULL,str,"Save TGA",MB_YESNO))
		{
			return;
		}
		::gos_FileSetReadWrite(filename);
	}

	//
	//----------------------------------------------------------------------
	// If this is an alpha texture, write out an alpha TGA after compositing
	// the colors for it
	//----------------------------------------------------------------------
	//
	DynamicArrayOf<BYTE> red;
	DynamicArrayOf<BYTE> green;
	DynamicArrayOf<BYTE> blue;
	DynamicArrayOf<BYTE> alpha;
	if (alpha_depth == 8)
	{
		GetChannels(&red, &green, &blue, &alpha);
		Verify(red.GetLength() == channel_size);
		Verify(green.GetLength() == channel_size);
		Verify(blue.GetLength() == channel_size);
		Verify(alpha.GetLength() == channel_size);
		DynamicArrayOf<TargaColorA> buffer(channel_size);
		for (int i=0; i<channel_size; ++i)
		{
			buffer[i].red = red[i];
			buffer[i].green = green[i];
			buffer[i].blue = blue[i];
			buffer[i].alpha = alpha[i];
		}
		WriteTargaFile(
			filename,
			size.x,
			size.y,
			buffer
		);
	}

	//
	//--------------------------------------------------------------------
	// This is not an alpha texture, so just write out a regular TGA after
	// compositing
	//--------------------------------------------------------------------
	//
	else
	{
		GetChannels(&red, &green, &blue, NULL);
		Verify(red.GetLength() == channel_size);
		Verify(green.GetLength() == channel_size);
		Verify(blue.GetLength() == channel_size);
		DynamicArrayOf<TargaColor> buffer(channel_size);
		for (int i=0; i<channel_size; ++i)
		{
			buffer[i].red = red[i];
			buffer[i].green = green[i];
			buffer[i].blue = blue[i];
		}
		WriteTargaFile(
			filename,
			size.x,
			size.y,
			buffer
		);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::ReadTarga(
		const char* filename,
		unsigned *red_depth,
		unsigned *blue_depth,
		unsigned *green_depth,
		unsigned *alpha_depth,
		Vector2DOf<int> *size,
		DynamicArrayOf<BYTE> *red,
		DynamicArrayOf<BYTE> *green,
		DynamicArrayOf<BYTE> *blue,
		DynamicArrayOf<BYTE> *alpha
	)
{
	Check_Object(this);
	Check_Pointer(filename);

	//
	//--------------
	// Open the file
	//--------------
	//
	FileStream targa_file(filename);
	if (!targa_file.IsFileOpened())
	{
		size->x = size->y = 0;
		*red_depth = *green_depth = *blue_depth = *alpha_depth = 0;
		return;
	}

	//
	//----------------------
	// Read the targa header
	//----------------------
	//
	TargaHeader header;
	ReadTargaHeader(
		&header,
		&targa_file,
		&size->x,
		&size->y,
		red_depth,
		green_depth,
		blue_depth,
		alpha_depth
	);
	Verify(size->x > 0 && size->y > 0);
	Verify(*red_depth == 8 && *green_depth == 8 && *blue_depth == 8);
	Verify(!*alpha_depth || *alpha_depth == 8);

	//
	//------------------------
	// Set the channel lengths
	//------------------------
	//
	unsigned channel_size = size->x * size->y;
	red->SetLength(channel_size);
	green->SetLength(channel_size);
	blue->SetLength(channel_size);

	//
	//----------------------------------------------
	// If this is an alpha texture, read it that way
	//----------------------------------------------
	//
	if (*alpha_depth > 0)
	{
		alpha->SetLength(channel_size);
		DynamicArrayOf<TargaColorA> raw(channel_size);
		ReadTargaChannels(&targa_file, &header, &raw);
		for (int i=0; i<channel_size; ++i)
		{
			(*red)[i] = raw[i].red;
			(*green)[i] = raw[i].green;
			(*blue)[i] = raw[i].blue;
			(*alpha)[i] = raw[i].alpha;
		}
	}

	//
	//-----------------------------
	// This is not an alpha texture
	//-----------------------------
	//
	else
	{
		DynamicArrayOf<TargaColor> raw(channel_size);
		ReadTargaChannels(&targa_file, &header, &raw);
		for (int i=0; i<channel_size; ++i)
		{
			(*red)[i] = raw[i].red;
			(*green)[i] = raw[i].green;
			(*blue)[i] = raw[i].blue;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	TextureProxy::ReadPNG(
		const char* filename,
		unsigned *red_depth,
		unsigned *blue_depth,
		unsigned *green_depth,
		unsigned *alpha_depth,
		Vector2DOf<int> *size,
		DynamicArrayOf<BYTE> *red,
		DynamicArrayOf<BYTE> *green,
		DynamicArrayOf<BYTE> *blue,
		DynamicArrayOf<BYTE> *alpha
	)
{
	Check_Object(this);
	Check_Pointer(filename);

	//
	//--------------
	// Open the file
	//--------------
	//
	if (!::gos_DoesFileExist(filename))
	{
		size->x = size->y = 0;
		*red_depth = *green_depth = *blue_depth = *alpha_depth = 0;
		return;
	}

	Image image;
	image.Load((char *)filename);

	size->x = image.GetWidth();
	size->y = image.GetHeight();
	*red_depth = 8;
	*green_depth = 8;
	*blue_depth = 8;

	/* default mask in Image::CreateBlank has red in the most significant byte */
	/* so make sure here the right mask is set */
	RGBMask mask;
	if(image.GetBpp()==32)
	{
		mask.Set(0x0000ff,0x00ff00,0xff0000); 
		*alpha_depth = 8;
	}
	else {
		mask.Set(0x000000ff,0x0000ff00,0x00ff0000,0xff000000); 
		*alpha_depth = 0;
	}

	image.MaskTo(mask);

	Verify(size->x > 0 && size->y > 0);
	Verify(*red_depth == 8 && *green_depth == 8 && *blue_depth == 8);
	Verify(!*alpha_depth || *alpha_depth == 8);

	//
	//------------------------
	// Set the channel lengths
	//------------------------
	//
	unsigned channel_size = size->x * size->y;
	red->SetLength(channel_size);
	green->SetLength(channel_size);
	blue->SetLength(channel_size);

	//
	//----------------------------------------------
	// If this is an alpha texture, read it that way
	//----------------------------------------------
	//
	BYTE *dat = (BYTE *)image.Lock();
	if (*alpha_depth > 0)
	{
		alpha->SetLength(channel_size);
		for (int i=0; i<channel_size; ++i)
		{
			(*red)[i] = *(dat+i*4+0);
			(*green)[i] = *(dat+i*4+1);
			(*blue)[i] = *(dat+i*4+2);
			(*alpha)[i] = *(dat+i*4+3);
		}
	}

	//
	//-----------------------------
	// This is not an alpha texture
	//-----------------------------
	//
	else
	{
		for (int i=0; i<channel_size; ++i)
		{
			(*red)[i] = *(dat+i*3+0);
			(*green)[i] = *(dat+i*3+1);
			(*blue)[i] = *(dat+i*3+2);
		}
	}
	image.UnLock();
}


/*******************************************************************************
/* function name: SaveAsPng
/* description: 
/*******************************************************************************/
void TextureProxy::SaveAsPng(const char* filename)
{
	Check_Object(this);
	Check_Pointer(filename);

	//
	//---------------------------------------------
	// Get the size of the image we will be copying
	//---------------------------------------------
	//
	Vector2DOf<int> size;
	GetImageSize(&size);
	Verify(size.x > 0 && size.y > 0);

	//
	//----------------------------
	// Analyze the type of texture
	//----------------------------
	//
	unsigned
		red_depth,
		green_depth,
		blue_depth,
		alpha_depth;
	GetChannelDepth(
		&red_depth,
		&green_depth,
		&blue_depth,
		&alpha_depth
	);
	Verify(red_depth == 8 && green_depth == 8 && blue_depth == 8);
	Verify(!alpha_depth || alpha_depth == 8);
	unsigned channel_size = size.x * size.y;

	/* default mask in Image::CreateBlank has red in the most significant byte */
	/* so make sure here the right mask is set */
	RGBMask mask;
	if(alpha_depth==0)
		mask.Set(0x0000ff,0x00ff00,0xff0000); 
	else
		mask.Set(0x000000ff,0x0000ff00,0x00ff0000,0xff000000); 
	
	Image pngImage;
	pngImage.CreateBlank(size.x, size.y, ITYPE_RGB, alpha_depth?32:24, mask);

	BYTE *dat=(BYTE *)pngImage.Lock();
	
	//
	//----------------------------------------------------------------------
	// If this is an alpha texture, write out RGBA
	//----------------------------------------------------------------------
	//
	DynamicArrayOf<BYTE> red;
	DynamicArrayOf<BYTE> green;
	DynamicArrayOf<BYTE> blue;
	DynamicArrayOf<BYTE> alpha;
	if (alpha_depth == 8)
	{
		GetChannels(&red, &green, &blue, &alpha);
		Verify(red.GetLength() == channel_size);
		Verify(green.GetLength() == channel_size);
		Verify(blue.GetLength() == channel_size);
		Verify(alpha.GetLength() == channel_size);
		for (int i=0; i<channel_size; ++i)
		{
			*(dat+i*4+0) = red[i];
			*(dat+i*4+1) = green[i];
			*(dat+i*4+2) = blue[i];
			*(dat+i*4+3) = alpha[i];
		}
	}

	//
	//--------------------------------------------------------------------
	// This is not an alpha texture, so just write out RGB
	//--------------------------------------------------------------------
	//
	else
	{
		GetChannels(&red, &green, &blue, NULL);
		Verify(red.GetLength() == channel_size);
		Verify(green.GetLength() == channel_size);
		Verify(blue.GetLength() == channel_size);
		for (int i=0; i<channel_size; ++i)
		{
			*(dat+i*3+0) = red[i];
			*(dat+i*3+1) = green[i];
			*(dat+i*3+2) = blue[i];
		}
	}

	pngImage.UnLock();
	if (::gos_FileReadOnly(filename))
	{
		MString str;
		str = MString(filename) + " is READ-ONLY.  Remove the Read-Only permission and save?";
		if (IDNO == MessageBox(NULL,str,"Save PNG",MB_YESNO))
		{
			return;
		}
		::gos_FileSetReadWrite(filename);
	}
	pngImage.SavePng((char* )filename);
}


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

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

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

//
//############################################################################
//######################    TextureLibrary    ##########################
//############################################################################
//

TextureLibrary::ClassData*
	TextureLibrary::DefaultData = NULL;

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TextureLibrary::TextureLibrary(
	ClassData *class_data,
	StateLibrary *state_library
):
	GenericProxy(class_data),
	stateLibrary(state_library),
	activeTextureProxies(NULL)
{
	Check_Object(this);
}

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

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