/*
 * Xenoborg - Xbox Emulator
 * Copyright (C) 2006-2010  blueshogun96
 *
 * Name: EmuDirect3D.cpp
 * Desc: Contains Direct3D 9.0 routines used to handle gfx.
 *
 * Changelog:
 *	4/24/11 - Initial writing to temporarily replace OpenGL since I'm having driver
 *		issues with multithreaded applications (wglMakeCurrent keeps failing).
 */

#include "XenoKrnl.h"


bool EnableDirect3D( D3DContext* wndData )
{
	HRESULT hr;

	// Initialize Direct3D
	wndData->d3d = Direct3DCreate9( D3D_SDK_VERSION );
	if( !wndData->d3d )
		return EmuError( "Error initializing Direct3D9!\n" );

	// Get the current display mode setings
	D3DDISPLAYMODE d3ddm;
	hr = wndData->d3d->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm );
	if( FAILED( hr ) )
		return EmuError( "Error getting display settings!\n", hr );

	// Create the Direct3D device
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory( &d3dpp, sizeof( d3dpp ) );
	d3dpp.BackBufferWidth = 640;
	d3dpp.BackBufferHeight = 480;
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; //d3ddm.Format;
//	d3dpp.EnableAutoDepthStencil = TRUE;
//	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.BackBufferCount = 1;
	d3dpp.hDeviceWindow = wndData->hWnd;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.Windowed = TRUE;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

	hr = wndData->d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wndData->hWnd,
		D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &wndData->d3ddevice );
	if( FAILED( hr ) )
		return EmuError( "Error creating Direct3D device!\n", hr );

	// Create a new render target with XBGR format to match the framebuffer
	/*hr = wndData->d3ddevice->CreateRenderTarget( 640, 480, D3DFMT_X8B8G8R8, D3DMULTISAMPLE_NONE, 0, 
		TRUE, &wndData->rendertarget, NULL );
	if( FAILED( hr ) )
		return EmuError( "Error creating render target!\n", hr );*/

	// Create a new depth and stencil buffer
	hr = wndData->d3ddevice->CreateDepthStencilSurface( 640, 480, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0,
		TRUE, &wndData->depthstencil, NULL );
	if( FAILED( hr ) )
		return EmuError( "Error creating depth stencil surface!\n", hr );

	// Set default viewport
	D3DVIEWPORT9 vp;
	vp.X = 0;
	vp.Y = 0;
	vp.Width = 640;
	vp.Height = 480;
	vp.MinZ = 0.0f;
	vp.MaxZ = 1.0f;
	wndData->d3ddevice->SetViewport( &vp );

	return TRUE;
}

void DisableDirect3D( D3DContext* wndData )
{
	if( wndData->depthstencil )
		wndData->depthstencil->Release();
	if( wndData->rendertarget )
		wndData->rendertarget->Release();

	if( wndData->d3ddevice )
	{
		wndData->d3ddevice->Release();
	}

	if( wndData->d3d )
	{
		wndData->d3d->Release();
	}
}

void D3DClearRenderTarget( D3DContext* context )
{
	// Clears the currently set render target
	context->d3ddevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0 );
}

void D3DClearDepthBuffer( D3DContext* context )
{
	context->d3ddevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0f, 0 );
}

void D3DClearStencilBuffer( D3DContext* context )
{
	context->d3ddevice->Clear( 0, NULL, D3DCLEAR_STENCIL, 0, 1.0f, 0 );
}

void D3DSwap( D3DContext* context )
{
	context->d3ddevice->Present( NULL, NULL, NULL, NULL );
}

void D3DFrameBufferWrite( D3DContext* context, DWORD addr, DWORD val )
{
	IDirect3DSurface9* backbuffer = NULL;
	D3DLOCKED_RECT LockedRect;

	// A really crappy function to write to the framebuffer
	HRESULT hr = context->d3ddevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer );
	if( FAILED( hr ) )
		return;

	if( SUCCEEDED( backbuffer->LockRect( &LockedRect, NULL, 0 ) ) )
	{
		DWORD* fb = (DWORD*) LockedRect.pBits;
		fb[addr] = val;

		backbuffer->UnlockRect();
	}
}

void D3DFrameBufferWrite8( D3DContext* context, DWORD addr, BYTE val )
{
	IDirect3DSurface9* backbuffer = NULL;
	D3DLOCKED_RECT LockedRect;

	// A really crappy function to write to the framebuffer
	HRESULT hr = context->d3ddevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer );
	if( FAILED( hr ) )
		return;

	if( SUCCEEDED( backbuffer->LockRect( &LockedRect, NULL, 0 ) ) )
	{
		BYTE* fb = (BYTE*) LockedRect.pBits;
		fb[addr] = val;

		backbuffer->UnlockRect();
	}

	backbuffer->Release();
}