#include "GeneralLoader.h"
#include "..\Core\Dx11Includes.h"
#include "..\Core\Core.h"
#include "ObjReader.h"

HRESULT CreateWICTextureFromFileEx( ID3D11Device* d3dDevice,
											 ID3D11DeviceContext* d3dContext,
											 const wchar_t* fileName,
											 size_t maxsize,
											 D3D11_USAGE usage,
											 unsigned int bindFlags,
											 unsigned int cpuAccessFlags,
											 unsigned int miscFlags,
											 bool forceSRGB,
											 ID3D11Resource** texture,
											 ID3D11ShaderResourceView** textureView );

void* Oyster::Graphics::Loading::LoadTexture(const wchar_t filename[])
{
	ID3D11ShaderResourceView* srv = NULL;
	HRESULT hr = CreateWICTextureFromFileEx(Core::device,Core::deviceContext,filename,0,D3D11_USAGE_DEFAULT,D3D11_BIND_SHADER_RESOURCE,0,0,false,NULL,&srv);
	if(hr!=S_OK)
	{
		return NULL;
	}
	else
	{
		return srv;
	}
}

void Oyster::Graphics::Loading::UnloadTexture(void* data)
{
	ID3D11ShaderResourceView* srv = (ID3D11ShaderResourceView*)data;
	SAFE_RELEASE(srv);
}

void* Oyster::Graphics::Loading::LoadOBJ(const wchar_t filename[])
{
	FileLoaders::ObjReader obj;
	obj.LoadFile(filename);
	Model::ModelInfo* info = new Model::ModelInfo();
	Oyster::FileLoaders::ObjReader::Vertex* vdata;
	int count;
	obj.GetVertexData(&vdata, count);
	info->Vertices = new Core::Buffer();
	Core::Buffer::BUFFER_INIT_DESC desc;
	desc.ElementSize = sizeof(FileLoaders::ObjReader::Vertex);
	desc.NumElements = count;
	desc.InitData = vdata;
	desc.Type = Core::Buffer::VERTEX_BUFFER;
	desc.Usage = Core::Buffer::BUFFER_USAGE_IMMUTABLE;

	info->VertexCount = count;
	info->Vertices->Init(desc);
	info->Indexed = false;

	void* texture = Core::loader.LoadResource((std::wstring(filename)+ L".png").c_str(),Graphics::Loading::LoadTexture, Graphics::Loading::UnloadTexture);

	info->Material.push_back((ID3D11ShaderResourceView*)texture);

	return info;
}

void Oyster::Graphics::Loading::UnloadOBJ(void* data)
{
	Model::ModelInfo* info = (Model::ModelInfo*) data;
	SAFE_DELETE(info->Vertices);
	if(info->Indexed)
	{
		SAFE_DELETE(info->Indecies);
	}
	for(UINT i =0;i<info->Material.size();++i)
	{
		Core::loader.ReleaseResource(info->Material[i]);
	}
	delete info;
}

#include <wrl.h>
#include <memory>
#include <cassert>

#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
#include <d2d1.h>
#endif

#pragma warning(push)
#pragma warning(disable : 4005)
#include <wincodec.h>
#pragma warning(pop)


template<class T> class ScopedObject : public Microsoft::WRL::ComPtr<T> {};

//-------------------------------------------------------------------------------------
// WIC Pixel Format Translation Data
//-------------------------------------------------------------------------------------
struct WICTranslate
{
	GUID                wic;
	DXGI_FORMAT         format;
};

static WICTranslate g_WICFormats[] = 
{
	{ GUID_WICPixelFormat128bppRGBAFloat,       DXGI_FORMAT_R32G32B32A32_FLOAT },

	{ GUID_WICPixelFormat64bppRGBAHalf,         DXGI_FORMAT_R16G16B16A16_FLOAT },
	{ GUID_WICPixelFormat64bppRGBA,             DXGI_FORMAT_R16G16B16A16_UNORM },

	{ GUID_WICPixelFormat32bppRGBA,             DXGI_FORMAT_R8G8B8A8_UNORM },
	{ GUID_WICPixelFormat32bppBGRA,             DXGI_FORMAT_B8G8R8A8_UNORM }, // DXGI 1.1
	{ GUID_WICPixelFormat32bppBGR,              DXGI_FORMAT_B8G8R8X8_UNORM }, // DXGI 1.1

	{ GUID_WICPixelFormat32bppRGBA1010102XR,    DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM }, // DXGI 1.1
	{ GUID_WICPixelFormat32bppRGBA1010102,      DXGI_FORMAT_R10G10B10A2_UNORM },

#ifdef DXGI_1_2_FORMATS

	{ GUID_WICPixelFormat16bppBGRA5551,         DXGI_FORMAT_B5G5R5A1_UNORM },
	{ GUID_WICPixelFormat16bppBGR565,           DXGI_FORMAT_B5G6R5_UNORM },

#endif // DXGI_1_2_FORMATS

	{ GUID_WICPixelFormat32bppGrayFloat,        DXGI_FORMAT_R32_FLOAT },
	{ GUID_WICPixelFormat16bppGrayHalf,         DXGI_FORMAT_R16_FLOAT },
	{ GUID_WICPixelFormat16bppGray,             DXGI_FORMAT_R16_UNORM },
	{ GUID_WICPixelFormat8bppGray,              DXGI_FORMAT_R8_UNORM },

	{ GUID_WICPixelFormat8bppAlpha,             DXGI_FORMAT_A8_UNORM },
};

//-------------------------------------------------------------------------------------
// WIC Pixel Format nearest conversion table
//-------------------------------------------------------------------------------------

struct WICConvert
{
	GUID        source;
	GUID        target;
};

static WICConvert g_WICConvert[] = 
{
	// Note target GUID in this conversion table must be one of those directly supported formats (above).

	{ GUID_WICPixelFormatBlackWhite,            GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM

	{ GUID_WICPixelFormat1bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
	{ GUID_WICPixelFormat2bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
	{ GUID_WICPixelFormat4bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
	{ GUID_WICPixelFormat8bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 

	{ GUID_WICPixelFormat2bppGray,              GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM 
	{ GUID_WICPixelFormat4bppGray,              GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM 

	{ GUID_WICPixelFormat16bppGrayFixedPoint,   GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT 
	{ GUID_WICPixelFormat32bppGrayFixedPoint,   GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT 

#ifdef DXGI_1_2_FORMATS

	{ GUID_WICPixelFormat16bppBGR555,           GUID_WICPixelFormat16bppBGRA5551 }, // DXGI_FORMAT_B5G5R5A1_UNORM

#else

	{ GUID_WICPixelFormat16bppBGR555,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM
	{ GUID_WICPixelFormat16bppBGRA5551,         GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM
	{ GUID_WICPixelFormat16bppBGR565,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM

#endif // DXGI_1_2_FORMATS

	{ GUID_WICPixelFormat32bppBGR101010,        GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM

	{ GUID_WICPixelFormat24bppBGR,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
	{ GUID_WICPixelFormat24bppRGB,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
	{ GUID_WICPixelFormat32bppPBGRA,            GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
	{ GUID_WICPixelFormat32bppPRGBA,            GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 

	{ GUID_WICPixelFormat48bppRGB,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
	{ GUID_WICPixelFormat48bppBGR,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
	{ GUID_WICPixelFormat64bppBGRA,             GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
	{ GUID_WICPixelFormat64bppPRGBA,            GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
	{ GUID_WICPixelFormat64bppPBGRA,            GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM

	{ GUID_WICPixelFormat48bppRGBFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
	{ GUID_WICPixelFormat48bppBGRFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
	{ GUID_WICPixelFormat64bppRGBAFixedPoint,   GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
	{ GUID_WICPixelFormat64bppBGRAFixedPoint,   GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
	{ GUID_WICPixelFormat64bppRGBFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
	{ GUID_WICPixelFormat64bppRGBHalf,          GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
	{ GUID_WICPixelFormat48bppRGBHalf,          GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 

	{ GUID_WICPixelFormat128bppPRGBAFloat,      GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
	{ GUID_WICPixelFormat128bppRGBFloat,        GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
	{ GUID_WICPixelFormat128bppRGBAFixedPoint,  GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
	{ GUID_WICPixelFormat128bppRGBFixedPoint,   GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
	{ GUID_WICPixelFormat32bppRGBE,             GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 

	{ GUID_WICPixelFormat32bppCMYK,             GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
	{ GUID_WICPixelFormat64bppCMYK,             GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
	{ GUID_WICPixelFormat40bppCMYKAlpha,        GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
	{ GUID_WICPixelFormat80bppCMYKAlpha,        GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM

#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
	{ GUID_WICPixelFormat32bppRGB,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM
	{ GUID_WICPixelFormat64bppRGB,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
	{ GUID_WICPixelFormat64bppPRGBAHalf,        GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
#endif

	// We don't support n-channel formats
};

static bool g_WIC2 = false;

//--------------------------------------------------------------------------------------

bool _IsWIC2()
{
	return g_WIC2;
}

IWICImagingFactory* _GetWIC()
{
	static IWICImagingFactory* s_Factory = nullptr;

	if ( s_Factory )
		return s_Factory;

#if(_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
	HRESULT hr = CoCreateInstance(
		CLSID_WICImagingFactory2,
		nullptr,
		CLSCTX_INPROC_SERVER,
		__uuidof(IWICImagingFactory2),
		(LPVOID*)&s_Factory
		);

	if ( SUCCEEDED(hr) )
	{
		// WIC2 is available on Windows 8 and Windows 7 SP1 with KB 2670838 installed
		g_WIC2 = true;
	}
	else
	{
		hr = CoCreateInstance(
			CLSID_WICImagingFactory1,
			nullptr,
			CLSCTX_INPROC_SERVER,
			__uuidof(IWICImagingFactory),
			(LPVOID*)&s_Factory
			);

		if ( FAILED(hr) )
		{
			s_Factory = nullptr;
			return nullptr;
		}
	}
#else
	HRESULT hr = CoCreateInstance(
		CLSID_WICImagingFactory,
		nullptr,
		CLSCTX_INPROC_SERVER,
		__uuidof(IWICImagingFactory),
		(LPVOID*)&s_Factory
		);

	if ( FAILED(hr) )
	{
		s_Factory = nullptr;
		return nullptr;
	}
#endif

	return s_Factory;
}


//---------------------------------------------------------------------------------
static DXGI_FORMAT _WICToDXGI( const GUID& guid )
{
	for( size_t i=0; i < _countof(g_WICFormats); ++i )
	{
		if ( memcmp( &g_WICFormats[i].wic, &guid, sizeof(GUID) ) == 0 )
			return g_WICFormats[i].format;
	}

#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
	if ( g_WIC2 )
	{
		if ( memcmp( &GUID_WICPixelFormat96bppRGBFloat, &guid, sizeof(GUID) ) == 0 )
			return DXGI_FORMAT_R32G32B32_FLOAT;
	}
#endif

	return DXGI_FORMAT_UNKNOWN;
}

//---------------------------------------------------------------------------------
static size_t _WICBitsPerPixel( REFGUID targetGuid )
{
	IWICImagingFactory* pWIC = _GetWIC();
	if ( !pWIC )
		return 0;
 
	ScopedObject<IWICComponentInfo> cinfo;
	if ( FAILED( pWIC->CreateComponentInfo( targetGuid, &cinfo ) ) )
		return 0;

	WICComponentType type;
	if ( FAILED( cinfo->GetComponentType( &type ) ) )
		return 0;

	if ( type != WICPixelFormat )
		return 0;

	Microsoft::WRL::ComPtr<IWICPixelFormatInfo> pfinfo;
	if ( FAILED( cinfo.As( &pfinfo ) ) )
		return 0;

	UINT bpp;
	if ( FAILED( pfinfo->GetBitsPerPixel( &bpp ) ) )
		return 0;

	return bpp;
}


//--------------------------------------------------------------------------------------
static DXGI_FORMAT MakeSRGB( _In_ DXGI_FORMAT format )
{
	switch( format )
	{
	case DXGI_FORMAT_R8G8B8A8_UNORM:
		return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;

	case DXGI_FORMAT_BC1_UNORM:
		return DXGI_FORMAT_BC1_UNORM_SRGB;

	case DXGI_FORMAT_BC2_UNORM:
		return DXGI_FORMAT_BC2_UNORM_SRGB;

	case DXGI_FORMAT_BC3_UNORM:
		return DXGI_FORMAT_BC3_UNORM_SRGB;

	case DXGI_FORMAT_B8G8R8A8_UNORM:
		return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;

	case DXGI_FORMAT_B8G8R8X8_UNORM:
		return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;

	case DXGI_FORMAT_BC7_UNORM:
		return DXGI_FORMAT_BC7_UNORM_SRGB;

	default:
		return format;
	}
}

static HRESULT CreateTextureFromWIC( _In_ ID3D11Device* d3dDevice,
									 _In_opt_ ID3D11DeviceContext* d3dContext,
									 _In_ IWICBitmapFrameDecode *frame,
									 _In_ size_t maxsize,
									 _In_ D3D11_USAGE usage,
									 _In_ unsigned int bindFlags,
									 _In_ unsigned int cpuAccessFlags,
									 _In_ unsigned int miscFlags,
									 _In_ bool forceSRGB,
									 _Out_opt_ ID3D11Resource** texture,
									 _Out_opt_ ID3D11ShaderResourceView** textureView )
{
	UINT width, height;
	HRESULT hr = frame->GetSize( &width, &height );
	if ( FAILED(hr) )
		return hr;

	assert( width > 0 && height > 0 );

	if ( !maxsize )
	{
		// This is a bit conservative because the hardware could support larger textures than
		// the Feature Level defined minimums, but doing it this way is much easier and more
		// performant for WIC than the 'fail and retry' model used by DDSTextureLoader

		switch( d3dDevice->GetFeatureLevel() )
		{
			case D3D_FEATURE_LEVEL_9_1:
			case D3D_FEATURE_LEVEL_9_2:
				maxsize = 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
				break;

			case D3D_FEATURE_LEVEL_9_3:
				maxsize = 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
				break;

			case D3D_FEATURE_LEVEL_10_0:
			case D3D_FEATURE_LEVEL_10_1:
				maxsize = 8192 /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
				break;

			default:
				maxsize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
				break;
		}
	}

	assert( maxsize > 0 );

	UINT twidth, theight;
	if ( width > maxsize || height > maxsize )
	{
		float ar = static_cast<float>(height) / static_cast<float>(width);
		if ( width > height )
		{
			twidth = static_cast<UINT>( maxsize );
			theight = static_cast<UINT>( static_cast<float>(maxsize) * ar );
		}
		else
		{
			theight = static_cast<UINT>( maxsize );
			twidth = static_cast<UINT>( static_cast<float>(maxsize) / ar );
		}
		assert( twidth <= maxsize && theight <= maxsize );
	}
	else
	{
		twidth = width;
		theight = height;
	}

	// Determine format
	WICPixelFormatGUID pixelFormat;
	hr = frame->GetPixelFormat( &pixelFormat );
	if ( FAILED(hr) )
		return hr;

	WICPixelFormatGUID convertGUID;
	memcpy( &convertGUID, &pixelFormat, sizeof(WICPixelFormatGUID) );

	size_t bpp = 0;

	DXGI_FORMAT format = _WICToDXGI( pixelFormat );
	if ( format == DXGI_FORMAT_UNKNOWN )
	{
		if ( memcmp( &GUID_WICPixelFormat96bppRGBFixedPoint, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
		{
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
			if ( g_WIC2 )
			{
				memcpy( &convertGUID, &GUID_WICPixelFormat96bppRGBFloat, sizeof(WICPixelFormatGUID) );
				format = DXGI_FORMAT_R32G32B32_FLOAT;
			}
			else
#endif
			{
				memcpy( &convertGUID, &GUID_WICPixelFormat128bppRGBAFloat, sizeof(WICPixelFormatGUID) );
				format = DXGI_FORMAT_R32G32B32A32_FLOAT;
			}
		}
		else
		{
			for( size_t i=0; i < _countof(g_WICConvert); ++i )
			{
				if ( memcmp( &g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
				{
					memcpy( &convertGUID, &g_WICConvert[i].target, sizeof(WICPixelFormatGUID) );

					format = _WICToDXGI( g_WICConvert[i].target );
					assert( format != DXGI_FORMAT_UNKNOWN );
					bpp = _WICBitsPerPixel( convertGUID );
					break;
				}
			}
		}

		if ( format == DXGI_FORMAT_UNKNOWN )
			return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
	}
	else
	{
		bpp = _WICBitsPerPixel( pixelFormat );
	}

#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
	if ( (format == DXGI_FORMAT_R32G32B32_FLOAT) && d3dContext != 0 && textureView != 0 )
	{
		// Special case test for optional device support for autogen mipchains for R32G32B32_FLOAT 
		UINT fmtSupport = 0;
		hr = d3dDevice->CheckFormatSupport( DXGI_FORMAT_R32G32B32_FLOAT, &fmtSupport );
		if ( FAILED(hr) || !( fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN ) )
		{
			// Use R32G32B32A32_FLOAT instead which is required for Feature Level 10.0 and up
			memcpy( &convertGUID, &GUID_WICPixelFormat128bppRGBAFloat, sizeof(WICPixelFormatGUID) );
			format = DXGI_FORMAT_R32G32B32A32_FLOAT;
			bpp = 128;
		}
	}
#endif

	if ( !bpp )
		return E_FAIL;

	// Handle sRGB formats
	if ( forceSRGB )
	{
		format = MakeSRGB( format );
	}
	else
	{
		ScopedObject<IWICMetadataQueryReader> metareader;
		if ( SUCCEEDED( frame->GetMetadataQueryReader( &metareader ) ) )
		{
			GUID containerFormat;
			if ( SUCCEEDED( metareader->GetContainerFormat( &containerFormat ) ) )
			{
				// Check for sRGB colorspace metadata
				bool sRGB = false;

				PROPVARIANT value;
				PropVariantInit( &value );

				if ( memcmp( &containerFormat, &GUID_ContainerFormatPng, sizeof(GUID) ) == 0 )
				{
					// Check for sRGB chunk
					if ( SUCCEEDED( metareader->GetMetadataByName( L"/sRGB/RenderingIntent", &value ) ) && value.vt == VT_UI1 )
					{
						sRGB = true;
					}
				}
				else if ( SUCCEEDED( metareader->GetMetadataByName( L"System.Image.ColorSpace", &value ) ) && value.vt == VT_UI2 && value.uiVal == 1 )
				{
					sRGB = true;
				}

				PropVariantClear( &value );

				if ( sRGB )
					format = MakeSRGB( format );
			}
		}
	}

	// Verify our target format is supported by the current device
	// (handles WDDM 1.0 or WDDM 1.1 device driver cases as well as DirectX 11.0 Runtime without 16bpp format support)
	UINT support = 0;
	hr = d3dDevice->CheckFormatSupport( format, &support );
	if ( FAILED(hr) || !(support & D3D11_FORMAT_SUPPORT_TEXTURE2D) )
	{
		// Fallback to RGBA 32-bit format which is supported by all devices
		memcpy( &convertGUID, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) );
		format = DXGI_FORMAT_R8G8B8A8_UNORM;
		bpp = 32;
	}

	// Allocate temporary memory for image
	size_t rowPitch = ( twidth * bpp + 7 ) / 8;
	size_t imageSize = rowPitch * theight;

	std::unique_ptr<uint8_t[]> temp( new (std::nothrow) uint8_t[ imageSize ] );
	if (!temp)
		return E_OUTOFMEMORY;

	// Load image data
	if ( memcmp( &convertGUID, &pixelFormat, sizeof(GUID) ) == 0
		 && twidth == width
		 && theight == height )
	{
		// No format conversion or resize needed
		hr = frame->CopyPixels( 0, static_cast<UINT>( rowPitch ), static_cast<UINT>( imageSize ), temp.get() );  
		if ( FAILED(hr) )
			return hr;
	}
	else if ( twidth != width || theight != height )
	{
		// Resize
		IWICImagingFactory* pWIC = _GetWIC();
		if ( !pWIC )
			return E_NOINTERFACE;

		ScopedObject<IWICBitmapScaler> scaler;
		hr = pWIC->CreateBitmapScaler( &scaler );
		if ( FAILED(hr) )
			return hr;

		hr = scaler->Initialize( frame, twidth, theight, WICBitmapInterpolationModeFant );
		if ( FAILED(hr) )
			return hr;

		WICPixelFormatGUID pfScaler;
		hr = scaler->GetPixelFormat( &pfScaler );
		if ( FAILED(hr) )
			return hr;

		if ( memcmp( &convertGUID, &pfScaler, sizeof(GUID) ) == 0 )
		{
			// No format conversion needed
			hr = scaler->CopyPixels( 0, static_cast<UINT>( rowPitch ), static_cast<UINT>( imageSize ), temp.get() );  
			if ( FAILED(hr) )
				return hr;
		}
		else
		{
			ScopedObject<IWICFormatConverter> FC;
			hr = pWIC->CreateFormatConverter( &FC );
			if ( FAILED(hr) )
				return hr;

			hr = FC->Initialize( scaler.Get(), convertGUID, WICBitmapDitherTypeErrorDiffusion, 0, 0, WICBitmapPaletteTypeCustom );
			if ( FAILED(hr) )
				return hr;

			hr = FC->CopyPixels( 0, static_cast<UINT>( rowPitch ), static_cast<UINT>( imageSize ), temp.get() );  
			if ( FAILED(hr) )
				return hr;
		}
	}
	else
	{
		// Format conversion but no resize
		IWICImagingFactory* pWIC = _GetWIC();
		if ( !pWIC )
			return E_NOINTERFACE;

		ScopedObject<IWICFormatConverter> FC;
		hr = pWIC->CreateFormatConverter( &FC );
		if ( FAILED(hr) )
			return hr;

		hr = FC->Initialize( frame, convertGUID, WICBitmapDitherTypeErrorDiffusion, 0, 0, WICBitmapPaletteTypeCustom );
		if ( FAILED(hr) )
			return hr;

		hr = FC->CopyPixels( 0, static_cast<UINT>( rowPitch ), static_cast<UINT>( imageSize ), temp.get() );  
		if ( FAILED(hr) )
			return hr;
	}

	// See if format is supported for auto-gen mipmaps (varies by feature level)
	bool autogen = false;
	if ( d3dContext != 0 && textureView != 0 ) // Must have context and shader-view to auto generate mipmaps
	{
		UINT fmtSupport = 0;
		hr = d3dDevice->CheckFormatSupport( format, &fmtSupport );
		if ( SUCCEEDED(hr) && ( fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN ) )
		{
			autogen = true;
			autogen = false;
		}
	}

	// Create texture
	D3D11_TEXTURE2D_DESC desc;
	desc.Width = twidth;
	desc.Height = theight;
	desc.MipLevels = (autogen) ? 0 : 1;
	desc.ArraySize = 1;
	desc.Format = format;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = usage;
	desc.CPUAccessFlags = cpuAccessFlags;

	if ( autogen )
	{
		desc.BindFlags = bindFlags | D3D11_BIND_RENDER_TARGET;
		desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_GENERATE_MIPS;
	}
	else
	{
		desc.BindFlags = bindFlags;
		desc.MiscFlags = miscFlags;
	}

	D3D11_SUBRESOURCE_DATA initData;
	initData.pSysMem = temp.get();
	initData.SysMemPitch = static_cast<UINT>( rowPitch );
	initData.SysMemSlicePitch = static_cast<UINT>( imageSize );

	ID3D11Texture2D* tex = nullptr;
	//Error with miscFlags Generate Mips
	hr = d3dDevice->CreateTexture2D( &desc, (autogen) ? nullptr : &initData, &tex );
	/// Replace To get Texture Data
	if ( SUCCEEDED(hr) && tex != 0 )
	{
		if (textureView != 0)
		{
			D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
			memset( &SRVDesc, 0, sizeof( SRVDesc ) );
			SRVDesc.Format = desc.Format;

			SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
			SRVDesc.Texture2D.MipLevels = (autogen) ? -1 : 1;
			//TODO calc mipmap data

			hr = d3dDevice->CreateShaderResourceView( tex, &SRVDesc, textureView );
			if ( FAILED(hr) )
			{
				tex->Release();
				return hr;
			}
			//todo check calc
			int TexSize = twidth * theight * (int)bpp;
			Oyster::Graphics::Core::UsedMem += TexSize;

			if ( autogen )
			{
				assert( d3dContext != 0 );
				d3dContext->UpdateSubresource( tex, 0, nullptr, temp.get(), static_cast<UINT>(rowPitch), static_cast<UINT>(imageSize) );
				d3dContext->GenerateMips( *textureView );
			}
		}

		if (texture != 0)
		{
			*texture = tex;
		}
		else
		{
			//SetDebugObjectName(tex, "WICTextureLoader");
			tex->Release();
		}
	}

	return hr;
}

HRESULT CreateWICTextureFromFileEx( ID3D11Device* d3dDevice,
											 ID3D11DeviceContext* d3dContext,
											 const wchar_t* fileName,
											 size_t maxsize,
											 D3D11_USAGE usage,
											 unsigned int bindFlags,
											 unsigned int cpuAccessFlags,
											 unsigned int miscFlags,
											 bool forceSRGB,
											 ID3D11Resource** texture,
											 ID3D11ShaderResourceView** textureView )
{
	if ( texture )
	{
		*texture = nullptr;
	}
	if ( textureView )
	{
		*textureView = nullptr;
	}

	if (!d3dDevice || !fileName || (!texture && !textureView))
		return E_INVALIDARG;

	IWICImagingFactory* pWIC = _GetWIC();
	if ( !pWIC )
		return E_NOINTERFACE;

	// Initialize WIC
	ScopedObject<IWICBitmapDecoder> decoder;
	HRESULT hr = pWIC->CreateDecoderFromFilename( fileName, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder );
	if ( FAILED(hr) )
		return hr;

	ScopedObject<IWICBitmapFrameDecode> frame;
	hr = decoder->GetFrame( 0, &frame );
	if ( FAILED(hr) )
		return hr;

	hr = CreateTextureFromWIC( d3dDevice, d3dContext, frame.Get(), maxsize,
							   usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
							   texture, textureView );

	return hr;
}