#include "Texture.h"

TextureMgr::TextureMgr() {

	IL_Blit = NULL;
	VS_Blit = NULL;
	PS_Blit = NULL;
	VB = IB = cb_VS = cb_PS = NULL;

	//create texture catalog:
	List = new Texture *[512];
	ZeroMemory( List, 512*sizeof(Texture*) );
	ListMax = 512;
	Free = 0;

	//create global texture repository
	GList = new Texture *[128];
	GListMax = 128;
	GFree = 0;

//Command list:
	InQueue = 0;
	for( UINT j = 0; j < 1024; j++ ) {
		Queue[j].tex = NULL;
		Queue[j].counter = 0;		
	}

	CommandMax = 64;
	CommandCount = 0;
	CList = new Command [64];
	ZeroMemory( CList, sizeof(Command)*64 );

//Create texture buffer:
	InitBuffer();
}

TextureMgr::~TextureMgr() {
	REL( IL_Blit );
	REL( VS_Blit );
	REL( PS_Blit );
	REL( VB );
	REL( IB );
	REL( cb_VS );
	REL( cb_PS );
	ClearQueue();
	ClearBuffer();
	ClearRemainingTextures();
	DeleteGlobalTextures();
	delete [ ] List;
	delete [ ] CList;
	delete [ ] GList;
}

//=======================================================
//			TEXTURE BUFFER. [BLIT, FILL].
//=======================================================
#pragma region Buffer
void TextureMgr::InitBuffer() {
/*
	Buffer for Blit, Fill, CreateFromBitmap functions.
	"Buffer" textures must not be created when they needed. Texture creation is very slow. (300-500 mcrs)
	DG4 2D panel fps:
	130 with buffer.
	30 with texture creation during a fuction call.
*/
	DWORD Res = 32, j = 0;

	BufferCount = 5;
	Buffer = new BufferTexture [5];
	//32x32, 64x64, 128x128, 256x256, 512x512,
	while( Res < 600 ) {
		Buffer[j].dim = Res;
		Buffer[j].tex = new Texture( Res, Res );
	//	Buffer[j].tex->CreateSRV();
	//	Buffer[j].tex->CreateRTV();
		Res *= 2;
		j++;
	}
}

Texture *TextureMgr::FindBuffer( DWORD w, DWORD h ) {
//find the smallest buffer. Add a new buffer texture if requested size is not presented
	DWORD Res = 32, j = 0, dim;

	dim = max( w, h );
	if( dim > 32 ) {
		while( dim > Res ) {
			if( Res > 4096 ) {//probably some kind of failure
				oapiWriteLog( "Too big texture buffer !" );
				return NULL;
			}
			Res *= 2;
			j++;
		}
		if( j < BufferCount )
			return Buffer[j].tex;
		else {
			BufferTexture *nbuf = new BufferTexture [j+1];
			memcpy( nbuf, Buffer, BufferCount*sizeof(BufferTexture) );
			for( DWORD i = j; i >= BufferCount; i-- ) {
				nbuf[i].tex = new Texture( Res, Res );
				nbuf[i].tex->CreateSRV();
				nbuf[i].tex->CreateRTV();
				nbuf[i].dim = Res;
				Res /= 2;
			}
			delete [ ] Buffer;
			Buffer = nbuf;
			BufferCount = j+1;
			return Buffer[j].tex;
		}
	}
	else
		return Buffer[0].tex;
}

void TextureMgr::ClearBuffer() {
	for( DWORD j = 0; j < BufferCount; j++ )
		delete Buffer[j].tex;
	delete [ ] Buffer;
}
#pragma endregion
//=======================================================
//			GLOBAL TEXTURE BUFFER. ( & 8 flag in LoadTexture )
//=======================================================
#pragma region Global Textures
void TextureMgr::AddGlobalTexture( Texture *tex ) {
	if( GFree == GListMax ) {
		Texture **tmp = new Texture *[GListMax+64];
		memset( tmp, 0, sizeof(Texture*)*(GListMax+64) );
		memcpy( tmp, GList, sizeof(Texture*)*GListMax );
		delete [ ] GList;
		GList = tmp;
		GListMax += 64;
	}
	GList[GFree] = tex;
	GFree++;
}

Texture *TextureMgr::SearchGlobalTexture( const char *fname ) {
	for( DWORD j = 0; j < GFree; j++ )
		if( !strcmp( fname, GList[j]->file_name ) )
			return GList[j];
	return NULL;
}

bool TextureMgr::FindGlobal( Texture *tex ) {
	for( DWORD j = 0; j < GFree; j++ )
		if( GList[j] == tex )
			return true;
	return false;
}

void TextureMgr::DeleteGlobalTextures() {
	for( UINT j = 0; j < GFree; j++ )
		delete GList[j];
}
#pragma endregion
//=======================================================
//			TEXTURE DELETION QUEUE. (waits some time before deletion of texture. required for 2-threaded mode 
//	(texture deletion is thread-safe, blit/fill is thread-unsafe, so blit/fill can find some texture deleted a moment before ExecuteCommandList() was called ).)
//=======================================================

//useless ?:
void TextureMgr::AddToQueue( Texture *tex ) {
	InQueue++;
	Queue[InQueue].tex = tex;
}

void TextureMgr::FrameRendered() {
	for( UINT j = 0; j < InQueue; j++ ) {
		Queue[j].counter++;
		if( Queue[j].counter > 5 ) {
			delete Queue[j].tex;
			InQueue--;
			Queue[j] = Queue[InQueue];
			Queue[InQueue].counter = 0;
			Queue[InQueue].tex = NULL;
		}
	}
}

void TextureMgr::ClearQueue() {
	for( UINT j = 0; j < InQueue; j++ )
		delete Queue[j].tex;
}

//=======================================================	++
//			TEXTURE CATALOG. (determines is a texture loaded or not)
//	and rejects attemps of Orbiter to delete not loaded texture.(that will cause CTD otherwise)
//=======================================================
#pragma region texture "catalog"
void TextureMgr::AddTexture( Texture *tex ) {
	if( Free == ListMax ) {
		Texture **tmp = new Texture *[ListMax+256];
		memset( tmp, 0, sizeof(Texture*)*(ListMax+256) );
		memcpy( tmp, List, sizeof(Texture*)*ListMax );
		delete [ ] List;
		List = tmp;
		ListMax += 256;
	}
	List[Free] = tex;
	Free++;
}

bool TextureMgr::DeleteTexture( Texture *tex ) {
	if( !tex )			//back buffer cannot be deleted
		return false;
	for( DWORD j = 0; j < ListMax; j++ )
		if( List[j] == tex ) {
			if( tex->RefCounter == 1 ) {
				Free--;
				List[j] = List[Free];
				List[Free] = 0;
			}
			return true;
		}
	return false;
}

bool TextureMgr::Find( Texture *tex ) {
	if( !tex )			//back buffer always exist
		return true;
	for( DWORD j = 0; j < Free; j++ )
		if( List[j] == tex )
			return true;
	return false;
}

void TextureMgr::ClearRemainingTextures() {
	for( DWORD j = 0; j < Free; j++ )
		 delete List[j];
}
#pragma endregion
//=======================================================
//			CRATE/DELETE FUNCTIONS (thread-safe)
//=======================================================

Texture *TextureMgr::CreateSurface( DWORD w, DWORD h ) {
	if( !w || !h )
		return NULL;
	if( w > 4096 || h > 4096 )
		return NULL;
	Texture *tex = new Texture( w, h );
	AddTexture( tex );
	return tex;
}

Texture *TextureMgr::LoadTextureFromFile( const char *fname, DWORD flags ) {
//0,1,2 bits of flags are useless in D3D11.
	Texture *tex;

	if( (tex = SearchGlobalTexture( fname )) )
		return tex;

	char path[256];
	strcpy( path, "Textures\\" );
	strcat( path, fname );
	if( flags & 8 ) {
		tex = new Texture( path );
		if( tex->Tex ) {
			AddGlobalTexture( tex );
			
			sprintf( path, "Loading texture: %s", tex->file_name );
			gc->ProgressString( path, 1 );
			return tex;
		}
	}
	else {
		tex = new Texture( path );
		if( tex->Tex ) {	//i.e. the texture was succesfully loaded
			AddTexture( tex );

			sprintf( path, "Loading texture: %s", tex->file_name );
			gc->ProgressString( path, 1 );
			return tex;
		}		
	}

	sprintf( path, "Texture not found: %s", tex->file_name );
	gc->ProgressString( path, 2 );
	return tex;

	delete tex;
	return NULL;
}

Texture *TextureMgr::LoadNormalMapFromFile( const char *nname ) {
//loads normal maps from file
	Texture *tex;
	char path[128];

	tex = new Texture( nname );
	if( tex->Tex ) {
		AddTexture( tex );

		sprintf( path, "Loading normal map: %s", tex->file_name );
		gc->ProgressString( path, 5 );
		return tex;
	}
	delete tex;
	return NULL;
}

Texture *TextureMgr::LoadBumpMapFromFile( const char *bname ) {
	Texture *tex, *out;
	char path[128];

	tex = new Texture( bname );
	if( tex->Tex ) {
		out = new Texture( tex->width, tex->height, DXGI_FORMAT_BC1_UNORM, tex->file_name, false );
		HRESULT hr = D3DX11ComputeNormalMap( iCtx, tex->Tex, 0, D3DX_CHANNEL_RED, cfg->BumpAmplitude, out->Tex );
		delete tex;
		if( hr == S_OK ) {
			AddTexture( out );

			sprintf( path, "Loading bump map: %s", tex->file_name );
			gc->ProgressString( path, 5 );
			return out;
		}
		else {
			delete out;
			return NULL;
		}
	}
	delete tex;
	return NULL;
}

Texture *TextureMgr::LoadSpecularMapFromFile( const char *sname ) {
	Texture *tex;
	char path[128];

	tex = new Texture( sname );
	if( tex->Tex ) {
		AddTexture( tex );

		sprintf( path, "Loading specular map: %s", tex->file_name );
		gc->ProgressString( path, 5 );
		return tex;
	}
	delete tex;
	return NULL;
}

Texture *TextureMgr::LoadEmissiveMapFromFile( const char *ename ) {
	Texture *tex;
	char path[128];

	tex = new Texture( ename );
	if( tex->Tex ) {
		AddTexture( tex );

		sprintf( path, "Loading emissive map: %s", tex->file_name );
		gc->ProgressString( path, 5 );
		return tex;
	}
	delete tex;
	return NULL;
}

Texture *TextureMgr::CreateSurfaceBitmap( HBITMAP hbm ) {
	Texture *tex = CreateTextureFromBitmap( hbm );
	AddTexture( tex );
	return tex;
}

void TextureMgr::ReleaseTexture( Texture *tex ) {
	if( DeleteTexture( tex ) ) {
		if( tex->RefCounter == 1 )
			AddToQueue( tex );
		else
			tex->RefCounter--;
	}
}

//=======================================================
//			GDI. thread-unsafe
//=======================================================
/*
DXGI functions must not be called when immediate ID3D11DeviceContext is in use.
(It sometimes can hang OS to the reset-required state)
*/
HDC TextureMgr::GetSurfaceDC( Texture *tex ) {
	if( !Find( tex ) && !FindGlobal( tex ) )
		return NULL;

	if( !tex ) {	//2D buffer. GDI-compatible
		SC->BBDrawCount++;//notify Scene
		HDC hDC;
		if( cfg->RThread ) {
			SetEvent( ResourceRequest );		//send a request to RT
			EnterCriticalSection( &Resources );	//sync with RT
		}
		HR( SC->PBuffer_Surf->GetDC( FALSE, &hDC ) );	//??
		return hDC;
	}
	else {			//some texture or surface.
		if( tex->bTex )
			ConvertTextureToSurface( tex );		//texture is incompatible with GDI. so, convert it to surface.
		if( cfg->RThread ) {
			SetEvent( ResourceRequest );		//send a request to RT
			EnterCriticalSection( &Resources );	//sync with RT
			ExecuteCommandList();
		}
		return tex->GetDC();
	}
}

void TextureMgr::ReleaseSurfaceDC( HDC hdc, Texture *tex ) {
	if( !tex ) {	//GDI_buffer
		if( cfg->RThread ) {
			LeaveCriticalSection( &Resources );
			SetEvent( ResourceReleased );		//notify RT
		}
		HR( SC->PBuffer_Surf->ReleaseDC( NULL ) );
		return;
	}
	else {
		if( cfg->RThread ) {
			LeaveCriticalSection( &Resources );
			SetEvent( ResourceReleased );		//notify RT
		}
		tex->ReleaseDC();
	}
}

//=======================================================
//			MISC. thread-safe
//=======================================================

void TextureMgr::GetSize( Texture *tex, DWORD *w, DWORD *h ) {
	if( !Find( tex ) )		return;

	if( !tex ) {
		*w = cfg->cWidth;
		*h = cfg->cHeight;
	}
	else {
		*w = tex->width;
		*h = tex->height;
	}
}

void TextureMgr::IncrSurfaceRef( Texture *tex ) {
	if( !Find( tex ) )		return;

	tex->RefCounter++;
}

DWORD TextureMgr::GetDevColor( BYTE r, BYTE g, BYTE b ) {
	BYTE A = 255;
// ARGB => correct D3DXCOLOR !
	return (A << 24) | (r << 16) | (g << 8) | (b << 0);
}

bool TextureMgr::SetColorKey( Texture *tex, DWORD ckey ) {
	if( !Find( tex ) )		return false;

	tex->SetColorKey( ckey );
	return true;
}

//=======================================================
//=======================================================
#pragma region Main
//(!) CopySubresourceRegion() with invalid dimension param will cause DXGI_ERROR_DEVICE_REMOVED error and then CTD.
//textures have immutable usage (GPU-read-only) and cannot be used in any of these functions
bool TextureMgr::CopyBitmap( Texture *tex, HBITMAP hbm, int x, int y, int dx, int dy ) {	
	if( !tex || ( !Find( tex ) && !FindGlobal( tex ) ) )
		return false;
	if( !dx )		dx = 4096;
	if( !dy )		dy = 4096;
	if( tex->bTex )
		ConvertTextureToSurface( tex );

	Texture *ntex = CreateTextureFromBitmapRect( hbm, x, y, dx, dy );
	if( !ntex )		return false;

	Data prm;
	ZeroMemory( &prm, sizeof(Data) );
	prm.tgt = tex;
	prm.src = ntex;
	prm.tgtx = x;
	prm.tgty = y;
	prm.tgtw = dx;
	prm.tgth = dy;

	AddToQueue( ntex );

	if( cfg->RThread ) {
		AddToCommandList( 0, &prm );
		return true;
	}

	_CopyBitmap( &prm );
	return true;
}

bool TextureMgr::FillSurface( Texture *tex, DWORD col ) {
	if( !Find( tex ) && !FindGlobal( tex ) )
		return false;

	if( tex->bTex )
		ConvertTextureToSurface( tex );

//	if( !tex->bRTV )
//		tex->CreateRTV();	GetRTV
	
	Data prm;
	ZeroMemory( &prm, sizeof(Data) );
	prm.tgt = tex;
	prm.flags = col;
	
	if( cfg->RThread ) {		
		AddToCommandList( 1, &prm );
		return true;
	}

	_FillSurface1( &prm );
	return true;
}

bool TextureMgr::FillSurfaceRect( Texture *tex, DWORD tgtx, DWORD tgty, DWORD w, DWORD h, DWORD col ) {
	if( !Find( tex ) && !FindGlobal( tex ) )
		return false;
	if( tgtx > tex->width || tgty > tex->height )
		return false;
	if( (tgtx + w) > tex->width || !w )	//correct incorrect params
		w = tex->width - tgtx;
	if( (tgty + h) > tex->height || !h )//correct incorrect params
		h = tex->height - tgty;
	if( !w )							//correct incorrect params
		w = tex->width - tgtx;
	if( !h )							//correct incorrect params
		h = tex->height - tgty;

	if( tex->bTex )
		ConvertTextureToSurface( tex );

	Data prm;
	ZeroMemory( &prm, sizeof(Data) );
	prm.tgt = tex;
	prm.tgtx = tgtx;
	prm.tgty = tgty;
	prm.tgtw = w;
	prm.tgth = h;
	prm.flags = col;

	if( cfg->RThread ) {
		AddToCommandList( 2, &prm );
		return true;
	}

	_FillSurface2( &prm );
	return true;
}

bool TextureMgr::Blit1( Texture *tgt, DWORD tgtx, DWORD tgty, Texture *src, DWORD flags ) {
	if( (!Find( tgt ) && !FindGlobal( tgt )) || (!Find( src ) && !FindGlobal( src )) )
		return false;
	if( !tgt && !src )
		return false;
	if( tgt && tgt->bTex )
		ConvertTextureToSurface( tgt );
	if( src && src->bTex )
		ConvertTextureToSurface( src );

	DWORD	twidth = tgt ? tgt->width : cfg->cWidth,
			theight = tgt ? tgt->height : cfg->cHeight,
			swidth = src ? src->width : cfg->cWidth,
			sheight = src ? src->height : cfg->cHeight;

	if( tgtx > twidth || tgty > theight )
			return false;

	Data prm;
	ZeroMemory( &prm, sizeof(Data) );
	prm.tgt = tgt;
	prm.tgtx = tgtx;
	prm.tgty = tgty;
	prm.src = src;
	prm.flags = flags;

	if( cfg->RThread ) {
		AddToCommandList( 3, &prm );
		return true;
	}

	_Blit1( &prm );
	return true;
}

bool TextureMgr::Blit2( Texture *tgt, DWORD tgtx, DWORD tgty, Texture *src, DWORD srcx, DWORD srcy, DWORD w, DWORD h, DWORD flags ) {
	if( (!Find( tgt ) && !FindGlobal( tgt )) || (!Find( src ) && !FindGlobal( src )) )
		return false;
	if( !tgt && !src )
		return false;
	if( tgt && tgt->bTex )
		ConvertTextureToSurface( tgt );
	if( src && src->bTex )
		ConvertTextureToSurface( src );

	DWORD	twidth = tgt ? tgt->width : cfg->cWidth,
			theight = tgt ? tgt->height : cfg->cHeight,
			swidth = src ? src->width : cfg->cWidth,
			sheight = src ? src->height : cfg->cHeight;

	if( tgtx > twidth || tgty > theight )
		return false;
	if( (tgtx + w) > twidth )
		w = twidth - tgtx;
	if( (tgty + h) > theight )
		h = theight - tgty;

	if( srcx > swidth || srcy > sheight )
		return false;
	if( (srcx + w) > swidth )
		w = swidth - srcx;
	if( (srcy + h) > sheight )
		h = sheight - srcy;

	static Data prm;
	ZeroMemory( &prm, sizeof(Data) );
	prm.tgt = tgt;
	prm.tgtx = tgtx;
	prm.tgty = tgty;
	prm.src = src;
	prm.srcx = srcx;
	prm.srcy = srcy;
	prm.srcw = w;
	prm.srch = h;
	prm.flags = flags;

	if( cfg->RThread ) {
		AddToCommandList( 4, &prm );
		return true;
	}

	_Blit2( &prm );
	return true;
}

bool TextureMgr::ScaleBlit( Texture *tgt, DWORD tgtx, DWORD tgty, DWORD tgtw, DWORD tgth, Texture *src, DWORD srcx, DWORD srcy, DWORD srcw, DWORD srch, DWORD flags ) {
	if( (!Find( tgt ) && !FindGlobal( tgt )) || (!Find( src ) && !FindGlobal( src )) )		return false;
	if( !tgt && !src )						return false;
	if( tgt && tgt->bTex )
		ConvertTextureToSurface( tgt );
	if( src && src->bTex )
		ConvertTextureToSurface( src );

	DWORD	twidth = tgt ? tgt->width : cfg->cWidth,
			theight = tgt ? tgt->height : cfg->cHeight,
			swidth = src ? src->width : cfg->cWidth,
			sheight = src ? src->height : cfg->cHeight;

	if( tgtx > twidth || tgty > theight )
		return false;
	if( (tgtx + tgtw) > twidth )
		tgtw = twidth - tgtx;
	if( (tgty + tgth) > theight )
		tgth = theight - tgty;

	if( srcx > swidth || srcy > sheight )
		return false;
	if( (srcx + srcw) > swidth )
		srcw = swidth - srcx;
	if( (srcy + srch) > sheight )
		srch = sheight - srcy;

	Data prm;
	ZeroMemory( &prm, sizeof(Data) );
	prm.tgt = tgt;
	prm.tgtx = tgtx;
	prm.tgty = tgty;
	prm.tgtw = tgtw;
	prm.tgth = tgth;
	prm.src = src;
	prm.srcx = srcx;
	prm.srcy = srcy;
	prm.srcw = srcw;
	prm.srch = srch;
	prm.flags = flags;

	if( cfg->RThread ) {
		AddToCommandList( 5, &prm );
		return true;
	}

	_ScaleBlit( &prm );
	return true;
}
#pragma endregion
//=======================================================
//			misc FUNCTIONS.
//=======================================================

void TextureMgr::Render( Texture *tgt, float tgtx, float tgty, float tgtw, float tgth, Texture *src, float srcx, float srcy, float w, float h, DWORD flags ) {
	D3DXCOLOR color;
	PTVertex quad[4];
	float twidth, theight, swidth, sheight;
	static const UINT Stride = 16, Offset = 0;
	D3D11_MAPPED_SUBRESOURCE Res;
	
	twidth = tgt ? tgt->width : cfg->cWidth;
	theight = tgt ? tgt->height : cfg->cHeight;

	quad[0].px = tgtx/twidth;
	quad[0].py = tgty/theight;
	quad[1].px = quad[0].px;
	quad[1].py = (tgty+tgth)/theight;
	quad[2].px = (tgtx+tgtw)/twidth;
	quad[2].py = quad[1].py;
	quad[3].px = quad[2].px;
	quad[3].py = quad[0].py;

	swidth = src ? src->width : cfg->cWidth;
	sheight = src ? src->height : cfg->cHeight;

	quad[0].tu = srcx/swidth;
	quad[0].tv = srcy/sheight;
	quad[1].tu = quad[0].tu;
	quad[1].tv = (srcy+h)/sheight;
	quad[2].tu = (srcx+w)/swidth;
	quad[2].tv = quad[1].tv;
	quad[3].tu = quad[2].tu;
	quad[3].tv = quad[0].tv;

	iCtx->IASetVertexBuffers( 0, 1, &VB, &Stride, &Offset );
	iCtx->Map( (ID3D11Resource*)VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &Res );
	memcpy( Res.pData, quad, 64 );
	iCtx->Unmap( (ID3D11Resource*)VB, 0 );

	iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );
		
	iCtx->VSSetConstantBuffers( 0, 1, &cb_VS );
	iCtx->UpdateSubresource( cb_VS, 0, NULL, &Orth, 0, 0 );
		
	iCtx->PSSetShader( PS_Blit, 0, 0 );
	iCtx->VSSetShader( VS_Blit, 0, 0 );
	iCtx->IASetInputLayout( IL_Blit );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->RSSetState( RS_CullNone_Solid );
	tRender = true;

	if( flags & BLT_SRCCOLORKEY )
		color = src->ColorKey;
	if( flags & BLT_TGTCOLORKEY )
		color = tgt->ColorKey;
	if( !flags )		color.a = 3.0f;
	else				color.a = 1.0f;

	iCtx->PSSetConstantBuffers( 1, 1, &cb_PS );
	iCtx->UpdateSubresource( cb_PS, 0, NULL, &color, 0, 0 );

	if( tgtw == w )
		iCtx->PSSetSamplers( 0, 1, &SS_Point_Wrap );
	else
		iCtx->PSSetSamplers( 0, 1, &SS_Linear_Wrap );

	if( src )		iCtx->PSSetShaderResources( 0, 1, src->GetSRV() );
	else			iCtx->PSSetShaderResources( 0, 1, &SC->PBuffer_SRV );
	
	if( tgt )		iCtx->OMSetRenderTargets( 1, tgt->GetRTV(), NULL );
	else			iCtx->OMSetRenderTargets( 1, &SC->PBuffer_RTV, NULL );

	D3D11_VIEWPORT vp;
	vp.Width = twidth;
	vp.Height = theight;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	vp.MaxDepth = 1;
	vp.MinDepth = 0;

	iCtx->RSSetViewports( 1, &vp );

	iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );
	iCtx->OMSetBlendState( BS_NoBlend, D3DXCOLOR( 1, 1, 1, 1 ), 0xFFFFFFFF );

	iCtx->DrawIndexed( 6, 0, 0 );
}

void TextureMgr::ConvertTextureToSurface( Texture *tex ) {
	float col[4] = { 0.1f, 0.1f, 0.1f, 0.1f };
	BYTE tmp[128];
	DWORD *buffer, PixelCount, gray = 0xFFFFFF;

	PixelCount = tex->width * tex->height;
	buffer = new DWORD [PixelCount];
	for( DWORD j = 0; j < PixelCount; j++ )
		memcpy( &buffer[j], &gray, 4 );

	Texture *tnew = new Texture( tex->width, tex->height, (void*)buffer );
	delete [ ] buffer;

	memcpy( tmp, tex, sizeof(Texture) );
	memcpy( tex, tnew, sizeof(Texture) );
	memcpy( tnew, tmp, sizeof(Texture) );

	Data prm;
	ZeroMemory( &prm, sizeof(Data) );
	prm.tgt = tex;
	prm.src = tnew;

//copy valuable data
	tex->RefCounter = tnew->RefCounter;
	tex->ColorKey = tnew->ColorKey;
	tex->bColorKey = tnew->bColorKey;
	tex->has_alpha = tnew->has_alpha;
	if( tnew->file_name ) {
		tex->file_name = new char [strlen(tnew->file_name)+1];
		strcpy( tex->file_name, tnew->file_name );
	}

	AddToQueue( tnew );

	if( cfg->RThread ) {
		AddToCommandList( 6, &prm );
		return;
	}

	_ConvertToSurface( &prm );
}

Texture *TextureMgr::CreateTextureFromBitmap( HBITMAP hbm ) {
	DWORD PixelCount, btt = 0;
	BITMAP bmDesc;
	BYTE *data, *buffer;
	Texture *tex;

	data = NULL;
	GetObject( hbm, sizeof( BITMAP ), &bmDesc );
	PixelCount = bmDesc.bmWidth*bmDesc.bmHeight;
	buffer = new BYTE[4*PixelCount];

	switch( bmDesc.bmBitsPixel ) {
	case 8:
		{
			RGBQUAD ColorTable[256] = {0};
			data = new BYTE[PixelCount];
			GetBitmapBits( hbm, PixelCount, (LPVOID)data );
			HDC hdc = CreateDCA( "DISPLAY", NULL, NULL, NULL );
			hdc = CreateCompatibleDC( hdc );
			SelectObject( hdc, hbm );
			GetDIBColorTable( hdc, 0, 255, ColorTable );
			DeleteDC( hdc );

			BYTE value;
			for( DWORD j = 0; j < PixelCount; j++ ) {
				value = data[j];
				buffer[btt+3] = 255;
				buffer[btt+2] = ColorTable[value].rgbRed;
				buffer[btt+1] = ColorTable[value].rgbGreen;
				buffer[btt]   = ColorTable[value].rgbBlue;
				btt += 4;
			}
		}
		break;
	case 16:
		oapiWriteLog( "16 bit bmp not supported." );
		break;
	case 24:
		{
			DWORD btb = 0;
			data = new BYTE[PixelCount*3];
			GetBitmapBits( hbm, 3*PixelCount, (LPVOID)data );
			for( DWORD j = 0; j < PixelCount; j++ ) {
				buffer[btt] = data[btb];
				buffer[btt+1] = data[btb+1];
				buffer[btt+2] = data[btb+2];
				buffer[btt+3] = 255;
				btb += 3;
				btt += 4;
			}
		}
		break;
	case 32:
		GetBitmapBits( hbm, 4*PixelCount, (LPVOID)buffer );
		for( DWORD j = 0; j < PixelCount; j++ )
			buffer[j*4+3] = 255;
		break;
	default:
		oapiWriteLog( "Unrecognized Bitmap format" );
		break;
	}

	tex = new Texture( bmDesc.bmWidth, bmDesc.bmHeight, (void*)buffer );
	delete [ ] buffer;
	if( data )	
		delete [ ] data;
	return tex;
}

Texture *TextureMgr::CreateTextureFromBitmapRect( HBITMAP hbm, int x, int y, int dx, int dy ) {
	DWORD PixelCount, bt = 0, btt = 0;
	BITMAP bmDesc;
	BYTE *data, *buffer;
	Texture *tex;

	data = NULL;
	GetObject( hbm, sizeof( BITMAP ), &bmDesc );

	if( x > bmDesc.bmWidth || y > bmDesc.bmHeight )
		return NULL;

	if( (x + dx) > bmDesc.bmWidth )
		dx = bmDesc.bmWidth - x;
	if( (y + dy) > bmDesc.bmHeight )
		dy = bmDesc.bmHeight - y;

	PixelCount = bmDesc.bmWidth*bmDesc.bmHeight;
	buffer = new BYTE[4*dx*dy];

	switch( bmDesc.bmBitsPixel ) {
	case 8:
		{
			RGBQUAD ColorTable[256] = {0};
			data = new BYTE[PixelCount];
			GetBitmapBits( hbm, PixelCount, (LPVOID)data );
			HDC hdc = CreateDCA( "DISPLAY", NULL, NULL, NULL );
			hdc = CreateCompatibleDC( hdc );
			SelectObject( hdc, hbm );
			GetDIBColorTable( hdc, 0, 255, ColorTable );
			DeleteDC( hdc );

			BYTE value;
			for( DWORD h = bmDesc.bmHeight; h > 0; h-- )
				for( DWORD w = bmDesc.bmWidth; w > 0; w-- ) {
					if( w >= x && h >= y && w <= (x+dx) && h <= (y+dy) ) {
						value = data[bt];
						buffer[btt+3] = 255;
						buffer[btt+2] = ColorTable[value].rgbRed;
						buffer[btt+1] = ColorTable[value].rgbGreen;
						buffer[btt] = ColorTable[value].rgbBlue;
						btt += 4;
					}
					bt++;
				}
		}
		break;
	case 16:
		oapiWriteLog( "16 bitmap rect not supported." );
		break;
	case 24:
		oapiWriteLog( "24 bitmap rect not supported." );
		break;
	case 32:
		{
			data = new BYTE[PixelCount*4];
			GetBitmapBits( hbm, PixelCount*4, (LPVOID)data );
			for( DWORD h = 0; h < bmDesc.bmHeight; h++ )
				for( DWORD w = 0; w < bmDesc.bmWidth; w++ ) {
					if( w >= x && h >= y && w < (x+dx) && h < (y+dy) ) {
						memcpy( &buffer[btt], &data[bt], 4 );
						btt += 4;
					}
					bt += 4;
				}
		}
		break;
	default:
		oapiWriteLog( "Unrecognized Bitmap format" );
		break;
	}

	tex = new Texture( dx, dy, (void*)buffer );

	delete [ ] buffer;
	delete [ ] data;

	return tex;
}

void TextureMgr::GInit() {
	ID3DBlob *SBlob, *EBlob;

	PTVertex quad[4];
	quad[0] = _vPT( 0, 0, 0, 0 );
	quad[1] = _vPT( 1, 0, 1, 0 );
	quad[2] = _vPT( 1, -1, 1, 1 );
	quad[3] = _vPT( 0, -1, 0, 1 );
	UINT16 Idx[6] = { 0, 1, 2, 0, 2, 3 };
//vertex buffer
	D3D11_BUFFER_DESC BDesc;
	ZeroMemory( &BDesc, sizeof( BDesc ) );
	BDesc.ByteWidth = 64;
	BDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	BDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	BDesc.MiscFlags = 0;
	BDesc.StructureByteStride = 0;
	BDesc.Usage = D3D11_USAGE_DYNAMIC;

	D3D11_SUBRESOURCE_DATA SRData;
	ZeroMemory( &SRData, sizeof( SRData ) );
	SRData.pSysMem = quad;

	HR( Dev->CreateBuffer( &BDesc, &SRData, &VB ) );
//index buffer.
	ZeroMemory( &BDesc, sizeof( BDesc ) );
	BDesc.ByteWidth = 12;
	BDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	BDesc.CPUAccessFlags = 0;
	BDesc.MiscFlags = 0;
	BDesc.StructureByteStride = 0;
	BDesc.Usage = D3D11_USAGE_DEFAULT;

	ZeroMemory( &SRData, sizeof( SRData ) );
	SRData.pSysMem = Idx;

	HR( Dev->CreateBuffer( &BDesc, &SRData, &IB ) );
//constant buffers.
	ZeroMemory( &BDesc, sizeof( BDesc ) );
	BDesc.CPUAccessFlags = 0;
	BDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	BDesc.ByteWidth = 64;
	BDesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &BDesc, NULL, &cb_VS ) );

	ZeroMemory( &BDesc, sizeof( BDesc ) );
	BDesc.CPUAccessFlags = 0;
	BDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	BDesc.ByteWidth = 16;
	BDesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &BDesc, NULL, &cb_PS ) );
//projection matrix.
	D3DXMatrixIdentity( &Orth );
	Orth._11 = 2.0f;
	Orth._22 = -2.0f;
	Orth._33 = 0.5f;
	Orth._14 = -1.0f;
	Orth._24 = 1.0f;
	Orth._34 = 0.5f;
//shaders.
	HRESULT hr;
	D3D11_INPUT_ELEMENT_DESC IEDesc[ ] = {	
		{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 } };

	CompileFromFile( "Modules\\D3D11Shaders\\Blit.fx", NULL, NULL, "VSBlit", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateInputLayout( IEDesc, 2, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_Blit ) );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Blit ) );
	REL( SBlob );
	REL( EBlob );

	CompileFromFile( "Modules\\D3D11Shaders\\Blit.fx", NULL, NULL, "PSBlit", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Blit ) );
	REL( SBlob );
	REL( EBlob );
}

//=======================================================
//			COMMAND BUFFER + actual blit/fill functions
//=======================================================

void TextureMgr::AddToCommandList( DWORD type, Data *data ) {
	if( CommandCount == CommandMax ) {
		Command *tmp = new Command [CommandMax+32];
		ZeroMemory( tmp, sizeof(Command)*(CommandMax+32) );
		memcpy( tmp, CList, sizeof(Command)*CommandMax );
		delete [ ] CList;
		CList = tmp;
		CommandMax += 32;
	}

	CList[CommandCount].type = type;
	memcpy( &CList[CommandCount].params, data, sizeof(Data) );
	CommandCount++;
}

void TextureMgr::ExecuteCommandList() {

	for( DWORD j = 0; j < CommandCount; j++ ) {
		
		switch( CList[j].type ) {
		case 0:
			_CopyBitmap( &CList[j].params );
			break;
		case 1:
			_FillSurface1( &CList[j].params );
			break;
		case 2:
			_FillSurface2( &CList[j].params );
			break;
		case 3:
			_Blit1( &CList[j].params );
			break;
		case 4:
			_Blit2( &CList[j].params );
			break;
		case 5:
			_ScaleBlit( &CList[j].params );
			break;
		case 6:
			_ConvertToSurface( &CList[j].params );
			break;
		}
	}

	ZeroMemory( CList, sizeof(Command)*CommandMax );
	CommandCount = 0;
}

void TextureMgr::_CopyBitmap( Data *prm ) {
	iCtx->CopyResource( prm->tgt->Tex, prm->src->Tex );
}

void TextureMgr::_FillSurface1( Data *prm ) {
	D3DXCOLOR Col = D3DXCOLOR( prm->flags );
	Col.a = 0.0f;

	iCtx->OMSetRenderTargets( 1, prm->tgt->GetRTV(), NULL );
	iCtx->ClearRenderTargetView( *prm->tgt->GetRTV(), Col );
}

void TextureMgr::_FillSurface2( Data *prm ) {
	D3DXCOLOR Col = D3DXCOLOR( prm->flags );
	Col.a = 0.0f;

	Texture *buf = FindBuffer( prm->tgtw, prm->tgth );
	iCtx->OMSetRenderTargets( 1, buf->GetRTV(), NULL );
	iCtx->ClearRenderTargetView( *buf->GetRTV(), Col );

	D3D11_BOX box;
	box.front = 0;
	box.back = 1;
	box.top = 0;
	box.left = 0;
	box.right = prm->tgtw;
	box.bottom = prm->tgth;

	iCtx->CopySubresourceRegion( prm->tgt->Tex, 0, prm->tgtx, prm->tgty, 0, buf->Tex, 0, &box );
}

void TextureMgr::_Blit1( Data *prm ) {
	DWORD	twidth = prm->tgt ? prm->tgt->width : cfg->cWidth,
			theight = prm->tgt ? prm->tgt->height : cfg->cHeight,
			swidth = prm->src ? prm->src->width : cfg->cWidth,
			sheight = prm->src ? prm->src->height : cfg->cHeight;

	if( prm->flags )
		Render( 
		prm->tgt, (float)prm->tgtx, (float)prm->tgty, (float)swidth, (float)sheight,
		prm->src, 0.0f, 0.0f, (float)swidth, (float)sheight, prm->flags );
	else {
		ID3D11Texture2D *target = prm->tgt ? prm->tgt->Tex : SC->PBuffer,
						*source = prm->src ? prm->src->Tex : SC->PBuffer;

		if( (prm->tgtx + swidth) > twidth || (prm->tgty + sheight) > theight ) {
			DWORD	w = twidth - prm->tgtx,
					h = theight - prm->tgty;
			w = min( swidth, w );
			h = min( sheight, h );

			D3D11_BOX box;
			box.back = 1;
			box.front = 0;
			box.left = 0;
			box.right = w;
			box.top = 0;
			box.bottom = h;

			Texture *buf = FindBuffer( w, h );
			iCtx->CopySubresourceRegion( buf->Tex, 0, 0, 0, 0, source, 0, &box );
			iCtx->CopySubresourceRegion( target, 0, prm->tgtx, prm->tgty, 0, buf->Tex, 0, NULL );
		}
		iCtx->CopySubresourceRegion( target, 0, prm->tgtx, prm->tgty, 0, source, 0, NULL);
	}
}

void TextureMgr::_Blit2( Data *prm ) {

	if( prm->flags ) {
		if( prm->tgt == prm->src ) {
			Texture *buf = FindBuffer( prm->srcw, prm->srch );
			Render( 
				buf, 0.0f, 0.0f, (float)prm->srcw, (float)prm->srch,
				prm->src, (float)prm->srcx, (float)prm->srcy, (float)prm->srcw, (float)prm->srch, prm->flags );
			Render( 
				prm->tgt, (float)prm->tgtx, (float)prm->tgty, (float)prm->srcw, (float)prm->srch,
				buf, 0.0f, 0.0f, (float)prm->srcw, (float)prm->srch, prm->flags );
			return;
		}
		Render( 
			prm->tgt, (float)prm->tgtx, (float)prm->tgty, (float)prm->srcw, (float)prm->srch,
			prm->src, (float)prm->srcx, (float)prm->srcy, (float)prm->srcw, (float)prm->srch, prm->flags );
		return;
	}

	ID3D11Texture2D
		*target = prm->tgt ? prm->tgt->Tex : SC->PBuffer,
		*source = prm->src ? prm->src->Tex : SC->PBuffer;

	D3D11_BOX box;
	box.front = 0;
	box.back = 1;
	box.left = prm->srcx;
	box.top = prm->srcy;
	box.right = prm->srcx + prm->srcw;
	box.bottom = prm->srcy + prm->srch;

	D3D11_BOX Box;
	Box.front = 0;
	Box.back = 1;
	Box.left = 0;
	Box.right = prm->srcw;
	Box.top = 0;
	Box.bottom = prm->srch;

	Texture *buf = FindBuffer( prm->srcw, prm->srch );

	iCtx->CopySubresourceRegion( buf->Tex, 0, 0, 0, 0, source, 0, &box );
	iCtx->CopySubresourceRegion( target, 0, prm->tgtx, prm->tgty, 0, buf->Tex, 0, &Box );
}

void TextureMgr::_ScaleBlit( Data *prm ) {
	Render( prm->tgt, prm->tgtx, prm->tgty, prm->tgtw, prm->tgth, prm->src, prm->srcx, prm->srcy, prm->srcw, prm->srch, prm->flags );
}

void TextureMgr::_ConvertToSurface( Data *prm ) {
	static D3DXCOLOR color;
	PTVertex quad[4];
	static const UINT Stride = 16, Offset = 0;
	D3D11_MAPPED_SUBRESOURCE Res;

	quad[0] = _vPT( 0, 0, 0, 0 );
	quad[1] = _vPT( 0, 1, 0, 1 );
	quad[2] = _vPT( 1, 1, 1, 1 );
	quad[3] = _vPT( 1, 0, 1, 0 );

	float factors[4] = { 1.0f };
//buffers.
	iCtx->IASetVertexBuffers( 0, 1, &VB, &Stride, &Offset );
	iCtx->Map( (ID3D11Resource*)VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &Res );
	memcpy( Res.pData, quad, 64 );
	iCtx->Unmap( (ID3D11Resource*)VB, 0 );
	iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

	iCtx->VSSetConstantBuffers( 0, 1, &cb_VS );
	iCtx->UpdateSubresource( cb_VS, 0, NULL, &Orth, 0, 0 );

	color.a = 2.0f;
	iCtx->PSSetConstantBuffers( 1, 1, &cb_PS );
	iCtx->UpdateSubresource( cb_PS, 0, NULL, &color, 0, 0 );

	iCtx->PSSetShader( PS_Blit, 0, 0 );
	iCtx->VSSetShader( VS_Blit, 0, 0 );
	iCtx->IASetInputLayout( IL_Blit );
	
	iCtx->PSSetSamplers( 0, 1, &SS_Point_Wrap );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->RSSetState( RS_CullNone_Solid );

	iCtx->OMSetRenderTargets( 1, prm->tgt->GetRTV(), NULL );
	iCtx->PSSetShaderResources( 0, 1, prm->src->GetSRV() );

	D3D11_VIEWPORT vp;
	vp.Width = prm->src->width;
	vp.Height = prm->src->height;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	vp.MaxDepth = 1;
	vp.MinDepth = 0;
	iCtx->RSSetViewports( 1, &vp );

	iCtx->OMSetBlendState( BS_NoBlend, factors, 0xFFFFFFFF );
	iCtx->DrawIndexed( 6, 0, 0 );
}