//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2007-2008)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: loadableTexture_d3d9.cpp
//      Краткое описание: текстура, загружаемая из файла под платформу DirectX 9
//

#ifdef GD_D3D9

#include <Darkstar/Resource System/Textures/loadableTexture.hpp>
#include <Darkstar/Kernel/logh.hpp>
#include <Darkstar/Graphic System/Direct3D/graphicDriver.hpp>
#include <Darkstar/math.hpp>
#include <ddraw.h>

using namespace ker;
using namespace rs_private;
using namespace rs;

D3DFORMAT _getD3DFormat(const DSEFORMAT& format)
{
	switch(format)
	{
	case DFMT_R10G10B10A2_UNORM:
		return D3DFMT_A2B10G10R10;
	case DFMT_R8G8B8A8_UNORM:
		return D3DFMT_A8B8G8R8;
	case DFMT_B8G8R8A8_UNORM:
		return D3DFMT_A8R8G8B8;
	case DFMT_A8_UNORM:
		return D3DFMT_A8;
	case DFMT_R8_UNORM:
		return D3DFMT_L8;
	case DFMT_R16_UNORM:
		return D3DFMT_L16;
	case DFMT_R8G8_SNORM:
		return D3DFMT_V8U8;
	case DFMT_R8G8B8A8_SNORM:
		return D3DFMT_Q8W8V8U8;
	case DFMT_R16G16_SNORM:
		return D3DFMT_V16U16;
	case DFMT_R16G16_UNORM:
		return D3DFMT_G16R16;
	case DFMT_BC1_UNORM:
		return D3DFMT_DXT1;
	case DFMT_BC2_UNORM:
		return D3DFMT_DXT3;
	case DFMT_BC3_UNORM:
		return D3DFMT_DXT5;
	case DFMT_R16G16B16A16_UNORM:
		return D3DFMT_A16B16G16R16;
	case DFMT_R16G16B16A16_SNORM:
		return D3DFMT_Q16W16V16U16;
	case DFMT_R8G8_B8G8_UNORM:
		return D3DFMT_G8R8_G8B8;
	case DFMT_G8R8_G8B8_UNORM:
		return D3DFMT_R8G8_B8G8;
	case DFMT_R16_FLOAT:
		return D3DFMT_R16F;
	case DFMT_R16G16_FLOAT:
		return D3DFMT_G16R16F;
	case DFMT_R16G16B16A16_FLOAT:
		return D3DFMT_A16B16G16R16F;
	case DFMT_R32_FLOAT:
		return D3DFMT_R32F;
	case DFMT_R32G32_FLOAT:
		return D3DFMT_G32R32F;
	case DFMT_R32G32B32A32_FLOAT:
		return D3DFMT_A32B32G32R32F;
	default:
		return D3DFMT_UNKNOWN;
	};
};


LoadableTexture::LoadableTexture() : BaseTexture()
{
	
};

LoadableTexture::~LoadableTexture()
{
	
};

DWORD LoadableTexture::_getConvertedColor(const unsigned char red, const unsigned char green, const unsigned char blue, const unsigned char alpha) const
{
	return DWORD(blue + (green << 8) + (red << 16) + (alpha << 24));
};

void LoadableTexture::_setCorrectConvertedFormat()
{
	pixel_format = DFMT_B8G8R8A8_UNORM;
};

rs::DSEFORMAT LoadableTexture::_tryToSetSpecificPlatfromPixelFormat(const bool is_signed, const unsigned int bpp)
{
	if(!is_signed && masks.amask == 0xff000000 && masks.bmask == 0x000000ff && masks.gmask == 0x0000ff00 && masks.rmask == 0x00ff0000)
		return rs::DFMT_B8G8R8A8_UNORM;
	return rs::DFMT_UNKNOWN;
};

bool LoadableTexture::_makeProxyTexture(const std::string& filename)
{
	lwar << "Try to create proxy texture for file: " << filename;
	width = 2;
	height = 2;
	depth = 1;
	pixel_format = DFMT_B8G8R8A8_UNORM;
	mip_count = 1;
	unsigned char data[16];
	for(unsigned int i = 0; i < 16; i += 4)
	{
		data[i] = 255;
		data[i+1] = 0;
		data[i+2] = 255;
		data[i+3] = 0;
	};
	LPDIRECT3DTEXTURE9 texture2D;
	if(FAILED(gs::GraphicDriver::ptr()->getDevice()->CreateTexture(width, height, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture2D, NULL)))
	{
		lwar << "Cannot create proxy texture";
		return false;
	};
	LPDIRECT3DSURFACE9 surface;
	if(FAILED(texture2D->GetSurfaceLevel(0, &surface)))
	{
		lwar << "Cannot get surface from texture";
		return false;
	};
	RECT rect = {0, 0, 2, 2};
	if(FAILED(D3DXLoadSurfaceFromMemory(surface, NULL, NULL, data, D3DFMT_A8R8G8B8, width*4, NULL, &rect, D3DX_FILTER_NONE, 0)))
	{
		lwar << "Cannot load data to texture's surface";
		return false;
	};
	if(FAILED(surface->Release()))
		lwar << "Cannot release surface";
	tex = texture2D;
	lwar << "Will be use proxy texture for file: " << filename;
	return true;
};

bool LoadableTexture::_createTexture2D(const shared_array<DSUBRES_DATA>& sub_data)
{
	IDirect3DTexture9* texture2D;
	D3DFORMAT d3d_pixel_format = _getD3DFormat(pixel_format);
	if(FAILED(gs::GraphicDriver::ptr()->getDevice()->CreateTexture(width, height, mip_count, 0, d3d_pixel_format, D3DPOOL_MANAGED, &texture2D, NULL)))
	{
		lwar << "Cannot create texture 2D";
		return false;
	};
	IDirect3DSurface9* surface;
	unsigned int tmp_width = width;
	unsigned int tmp_height = height;
	unsigned int tmp_depth = depth;
	for(unsigned int i = 0; i < mip_count; ++i)
	{
		if(FAILED(texture2D->GetSurfaceLevel(i, &surface)))
		{
			lwar << "Cannot get surface level " << i;
			texture2D->Release();
			return false;
		};
		RECT rect = {0, 0, tmp_width, tmp_height};
		if(FAILED(D3DXLoadSurfaceFromMemory(surface, NULL, NULL, sub_data[i].pointer, d3d_pixel_format, sub_data[i].row_pitch,
			NULL, &rect, D3DX_FILTER_NONE, 0)))
		{
			lwar << "Cannot load surface from memory";
			texture2D->Release();
			return false;
		};
		if(FAILED(surface->Release()))
			lwar << "Cannot release surface";
		_reduceTmpValues(tmp_width, tmp_height, tmp_depth);
	};
	tex = texture2D;
	return true;
};

bool LoadableTexture::_createTextureCube(const shared_array<DSUBRES_DATA>& sub_data)
{
	IDirect3DCubeTexture9* textureCube;
	D3DFORMAT d3d_pixel_format = _getD3DFormat(pixel_format);
	if(FAILED(gs::GraphicDriver::ptr()->getDevice()->CreateCubeTexture(width, mip_count, 0, d3d_pixel_format, D3DPOOL_MANAGED, &textureCube, NULL)))
	{
		lwar << "Cannot create texture 2D";
		return false;
	};
	IDirect3DSurface9* surface;
	unsigned int tmp_width;
	unsigned int tmp_height;
	unsigned int tmp_depth;
	for(unsigned int k = 0; k < 6; ++k)
	{
		tmp_width = width;
		tmp_height = height;
		tmp_depth = depth;
		for(unsigned int i = 0; i < mip_count; ++i)
		{
			if(FAILED(textureCube->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(k), i, &surface)))
			{
				lwar << "Cannot get cube map face " << k << " and level " << i;
				textureCube->Release();
				return false;
			};
			RECT rect = {0, 0, tmp_width, tmp_height};
			if(FAILED(D3DXLoadSurfaceFromMemory(surface, NULL, NULL, sub_data[i + k*mip_count].pointer, d3d_pixel_format, sub_data[i + k*mip_count].row_pitch,
				NULL, &rect, D3DX_FILTER_NONE, 0)))
			{
				lwar << "Cannot load surface from memory";
				textureCube->Release();
				return false;
			};
			if(FAILED(surface->Release()))
				lwar << "Cannot release surface";
		};
	};
	tex = textureCube;
	return true;
};


bool LoadableTexture::_createTextureVolume(const shared_array<DSUBRES_DATA>& sub_data)
{
	/*D3D10_TEXTURE3D_DESC tex_desc;
	D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc;
	_fillD3DStructures<D3D10_TEXTURE3D_DESC, D3D10_TEX3D_SRV>(tex_desc, srv_desc.Texture3D);
	tex_desc.Depth = depth;

	ID3D10Texture3D* tex;
	if(FAILED(gs::GraphicDriver::ptr()->getDevice()->CreateTexture3D(&tex_desc, sub_data.get(), &tex)))
	{
		lwar << "Cannot create volume texture";
		return false;
	};
	texture = tex;
	srv_desc.Format = (DXGI_FORMAT)pixel_format;
	srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE3D;
	if(FAILED(gs::GraphicDriver::ptr()->getDevice()->CreateShaderResourceView(tex, &srv_desc, &srv)))
	{
		lwar << "Cannot create shader resource view";
		return false;
	};*/
	return true;
};

#endif
