/*
	File	:	texture.cpp
	Author	:	Richard Flower (c) 1998 Core Design
	----------------------------------------------------------------------------------
	Functions
	----------------------------------------------------------------------------------

	DXTextureInit				-	Initialise Texture Lists
	DXTextureFindPaletteSlot	-	Find A Free Palette Slot  
	DXTextureFreePalette		-	Free Palettes
	DXTextureFreeAllPalettes	-	Free All Palettes
	DXTextureGetFreeMemory		-	Get Free Texture Memory
	DXTextureGetInterface		-	Get Texture Interface
	DXTextureFindTextureSlot	-	Find A Free Texture Slot


			
	----------------------------------------------------------------------------------
*/


#include "standard.h"
#include "global.h"

#include "winmain.h"
#include "texture.h"
#include "hwrender.h"

DXTEXTURE DXTextureList[MAX_D3D_TEXTURES];
IDirectDrawPalette* DXPaletteList[MAX_D3D_PALETTES];

bool DXTextureGreyScale = false;


void DXTextureSetGreyScale(bool v)
{
	DXTextureGreyScale = v;
}


/*
	Function	:	DXTextureInit
	Useage		:	Initialise Texture Lists
*/

bool DXTextureInit(DXTEXTURE TextureList[],IDirectDrawPalette* PaletteList[])
{
	if(TextureList != NULL)
		Zero(TextureList);
	if(PaletteList != NULL)
		Zero(PaletteList);
	return true;
}

// EOF : DXTextureInit



/*
	Function	:	DXTextureFindPaletteSlot
	Useage		:	Find A Free Palette Slot
*/

int DXTextureFindPaletteSlot()
{
	for(int nSlot=0;nSlot<ArraySize(DXPaletteList);++nSlot)
		if (DXPaletteList[nSlot]==0)
			return nSlot;
	return -1;
}

// EOF  : DXTextureFindPaletteSlot



/*
	Function	:	DXTextureNewPalette
	Useage		:	Create A New Texture Palette
*/

int DXTextureNewPalette(unsigned char* pPal)
{
	int nSlot=DXTextureFindPaletteSlot();
	if(nSlot<0)
	{
		Log(LT_Error,"Out of palette slots");
		return -1;
	}
	
	DWORD adwPal[256];
	
	for(int nEntry=0;nEntry<256;++nEntry,pPal+=3)
		adwPal[nEntry]=RGB(pPal[0],pPal[1],pPal[2]);

	if(DX_TRY(App.lpDD->CreatePalette(DDPCAPS_8BIT|DDPCAPS_ALLOW256,(PALETTEENTRY*)adwPal,DXPaletteList+nSlot,0)))
		return -1;

	return nSlot;
}

// EOF : DXTextureNewPalette



/*
	Function	:	DXTextureFreePalette
	Useage		:	Free Palettes
*/


void DXTextureFreePalette(int nHandle)
{
	if (nHandle<0)
	{
		Log(LT_Error,"Tried to free invalid palette");
		return;
	}

	RELEASE_LOGTYPE(DXPaletteList[nHandle],LT_Texture);
}

// EOF : DXTextureFreePalette



/*
	Function	:	DXTextureFreeAllPalettes
	Useage		:	Free All Palettes
*/

void DXTextureFreeAllPalettes()
{
	for(int nSlot=0;nSlot<ArraySize(DXPaletteList);++nSlot)
		if(DXPaletteList[nSlot])
			RELEASE_LOGTYPE(DXPaletteList[nSlot],LT_Texture);
}

// EOF : DXTextureFreeAllPalettes




/*
	Function	:	DXTextureGetFreeMemory
	Useage		:	Get Free Texture Memory
*/

DWORD DXTextureGetFreeMemory()
{
	DDSCAPS caps;
	caps.dwCaps=DDSCAPS_TEXTURE;
	DWORD dwTotal,dwFree;
	App.lpDD->GetAvailableVidMem(&caps,&dwTotal,&dwFree);
	return dwFree;
}

// EOF : DXTextureGetFreeMemory


/*
	Function	:	DXTextureGetInterface
	Useage		:	Get Texture Interface
*/

IDirect3DTexture2* DXTextureGetInterface(IDirectDrawSurface3* pSurf)
{
	IDirect3DTexture2* pTexture;
    if (DX_TRY(pSurf->QueryInterface(IID_IDirect3DTexture2,(void**)&pTexture)))
		return 0;
	return pTexture;
}

// EOF : DXTextureGetInterface




/*
	Function	:	DXTextureFindTextureSlot
	Useage		:	Find Free Texture Slot
*/

int DXTextureFindTextureSlot(DXTEXTURE TextureList[])
{
	for(int nSlot=0;nSlot<MAX_D3D_TEXTURES;++nSlot)
		if(!(TextureList[nSlot].dwFlags&TX_FLAG_SLOTUSED))
			return nSlot;
	return -1;
}

// EOF : DXTextureFindTextureSlot



bool DXTextureMakeSystemSurface(DXTEXTURE& rTexture)
{
	DDSURFACEDESC ddsd;
	DXInit(ddsd);
    ddsd.dwFlags		= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT;
	ddsd.ddsCaps.dwCaps	= DDSCAPS_SYSTEMMEMORY|DDSCAPS_TEXTURE;
	ddsd.dwWidth		= rTexture.nWidth;
	ddsd.dwHeight		= rTexture.nHeight;
	ddsd.ddpfPixelFormat= DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).ddsd.ddpfPixelFormat;
	if (DX_TRY(DD_CreateSurface(ddsd,rTexture.pSystemSurface)))
	{
		Log("TX_MakeSystemSurface: Can't create surface");
		return false;
	}
	if (rTexture.pPalette)
	{
		if (DX_TRY(rTexture.pSystemSurface->SetPalette(rTexture.pPalette)))
		{
			Log("TX_MakeSystemSurface: Can't attach palette");
			return false;
		}
	}

	return true;
}


bool DXTextureMakeDeviceSurface(DXTEXTURE& rTexture)
{
	DDSURFACEDESC ddsd;
	DXInit(ddsd);
    ddsd.dwFlags		=DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT;
	ddsd.ddsCaps.dwCaps	=DDSCAPS_VIDEOMEMORY|DDSCAPS_TEXTURE|DDSCAPS_ALLOCONLOAD;
	ddsd.dwWidth		=rTexture.nWidth;
	ddsd.dwHeight		=rTexture.nHeight;
	ddsd.ddpfPixelFormat= DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).ddsd.ddpfPixelFormat;
	
	if (DX_TRY(DD_CreateSurface(ddsd,rTexture.pDeviceSurface)))
	{
		Log(LT_Error,"TX_MakeDeviceSurface: Can't create surface");
		return false;
	}

	if (rTexture.pPalette)
	{
		if (DX_TRY(rTexture.pDeviceSurface->SetPalette(rTexture.pPalette)))
		{
			Log(LT_Error,"TX_MakeDeviceSurface: Can't attach palette");
			RELEASE_LOGTYPE(rTexture.pDeviceSurface,LT_Texture);
			return false;
		}
	
		DDCOLORKEY ddck;
		ddck.dwColorSpaceLowValue=0;
		ddck.dwColorSpaceHighValue=0;
		if (DX_TRY(rTexture.pDeviceSurface->SetColorKey(DDCKEY_SRCBLT,&ddck)))
		{
			Log(LT_Error,"Can't set colorkey");
			RELEASE_LOGTYPE(rTexture.pDeviceSurface,LT_Texture);
			return false;
		}
	}
	
	rTexture.pTexture=DXTextureGetInterface(rTexture.pDeviceSurface);
	
	if (!rTexture.pTexture)
	{
		Log(LT_Error,"Can't get texture interface");
		RELEASE_LOGTYPE(rTexture.pDeviceSurface,LT_Texture);
		return false;
	}
	
	if (DX_TRY(rTexture.pTexture->GetHandle(App.lpD3DDevice,&rTexture.hTexture)))
	{
		Log(LT_Error,"Can't get texture handle");
		RELEASE_LOGTYPE(rTexture.pTexture,LT_Texture);
		RELEASE_LOGTYPE(rTexture.pDeviceSurface,LT_Texture);
		rTexture.hTexture=0;
		return false;
	}
	
	return true;
}

void DXTextureReleaseDeviceSurface(DXTEXTURE& rTexture)
{
	HWR_ResetCurrentTexture();
	rTexture.hTexture=0;
	RELEASE_LOGTYPE(rTexture.pTexture,LT_Texture);
	RELEASE_LOGTYPE(rTexture.pDeviceSurface,LT_Texture);
}



int DXTextureNew(int nWidth,int nHeight,IDirectDrawPalette* pPalette,DXTEXTURE TextureList[])
{
	int nSlot;
	
	nSlot = DXTextureFindTextureSlot(TextureList);
	if(nSlot<0)
	{
		Log(LT_Error,"Out of texture slots");
		return -1;
	}
	
	DXTEXTURE& rTexture=TextureList[nSlot];
	Zero(rTexture);
	
	rTexture.dwFlags=TX_FLAG_SLOTUSED;
	rTexture.nWidth=nWidth;
	rTexture.nHeight=nHeight;
	rTexture.pPalette=pPalette;

	if (!DXTextureMakeSystemSurface(rTexture))
	{
		Log(LT_Error,"Can't make system texture surface");
		return -1;
	}

	if(DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bHardware)	
	{
		if (DXTextureMakeDeviceSurface(rTexture))
			Log(LT_Info,"Added texture in slot %d; Texture mem free:%x; D3D handle=%d",nSlot,DXTextureGetFreeMemory(),rTexture.hTexture);
		else
			Log(LT_Warning,"Texture %d has no device surface",nSlot);
	}
	else
	{
		rTexture.pDeviceSurface = rTexture.pSystemSurface;
		rTexture.pTexture		= DXTextureGetInterface(rTexture.pDeviceSurface);
		rTexture.pTexture->GetHandle(App.lpD3DDevice,&rTexture.hTexture);
		rTexture.pSystemSurface->AddRef();	
	}

	return nSlot;
}

void DXTextureCleanup(int nSlot,DXTEXTURE TextureList[])
{
	DXTEXTURE& rTexture=TextureList[nSlot];
	DXTextureReleaseDeviceSurface(rTexture);
	RELEASE_LOGTYPE(rTexture.pSystemSurface,LT_Texture);
	
	if(rTexture.pSoftwareSurface != NULL) 
	{
		free(rTexture.pSoftwareSurface);
		rTexture.pSoftwareSurface = 0;
	}
	rTexture.dwFlags=0;

}

void DXTextureFree(int nHandle,DXTEXTURE TextureList[])
{
	if (nHandle<0)
	{
		Log(LT_Error,"Tried to cleanup invalid texture");
		return;
	}

	if (TextureList[nHandle].dwFlags&TX_FLAG_SLOTUSED)
		DXTextureCleanup(nHandle,TextureList);
}


void DXTextureFreeAll(DXTEXTURE TextureList[])
{
	for(int nSlot=0;nSlot<MAX_D3D_TEXTURES;++nSlot)
		if(TextureList[nSlot].dwFlags&TX_FLAG_SLOTUSED)
			DXTextureCleanup(nSlot,TextureList);
}

bool DXTextureRestore(int nHandle,bool tForce,DXTEXTURE TextureList[])
{
	Log("DXTexture Restore : Entry...");
		
	if(nHandle<0)
	{
		Log(LT_Error,"Tried to restore invalid texture");
		return false;
	}
	
	DXTEXTURE& rTexture=TextureList[nHandle];
	
	if(rTexture.pDeviceSurface == rTexture.pSystemSurface) return true;


	if(tForce || (!rTexture.pDeviceSurface) || (DX_TRY(DD_EnsureSurfaceAvailable(rTexture.pDeviceSurface))))
	{
		DXTextureReleaseDeviceSurface(rTexture);
		if(!DXTextureMakeDeviceSurface(rTexture))
		{
			Log(LT_Error,"Texture device surface lost; can't recreate (%d)",nHandle);
			return false;
		}
		Log("Re-created device surface for texture %d; D3D Handle=%d",nHandle,rTexture.hTexture);
	}
	
	DX_TRY(DD_EnsureSurfaceAvailable(rTexture.pSystemSurface));
	
	IDirect3DTexture2* pSrc;
	pSrc=DXTextureGetInterface(rTexture.pSystemSurface);
	
	if (!pSrc)
	{
		Log("DXTextureRestore(%d): can't get src iface",nHandle);
		return false;
	}

	if (rTexture.pTexture->Load(pSrc) != D3D_OK)
	{
		Log("DXTextureRestore(%d): can't load texture",nHandle);
		RELEASE_NOLOG(pSrc);
		return false;
	}
	
	RELEASE_NOLOG(pSrc);
	
	return true;
}



bool DXTextureRestoreAll(bool tForce,DXTEXTURE TextureList[])
{
	Log(LT_Enter,"DXTextureRestoreAll");
	bool tSuccess=true;
	for(int nSlot=0;nSlot<MAX_D3D_TEXTURES;++nSlot)
		if(TextureList[nSlot].dwFlags&TX_FLAG_SLOTUSED)
			tSuccess&=DXTextureRestore(nSlot,tForce,TextureList);

	return tSuccess;
}

DWORD DXTextureGetHandle(int nHandle,DXTEXTURE TextureList[])
{
	if(nHandle<0)
	{
		Log(LT_Error,"Tried to get handle of invalid texture");
		return 0;
	}

	DXTEXTURE& rTexture= TextureList[nHandle];
	
	if(rTexture.pDeviceSurface && (rTexture.pDeviceSurface->IsLost()==DDERR_SURFACELOST))
		DXTextureRestore(nHandle,true,TextureList);
	
	return (DWORD)rTexture.hTexture;
}


int DXTextureAddPal(int nWidth,int nHeight,uint8* pTexture,int nPaletteHandle,DXTEXTURE TextureList[])
{
	if(nPaletteHandle<0)
	{
		Log(LT_Error,"DXTextureAddPal: invalid palette handle");
		return -1;
	}
	
	int nHandle=DXTextureNew(nWidth,nHeight,DXPaletteList[nPaletteHandle],TextureList);
	
	if(nHandle<0)
	{
		Log(LT_Error,"DXTextureAddPal: can't create new texture");
		return -1;
	}
	
	DXTEXTURE& rTexture=TextureList[nHandle];

	DDSURFACEDESC ddsd;
	if (DX_FAILED(DD_LockSurface(rTexture.pSystemSurface,ddsd)))
		return -1;
	
	
	
	int nPitch=ddsd.lPitch;
	char* pDest=(char*)ddsd.lpSurface;
	for (int nY=nHeight;nY--;)
	{
		memcpy(pDest,pTexture,nWidth);
		pTexture+=nWidth;
		pDest+=nPitch;
	}
	
	/*
	int nPitch=ddsd.lPitch;

	char* pDest=(char*)ddsd.lpSurface;
	uint8* pTexPtr = pTexture;	

	for(int y = 0;y<nHeight;y++)
	{
		for(int x = 0;x<nWidth;x++)
		{
			int offset;
			offset = (((x&0xf8)<<2)+(x&7))+((y&0xfc)<<8)+((y&3)<<3);
			*(pDest+offset) = *(pTexture++);	
		}
	}
	*/

	DD_UnlockSurface(rTexture.pSystemSurface,ddsd);

	DXTextureRestore(nHandle,true,TextureList);

	return nHandle;
}

int DXTextureAdd(int nWidth,int nHeight,uint16* pTexture,DXTEXTURE TextureList[],int BitFormat)
{
	int nHandle = DXTextureNew(nWidth,nHeight,0,TextureList);

	Log("DXTextureAdd : Create Texture");

	if (nHandle<0)
	{
		Log(LT_Error,"DXTextureAdd : Can't create new texture");
		return -1;
	}
	
	DXTEXTURE& rTexture=TextureList[nHandle];
	
	DDSURFACEDESC ddsd;
	
	if (DX_FAILED(DD_LockSurface(rTexture.pSystemSurface,ddsd)))
		return -1;
	
	int nPitch=ddsd.lPitch;
	
	char* pDest=(char*) ddsd.lpSurface;
	
	for(int nY=nHeight;nY--;)
	{
		uint8* pDest2 = (uint8*) pDest;

		for(int nX=nWidth;nX--;)
		{
			uint16 wPixel=*pTexture++;

			int bA,bR,bG,bB;
			
			switch(BitFormat)
			{
				case 555:
					bA=((sint16)wPixel)>>15;
					bR=(wPixel>>7)&0xf8;
					bG=(wPixel>>2)&0xf8;
					bB=(wPixel<<3)&0xf8;
					break;
				case 565:
					bA=0;
					bR=(wPixel>>8)&0xf8;
					bG=(wPixel>>3)&0xf8;
					bB=(wPixel<<3)&0xf8;
					break;
			}
			
			if(DXTextureGreyScale)
			{
				bR = bB;
				bG = bB;
			}

			DWORD dwRGBA;
				
			if(DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha)
			{
				dwRGBA = DXTEXTURERGBA(App.DeviceInfoPtr,App.DXConfigPtr,bR,bG,bB,bA);
			}
			else
				dwRGBA = DXTEXTURERGB(App.DeviceInfoPtr,App.DXConfigPtr,bR,bG,bB);
				
			
			for(int nBits=DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).bpp;nBits>0;nBits-=8)
			{
				*pDest2++=(uint8)dwRGBA;
				dwRGBA>>=8;
			}

		}
		pDest+=nPitch;
	}

	// Copy Texture To Our Buffer

	if(!DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bHardware)
	{
		rTexture.pSoftwareSurface = (unsigned short*)malloc(sizeof(short)*256*256);
		memcpy((char*)rTexture.pSoftwareSurface,ddsd.lpSurface,sizeof(short)*256*256);
	}

	if(!DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha)	
	{
		DDCOLORKEY ck;
		ck.dwColorSpaceLowValue  = 0;
		ck.dwColorSpaceHighValue = 0;
		rTexture.pSystemSurface->SetColorKey(DDCKEY_SRCBLT,&ck);	
	}	
		
	DD_UnlockSurface(rTexture.pSystemSurface,ddsd);

	if(DXTextureRestore(nHandle,true,TextureList)==false)
		Log("DXTextureAdd : Failed To Restore Texture");

	return nHandle;
}



void DXTextureFinish(DXTEXTURE TextureList[])
{
	Log(LT_Enter,"DXTextureFinish");
	DXTextureFreeAll(TextureList);
	DXTextureFreeAllPalettes();
}


