#include"CVideoCardDriverD3D9.h"

#include"CSurfaceBufferD3D9.h"
#include"CTextureBufferD3D9.h"
#include"CIndexBufferD3D9.h"
#include"CVertexBufferD3D9.h"
#include"CVertexShaderBufferD3D9.h"
#include"CPixelShaderBufferD3D9.h"
#include"CVertexDeclarationBufferD3D9.h"
#include"../../CPlaneTransiter.h"

#include"../../../../../Auxiliary/Debug/CException.h"
#include"../../../../../Auxiliary/Macro.h"

namespace Maid
{

	//	ここには CreateXXX, と SetXXX, GetXXX を書く

	// 2 の累乗の値に変換する
inline int ExtendSize ( int size )
{
	int ret = 0;
	if( size<=   1 ) { ret=   1; }
	ef( size<=   2 ) { ret=   2; }
	ef( size<=   4 ) { ret=   4; }
	ef( size<=   8 ) { ret=   8; }
	ef( size<=  16 ) { ret=  16; }
	ef( size<=  32 ) { ret=  32; }
	ef( size<=  64 ) { ret=  64; }
	ef( size<= 128 ) { ret= 128; }
	ef( size<= 256 ) { ret= 256; }
	ef( size<= 512 ) { ret= 512; }
	ef( size<=1024 ) { ret=1024; }
	ef( size<=2048 ) { ret=2048; }
	ef( size<=4096 ) { ret=4096; }
	else { MAID_ASSERT( true, "テクスチャが大きすぎます" ); }

	return ret;
}

SPSURFACEBUFFER CVideoCardDriverD3D9::CreateSurface( const SIZE2DI& size, PIXELFORMAT Format )
{
	const SPD3DDEVICE& pDevice = m_pDevice;

	IDirect3DSurface9* pBuffer=NULL;
	
	switch( Format )
	{
	case PIXELFORMAT_D16:
	case PIXELFORMAT_D32:
	case PIXELFORMAT_D24X8:
	case PIXELFORMAT_D24S8:
		{
			const D3DFORMAT fmt = PIXELFORMATtoD3DFORMAT( Format );
			const HRESULT ret = pDevice->CreateDepthStencilSurface( size.w, size.h, fmt, D3DMULTISAMPLE_NONE, 0, FALSE, &pBuffer, NULL );
			if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::CreateDepthStencilSurface()")); }
		}break;

	default:
		{
			MySTL::vector<PIXELFORMAT> type = EnumFormat( Format );
			for( int i=0; i<(int)type.size(); ++i )
			{
				const D3DFORMAT fmt = PIXELFORMATtoD3DFORMAT( type[i] );

				const HRESULT ret = pDevice->CreateRenderTarget( size.w, size.h, fmt, D3DMULTISAMPLE_NONE, 0, FALSE, &pBuffer, NULL );
				if( SUCCEEDED(ret) ) { break; }

			}
			if( pBuffer==NULL ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::CreateRenderTarget()")); }

		}break;
	}

	return SPSURFACEBUFFER( new CSurfaceBufferD3D9(pBuffer) );
}


//! IVideoCardDriver::CreateTextureBuffer() を参照
SPTEXTUREBUFFER CVideoCardDriverD3D9::CreateTextureBuffer( const CTextureBufferMemory& buffer )
{
	boost::shared_ptr<CTextureBufferD3D9> pTex;
	{
		IDirect3DTexture9* p=NULL;

		const SIZE2DI size = buffer.GetSize();
		//	テクスチャは 2の累乗で作る
		const SIZE2DI extend = SIZE2DI(ExtendSize(size.w),ExtendSize(size.h));
		const UINT  mip = 1;
		const DWORD usage = 0; // D3DUSAGE_AUTOGENMIPMAP
		const D3DPOOL pool = D3DPOOL_MANAGED;

		MySTL::vector<PIXELFORMAT> type = EnumFormat( buffer.GetPixelFormat() );

		//	ピクセルフォーマットが違う可能性があるので、似ているものを列挙して作る

		SPD3DDEVICE& pDevice = m_pDevice;
		for( int i=0; i<(int)type.size(); ++i )
		{
			HRESULT ret;
			const D3DFORMAT fmt = PIXELFORMATtoD3DFORMAT(type[i]);

			{
				ret = pDevice->CreateTexture( extend.w, extend.h, mip, usage, fmt, pool, &p, NULL );
				if( SUCCEEDED(ret) ) { break; }
			}

			{
				// それがダメだったら正方形テクスチャでチャレンジ
				const int len = max(extend.w,extend.h);
				ret = pDevice->CreateTexture( len, len, mip, usage, fmt, pool, &p, NULL );
				if( SUCCEEDED(ret) ) { break; }
			}

			//	それでもダメならピクセルフォーマットを変えて
		}

		MAID_ASSERT( p==NULL, MAIDTEXT("IDirect3DDevice9::CreateTexture()") );

		pTex.reset( new CTextureBufferD3D9( p ) );
	}

	const RECT2DI rc( POINT2DI(0,0), buffer.GetSize() );

	ISurfaceBuffer& dst = pTex->GetSurface(0);
	const ISurfaceBuffer& src = const_cast<CTextureBufferMemory&>(buffer).GetSurface(0);

	CPlaneTransiter::Blt( dst, &rc, src, &rc );

	return pTex;
}


SPTEXTUREBUFFER CVideoCardDriverD3D9::CreateRTTextureBuffer( const SIZE2DI& size, PIXELFORMAT Format )
{
	const DWORD		Usage = D3DUSAGE_RENDERTARGET;
	const D3DPOOL	Pool  = D3DPOOL_DEFAULT;

	boost::shared_ptr<CTextureBufferD3D9> pTex;

	{
		SPD3DDEVICE& pDevice = m_pDevice;
		IDirect3DTexture9* p=NULL;

		//	ピクセルフォーマットが違う可能性があるので、似ているものを列挙して作る
		MySTL::vector<PIXELFORMAT> type = EnumFormat( Format );
		for( int i=0; i<(int)type.size(); ++i )
		{
			HRESULT ret;
			const D3DFORMAT fmt = PIXELFORMATtoD3DFORMAT(type[i]);

			ret = pDevice->CreateTexture( size.w,size.h, 0, Usage, fmt, Pool, &p, NULL );
			if( SUCCEEDED(ret) ) { break; }

			// 失敗したら正方形テクスチャでチャレンジ
			const int line = max(size.w,size.h);
			ret = pDevice->CreateTexture( line, line, 0, Usage, fmt, Pool, &p, NULL );
			if( SUCCEEDED(ret) ) { break; }
		}

		MAID_ASSERT( p==NULL, MAIDTEXT("IDirect3DDevice9::CreateTexture()") );

		pTex.reset( new CTextureBufferD3D9( p ) );
	}

	return pTex;
}


//! IVideoCardDriver::CreateIndexBuffer() を参照
SPINDEXBUFFER CVideoCardDriverD3D9::CreateIndexBuffer( const MySTL::vector<unt08>& data, int Format )
{
	SPD3DINDEXBUFFER pBuffer;
	{
		IDirect3DIndexBuffer9* p;
		HRESULT ret;

		const UINT BufferSize = (unt)data.size();
//		const DWORD usage = D3DUSAGE_SOFTWAREPROCESSING;
		const DWORD usage = 0;
		const D3DPOOL pool = D3DPOOL_MANAGED;

		//Format
		D3DFORMAT fmt;

		if( Format==2 ) { fmt = D3DFMT_INDEX16; }
		ef( Format==4 ) { fmt = D3DFMT_INDEX32; }
		else { MAID_ASSERT( true, MAIDTEXT("不明なサイズ ") << Format ); }

		ret = m_pDevice->CreateIndexBuffer( BufferSize, usage, fmt, pool, &p, NULL );
		if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::CreateIndexBuffer()")); }

		pBuffer.reset(p);
	}


	{
		HRESULT ret;
		void* pDst;
		ret = pBuffer->Lock( 0, 0, &pDst, 0 );
		if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3IndexBuffer9::Lock()")); }

		::memcpy( pDst, &(data[0]), data.size() );

		ret = pBuffer->Unlock();
		if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3IndexBuffer9::Unlock()")); }
	}

	boost::shared_ptr<CIndexBufferD3D9> pRet( new CIndexBufferD3D9( pBuffer ) );

	return pRet;
}


//! IVideoCardDriver::CreateVertexBuffer() を参照
SPVERTEXBUFFER CVideoCardDriverD3D9::CreateVertexBuffer( const MySTL::vector<unt08>& data, VERTEXFORMAT Format )
{
	SPD3DVERTEXBUFFER pBuffer;
	HRESULT ret;
	{
		IDirect3DVertexBuffer9* p;

		//	nvidia の開発の鉄人によると ＡＧＰ転送をする際に 32byte 単位にそろえておくといいらしい
//		const DWORD usage = D3DUSAGE_SOFTWAREPROCESSING;
		const DWORD usage = 0;
		const DWORD fvf = VERTEXFORMAT2FVF(Format);
		const D3DPOOL pool = D3DPOOL_MANAGED;
		const unt BufferSize = ((unt)data.size() + 31) & (~31);
		ret = m_pDevice->CreateVertexBuffer( BufferSize, usage, fvf, pool, &p, NULL );
		if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::CreateVertexBuffer()")); }

		pBuffer.reset(p);
	}

	{
		void* pDst;
		ret = pBuffer->Lock( 0, 0, &pDst, 0 );
		if( FAILED(ret) ) 
		{
			MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DVertexBuffer9::Lock()"));
		}

		::memcpy( pDst, &(data[0]), data.size() );

		ret = pBuffer->Unlock();
		if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DVertexBuffer9::Unlock()")); }
	}

	boost::shared_ptr<CVertexBufferD3D9> pRet( new CVertexBufferD3D9( pBuffer, GetFVFSize(Format) ) );
	return pRet;
}



//! IVideoCardDriver::CreateVertexShader() を参照
SPVERTEXSHADERBUFFER CVideoCardDriverD3D9::CreateVertexShader( const mstring& Name )
{
	return m_pVertexShaderCompiler->Compile( m_pDevice, Name );
}

//! IVideoCardDriver::CreateVertexShader() を参照
SPVERTEXSHADERBUFFER CVideoCardDriverD3D9::CreateVertexShader( const MySTL::vector<unt08>& Code, SHADERCOMPILE type )
{
	return m_pVertexShaderCompiler->Compile( m_pDevice, Code, type );
}



//! IVideoCardDriver::CreatePixelShader() を参照
SPPIXELSHADERBUFFER CVideoCardDriverD3D9::CreatePixelShader( const mstring& Name )
{
	return m_pPixelShaderCompiler->Compile( Name );
}

//! IVideoCardDriver::CreatePixelShader() を参照
SPPIXELSHADERBUFFER CVideoCardDriverD3D9::CreatePixelShader( const MySTL::vector<unt08>& Code, SHADERCOMPILE type )
{
	return m_pPixelShaderCompiler->Compile( Code, type );
}


void CVideoCardDriverD3D9::SetRenderTarget( const ISurfaceBuffer* pBuffer )
{
	const CSurfaceBufferD3D9* p = static_cast<const CSurfaceBufferD3D9*>(pBuffer);

	HRESULT ret;	
	ret = m_pDevice->SetRenderTarget( 0, p->pSurface.get() );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetRenderTarget()")); }
}

void CVideoCardDriverD3D9::ResetRenderTarget()
{
	SPD3DSURFACEBUFFER pBuff;

	if( IsAspectLock() )
	{
		pBuff = m_pAspectLockBuffer;
	}else
	{
		IDirect3DSurface9* p;
		HRESULT ret;	
		ret = m_pDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &p );
		if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::GetBackBuffer()")); }

		pBuff.reset(p);
	}

	{
		HRESULT ret;	
		ret = m_pDevice->SetRenderTarget( 0, pBuff.get() );
		if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetRenderTarget()")); }
	}
}

void CVideoCardDriverD3D9::SetDepthStencil( const ISurfaceBuffer* pBuffer )
{
	const CSurfaceBufferD3D9* p = static_cast<const CSurfaceBufferD3D9*>(pBuffer);
	HRESULT ret;	
	ret = m_pDevice->SetDepthStencilSurface( p->pSurface.get() );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetDepthStencilSurface()")); }
}

void CVideoCardDriverD3D9::ResetDepthStencil()
{
	HRESULT ret;	
	ret = m_pDevice->SetDepthStencilSurface( m_pDefaultDepthStencil.get() );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetDepthStencilSurface()")); }
}





//! IVideoCardDriver::UnsetTextureBuffer() を参照
void CVideoCardDriverD3D9::UnsetTextureBuffer( int stage )
{
	HRESULT ret;	
	ret = m_pDevice->SetTexture( stage, NULL );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetTexture()")); }
}



//! IVideoCardDriver::SetTextureBuffer() を参照
void CVideoCardDriverD3D9::SetTextureBuffer( const SPTEXTUREBUFFER& pBuffer, int stage )
{
	MAID_ASSERT( pBuffer.get()==NULL, "NULL" );
	CTextureBufferD3D9* p = static_cast<CTextureBufferD3D9*>(pBuffer.get());

	HRESULT ret;	
	ret = m_pDevice->SetTexture( stage, p->pTexture.get() );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetTextureBuffer()")); }
}

void CVideoCardDriverD3D9::SetIndexBuffer( const SPINDEXBUFFER& pBuffer )
{
	MAID_ASSERT( pBuffer.get()==NULL, "NULL" );
	CIndexBufferD3D9* p = static_cast<CIndexBufferD3D9*>(pBuffer.get());

	HRESULT ret;	
	ret = m_pDevice->SetIndices( p->pBuffer.get() );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetIndices()")); }
}

//! IVideoCardDriver::SetVertexBuffer() を参照
void CVideoCardDriverD3D9::SetVertexBuffer( const SPVERTEXBUFFER& pBuffer, int pos )
{
	MAID_ASSERT( pBuffer.get()==NULL, "NULL" );
	CVertexBufferD3D9* p = static_cast<CVertexBufferD3D9*>(pBuffer.get());

	HRESULT ret;	
	ret = m_pDevice->SetStreamSource( pos, p->pBuffer.get(), 0, p->Stride );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetStreamSource()")); }
}


//! IVideoCardDriver::SetVertexShader() を参照
void CVideoCardDriverD3D9::SetVertexShader( const SPVERTEXSHADERBUFFER& pShader )
{
	MAID_ASSERT( pShader.get()==NULL, "NULL" );

	CVertexShaderBufferD3D9* p = static_cast<CVertexShaderBufferD3D9*>(pShader.get());
	p->Setup(m_pDevice);
}

//! IVideoCardDriver::SetPixelShader() を参照
void CVideoCardDriverD3D9::SetPixelShader( const SPPIXELSHADERBUFFER& pShader )
{
	MAID_ASSERT( pShader.get()==NULL, "NULL" );

	CPixelShaderBufferD3D9* p = static_cast<CPixelShaderBufferD3D9*>(pShader.get());
	p->Setup(m_pDevice);
}

void CVideoCardDriverD3D9::SetVertexDeclaration( const SPVERTEXDECLARATIONBUFFER& pDecl )
{
	CVertexDeclarationBufferD3D9* p = static_cast<CVertexDeclarationBufferD3D9*>(pDecl.get());

	HRESULT ret;	
	ret = m_pDevice->SetVertexDeclaration( p->pBuffer.get() );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetVertexDeclaration()")); }
}

void CVideoCardDriverD3D9::SetViewPort( const RECT2DI& screen, float MinZ, float MaxZ )
{
	D3DVIEWPORT9 vp;
	vp.X      = screen.x;
	vp.Y      = screen.y;
	vp.Width  = screen.w;
	vp.Height = screen.h;
	vp.MinZ   = MinZ;
	vp.MaxZ   = MaxZ;

	HRESULT ret;	
	ret = m_pDevice->SetViewport( &vp );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetViewPort()")); }
}


void CVideoCardDriverD3D9::SetVertexShaderConstF( int pos, const VECTOR4DF& vec )
{
	HRESULT ret;	
	ret = m_pDevice->SetVertexShaderConstantF( pos, (float*)&vec, 1 );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetVertexShaderConstantF()")); }
}

void CVideoCardDriverD3D9::CopySurface( const SPSURFACEBUFFER& pSrc, SPSURFACEBUFFERMEMORY& pDst )
{
	CSurfaceBufferD3D9* pSurfaceD3D = static_cast<CSurfaceBufferD3D9*>(pSrc.get());
	CSurfaceBufferD3D9 Sys;

	const int w = pSurfaceD3D->Desc.Width;
	const int h = pSurfaceD3D->Desc.Height;
	const D3DFORMAT fmt = pSurfaceD3D->Desc.Format;

	//	まずはシステムメモリに持ってくる
	{
		{
			IDirect3DSurface9* p;

			const HRESULT ret = m_pDevice->CreateOffscreenPlainSurface( w, h, fmt, D3DPOOL_SYSTEMMEM, &p, NULL );
			if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::CreateOffscreenPlainSurface()")); }
			Sys = CSurfaceBufferD3D9(p);
		}

		{
			const HRESULT ret = m_pDevice->GetRenderTargetData( pSurfaceD3D->pSurface.get(), Sys.pSurface.get() );
			if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::GetRenderTargetData()")); }
		}
	}

	{
		pDst.reset( new CSurfaceBufferMemory );
		pDst->Create( SIZE2DI(w,h), PIXELFORMAT_R08G08B08I );
	}

	//	次に転送
	const RECT2DI rc( 0, 0, w, h );

	CPlaneTransiter::Blt( *pDst, &rc, Sys, &rc );

}

void CVideoCardDriverD3D9::SetRenderState( RENDERSTATE state, unt value )
{
	D3DRENDERSTATETYPE type;
	DWORD v;

	switch( state )
	{
	case RENDERSTATE_CULLINGMODE:
		{
			type = D3DRS_CULLMODE;

			switch( value )
			{
			case CULLINGMODE_NONE: { v = D3DCULL_NONE; }break;
			case CULLINGMODE_LEFT: { v = D3DCULL_CCW; }break;
			case CULLINGMODE_RIGHT:{ v = D3DCULL_CW; }break;
			}
		}break;

	case RENDERSTATE_ZWRITEENABLE:
		{
			type = D3DRS_ZWRITEENABLE;

			switch( value )
			{
			case STATE_TRUE: { v = TRUE; }break;
			case STATE_FALSE:{ v = FALSE; }break;
			}

		}break;

	case RENDERSTATE_ZWRITEFUNC:
		{
			type = D3DRS_ZFUNC;

			v = CMPTYPE2D3DCMPFUNC( (IVideoCardDriver::CMPTYPE)value );
		}break;

	case RENDERSTATE_ZTESTENABLE:
		{
			type = D3DRS_ZENABLE;

			switch( value )
			{
			case STATE_TRUE: { v = D3DZB_TRUE; }break;
			case STATE_FALSE:{ v = D3DZB_FALSE; }break;
			}

		}break;
	}

	const HRESULT ret = m_pDevice->SetRenderState(type, v );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetRenderState()")); }
}


void CVideoCardDriverD3D9::SetSamplerState( int Stage, SAMPLERSTATE State, unt Value )
{
	D3DSAMPLERSTATETYPE t;
	DWORD v;

	switch( State )
	{
	case SAMPLERSTATE_MAGFILTER:
	case SAMPLERSTATE_MINFILTER:
		{
			if( State==SAMPLERSTATE_MAGFILTER ) { t = D3DSAMP_MAGFILTER; }
			ef( State==SAMPLERSTATE_MINFILTER ) { t = D3DSAMP_MINFILTER; }

			switch( Value )
			{
			case FILTERTYPE_POINT:		{ v = D3DTEXF_POINT; }break;
			case FILTERTYPE_LINEAR:		{ v = D3DTEXF_LINEAR; }break;
			case FILTERTYPE_ANISOTROPIC:{ v = D3DTEXF_ANISOTROPIC; }break;
			}
			
		}break;

	case SAMPLERSTATE_TADDRESSU:
	case SAMPLERSTATE_TADDRESSV:
		{
			if( State==SAMPLERSTATE_TADDRESSU ) { t = D3DSAMP_ADDRESSU; }
			ef( State==SAMPLERSTATE_TADDRESSV ) { t = D3DSAMP_ADDRESSV; }

			switch( Value )
			{
			case TEXTUREADDRESS_WRAP:	{ v = D3DTADDRESS_WRAP; }break;
			case TEXTUREADDRESS_MIRROR:	{ v = D3DTADDRESS_MIRROR; }break;
			case TEXTUREADDRESS_CLAMP:	{ v = D3DTADDRESS_CLAMP; }break;
			}
			
		}break;

	}
	const HRESULT ret = m_pDevice->SetSamplerState(Stage, t, v );
	if( FAILED(ret) ) { MAID_THROWEXCEPTION(MAIDTEXT("IDirect3DDevice9::SetSamplerState()")); }
}

}

