#include "Texture.h"

//==========================================================================
//			Texture or Surface creation/loading
//==========================================================================

Texture::Texture( DWORD w, DWORD h ) {
//	Creates GDI compatible surface without initial data.
	D3D11_TEXTURE2D_DESC Desc;
	ZeroMemory( &Desc, sizeof( Desc ) );
	bTex = false;

	bSRV = bRTV = false;
	SRV = NULL;
	RTV = NULL;
	file_name = NULL;
	RefCounter = 1;
	
	has_alpha = true;
	bColorKey = false;
	ColorKey.r = ColorKey.g = ColorKey.b = ColorKey.a = 0.0f;

	Desc.Width = width = w;
	Desc.Height = height = h;
	Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
	Desc.MipLevels = 1;
	Desc.Usage = D3D11_USAGE_DEFAULT;
	Desc.SampleDesc.Count = 1;
	Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;			//required for GDI compatibility
	Desc.ArraySize = 1;
	Desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;//required for GDI compatibility
	Desc.CPUAccessFlags = 0;
		
	HR( Dev->CreateTexture2D( &Desc, NULL, &Tex ) );
	HR( Tex->QueryInterface( __uuidof(IDXGISurface1), reinterpret_cast<void**>(&Surf) ) );
}

Texture::Texture( DWORD w, DWORD h, void *data ) {
//	Creates GDI compatible surface from R8G8B8A8 bitmap.
	D3D11_TEXTURE2D_DESC Desc;
	ZeroMemory( &Desc, sizeof( Desc ) );
	Desc.Width = width = w;
	Desc.Height = height = h;
	Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
	Desc.MipLevels = 1;
	Desc.CPUAccessFlags = 0;
	Desc.Usage = D3D11_USAGE_DEFAULT;
	Desc.SampleDesc.Count = 1;
	Desc.SampleDesc.Quality = 0;
	Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	Desc.ArraySize = 1;
	Desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;

	D3D11_SUBRESOURCE_DATA SData;
	ZeroMemory( &SData, sizeof( SData ) );
	SData.pSysMem = data;
	SData.SysMemPitch = width*4;

	HR( Dev->CreateTexture2D( &Desc, &SData, &Tex ) );
	HR( Tex->QueryInterface( __uuidof(IDXGISurface1), reinterpret_cast<void**>(&Surf) ) );

	SRV = NULL;
	RTV = NULL;
	file_name = NULL;
	has_alpha = true;
	bTex = bSRV = bRTV = false;
	RefCounter = 1;
	bColorKey = false;
	ColorKey.r = ColorKey.g = ColorKey.b = ColorKey.a = 0.0f;
}

Texture::Texture( DWORD w, DWORD h, DXGI_FORMAT Format, const char *fname, bool alpha ) {
//used for creation of normal map texture from the red channel of bump map.
	D3D11_TEXTURE2D_DESC Desc;
	ZeroMemory( &Desc, sizeof( Desc ) );
	Desc.Width = width = w;
	Desc.Height = height = h;
	Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	Desc.MipLevels = cfg->TextureMipMapCount;
	Desc.CPUAccessFlags = 0;
	Desc.Usage = D3D11_USAGE_DEFAULT;
	Desc.SampleDesc.Count = 1;
	Desc.SampleDesc.Quality = 0;
	Desc.Format = Format;
	Desc.ArraySize = 1;
	Desc.MiscFlags = 0;

	HR( Dev->CreateTexture2D( &Desc, NULL, &Tex ) );

	D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;
	ZeroMemory( &SDesc, sizeof( SDesc ));
	SDesc.Format = Format;
	SDesc.Texture2D.MipLevels = 1;
	SDesc.Texture2D.MostDetailedMip = 0;
	SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

	HR( Dev->CreateShaderResourceView( (ID3D11Resource*)Tex, &SDesc, &SRV ) );
	bTex = bSRV = true;

	Surf = NULL;
	RTV = NULL;

	UINT len = strlen( fname );
	if( len ) {
		file_name = new char [len+1];
		strcpy( file_name, fname );
	}

	has_alpha = alpha;	
	bRTV = false;
	RefCounter = 1;
	bColorKey = false;
	ColorKey.r = ColorKey.g = ColorKey.b = ColorKey.a = 0.0f;
}

Texture::Texture( const char *fname ) {
//	Loads a texture from file without format modification (if possible).
//	char path[256];
	D3DX11_IMAGE_INFO Info;
	D3DX11_IMAGE_LOAD_INFO LoadInfo;
	HRESULT hr;

	//init:
	Tex = NULL;			//file not found.
	SRV = NULL;
	Surf = NULL;
	RTV = NULL;

	bTex = true;
	has_alpha = bSRV = bRTV = bColorKey = false;
	ColorKey.r = ColorKey.g = ColorKey.b = ColorKey.a = 0.0f;
	RefCounter = 1;

	ZeroMemory( &Info, sizeof(Info) );
	ZeroMemory( &LoadInfo, sizeof(LoadInfo) );

	//load:
	file_name = NULL;	
	file_name = new char [strlen(fname)+1];
	strcpy( file_name, fname );

	FILE *file;
	BYTE *data;	

	if( !(file = fopen( fname, "rb" )) )
		return;
	fseek( file, 0, SEEK_END );
	DWORD file_size = ftell( file );
	fseek( file, 0, SEEK_SET );

	data = new BYTE [file_size];
	fread( data, 1, file_size, file );
	fclose( file );

	if( *((DWORD*)data) != MAKEFOURCC( 'D', 'D', 'S', ' ' ) ) {
		delete [ ] data;	//not .dds
		return;
	}

//	DDSURFACEDESC2 *ddesc = (DDSURFACEDESC2*)(data+4);
//	has_alpha = ddesc->ddpfPixelFormat.dwAlphaBitDepth > 0 ? true : false;

	D3DX11GetImageInfoFromMemory( data, file_size, NULL, &Info, &hr );
	if( hr != S_OK ) {		//get image info error
		delete [ ] data;
		return;
	}

	has_alpha = FindAlpha( Info.Format );

	LoadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	LoadInfo.CpuAccessFlags = 0;
	LoadInfo.Depth = 1;
	LoadInfo.Filter = D3DX11_FILTER_NONE;
	LoadInfo.FirstMipLevel = 0;
	LoadInfo.Format = Info.Format;
	LoadInfo.Height = height = Info.Height;
	LoadInfo.Width = width = Info.Width;
	LoadInfo.Usage = D3D11_USAGE_IMMUTABLE;
	LoadInfo.pSrcInfo = &Info;
	LoadInfo.MiscFlags = 0;
	LoadInfo.MipLevels = cfg->TextureMipMapCount;
	LoadInfo.MipFilter = D3DX11_FILTER_LINEAR;

	D3DX11CreateShaderResourceViewFromMemory( Dev, data, file_size, &LoadInfo, NULL, &SRV, &hr );
	bSRV = true;

	delete [ ] data;

	if( hr != S_OK )	Tex = NULL;			//SRV creation error
	else				SRV->GetResource( (ID3D11Resource**)&Tex );

	bTex = true;
	Surf = NULL;
	RTV = NULL;
	bRTV = bColorKey = false;
	ColorKey.r = ColorKey.g = ColorKey.b = ColorKey.a = 0.0f;
	RefCounter = 1;
	return;
/*
	Tex = NULL;
	SRV = NULL;



	D3DX11GetImageInfoFromFileA( path, NULL, &Info, &hr );
	if( hr != S_OK ) {
		//texture loading failed. jump to end of function.
		oapiWriteLog( "Error loading image info." );
		oapiWriteLog( path );
		Tex = NULL;
		SRV = NULL;

		bTex = true;
		Surf = NULL;
		RTV = NULL;
		bRTV = bColorKey = false;
		ColorKey.r = ColorKey.g = ColorKey.b = ColorKey.a = 0.0f;
		RefCounter = 1;
		return;
	}

	file_name = new char [strlen(path)+1];
	strcpy( file_name, path );

	width = Info.Width;
	height = Info.Height;

	LoadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	LoadInfo.CpuAccessFlags = 0;
	LoadInfo.Depth = 1;
	LoadInfo.Filter = D3DX11_FILTER_NONE;
	LoadInfo.FirstMipLevel = 0;
	LoadInfo.Format = Info.Format;
	LoadInfo.Height = Info.Height;
	LoadInfo.Width = Info.Width;
	LoadInfo.Usage = D3D11_USAGE_IMMUTABLE;
	LoadInfo.pSrcInfo = &Info;
	LoadInfo.MiscFlags = 0;
	LoadInfo.MipLevels = 1;
	LoadInfo.MipFilter = D3DX11_FILTER_LINEAR;

	D3DX11CreateShaderResourceViewFromFileA( Dev, path, &LoadInfo, NULL, &SRV, &hr );
	bSRV = true;

	if( hr != S_OK ) {
		oapiWriteLog( "Failed to create SRV" );
		Tex = NULL;
	}
	else
		SRV->GetResource( (ID3D11Resource**)&Tex );
	bTex = true;
	Surf = NULL;
	RTV = NULL;
	bRTV = bColorKey = false;
	ColorKey.r = ColorKey.g = ColorKey.b = ColorKey.a = 0.0f;
	RefCounter = 1;*/
}

//==========================================================================
//==========================================================================

Texture::~Texture() {
//Release() is thread safe
	if( Tex )	Tex->Release();
	if( Surf )	Surf->Release();
	if( SRV )	SRV->Release();
	if( RTV )	RTV->Release();
	if( file_name )	delete [ ] file_name;
}

void Texture::GetSize( DWORD *w, DWORD *h ) {
	*w = width;
	*h = height;
}

void Texture::SetColorKey( DWORD ckey ) {
//color key can be used in blit for transparency
	ColorKey = D3DXCOLOR( ckey );
	bColorKey = true;
}

HDC Texture::GetDC() {
/*	returns GDI DC for surface	(thread-unsafe)
	works only for B8G8R8A8 textures
	slow
*/
	HDC hdc;
	HRESULT hr;
	hr = Surf->GetDC( FALSE, &hdc );
	if( hr == S_OK )
		return hdc;
	else {
		oapiWriteLog( "GetDC() failed");
		return NULL;
	}
}

void Texture::ReleaseDC() {
//releases GDI DC;
	Surf->ReleaseDC( NULL );
}

ID3D11ShaderResourceView **Texture::CreateSRV() {
//creates SRV for surface
	D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;

	ZeroMemory( &SDesc, sizeof( SDesc ));
	SDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	SDesc.Texture2D.MipLevels = 1;
	SDesc.Texture2D.MostDetailedMip = 0;
	SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

	HR( Dev->CreateShaderResourceView( (ID3D11Resource*)Tex, &SDesc, &SRV ) );
	bSRV = true;
	return &SRV;
}

ID3D11RenderTargetView **Texture::CreateRTV() {
	if( bTex )	return NULL;	//RTV cannot be created for a texture

	D3D11_RENDER_TARGET_VIEW_DESC RDesc;

	ZeroMemory( &RDesc, sizeof( RDesc ) );
	RDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	RDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
	RDesc.Texture2D.MipSlice = 0;

	HR( Dev->CreateRenderTargetView( Tex, &RDesc, &RTV ) );
	bRTV = true;
	return &RTV;
}

bool Texture::FindAlpha( DXGI_FORMAT format ) {
	switch( format ) {
	case DXGI_FORMAT_BC2_UNORM:
	case DXGI_FORMAT_BC3_UNORM:
	case DXGI_FORMAT_BC4_UNORM:
	case DXGI_FORMAT_BC5_UNORM:
	case DXGI_FORMAT_R8G8B8A8_UNORM:
	case DXGI_FORMAT_B5G5R5A1_UNORM:
	case DXGI_FORMAT_B8G8R8A8_UNORM:
	case DXGI_FORMAT_B8G8R8X8_UNORM:
		return true;
	}
	return false;
}