#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <string>
#include <d3d9.h>
#include <d3dx9math.h>
#include <dxerr9.h>

//#include "res\resource.h"

#include "PajaTypes.h"
#include "ColorC.h"
#include "DX9DeviceC.h"

using namespace PajaTypes;
using namespace PajaSystem;
using namespace PluginClass;
using namespace Edit;
using namespace FileIO;


static
void
Trace( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}

#define				TRACE					Trace( "%s(%i) : ", __FILE__, __LINE__ ), Trace



// static variables
int32	DX9DeviceC::m_i32RefCount = 0;
bool	DX9DeviceC::m_bClassCreated = false;



LRESULT CALLBACK
DX9DeviceC::stub_window_proc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	DX9DeviceC*	pDevice = reinterpret_cast<DX9DeviceC*>(GetWindowLong( hWnd, GWL_USERDATA ));

	// the usual window procedure...

	switch( uMsg ) {
	case WM_CREATE:
		{
			SetWindowLong( hWnd, GWL_USERDATA, (LONG)((LPCREATESTRUCT)lParam)->lpCreateParams );
			pDevice = reinterpret_cast<DX9DeviceC*>(((LPCREATESTRUCT)lParam)->lpCreateParams);
		}
		TRACE( "DX9 Device Create\n" );
		return 0;

	case WM_DESTROY:
		TRACE( "Device Destroy\n" );
		pDevice->m_hWnd = 0;
		return 0;

	case WM_ERASEBKGND:
		return -1;	// we clear out mess ourselfs.

	case WM_PAINT:
//		BeginPaint( hWnd, &rPS );
//		EndPaint( hWnd, &rPS );
		break;

	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_KEYDOWN:
	case WM_KEYUP:
	case WM_SETCURSOR:
	case WM_MOUSEMOVE:
		if( pDevice->m_ui32CreateFlags == GRAPHICSDEVICE_CREATE_EDITOR_CHILD )
			return PostMessage( GetParent( hWnd ), uMsg, wParam, lParam );
		break;

	case WM_CHAR:
		if( pDevice->m_ui32CreateFlags != GRAPHICSDEVICE_CREATE_EDITOR_CHILD && wParam == 27 ) {
			PostMessage( hWnd, DP_END_PREVIEW, 0, 0);
			return 0;
		}
		break;

	case WM_CLOSE:
		if( pDevice->m_ui32CreateFlags != GRAPHICSDEVICE_CREATE_EDITOR_CHILD ) {
			PostMessage( hWnd, DP_END_PREVIEW, 0, 0);
			return 0;
		}
		break;

	case WM_SIZE:
		if( pDevice->m_pD3DDevice && pDevice->m_ui32CreateFlags != GRAPHICSDEVICE_CREATE_EDITOR_CHILD ) {
			uint32	ui32Width = LOWORD( lParam );
			uint32	ui32Height = HIWORD( lParam );
			pDevice->set_size( 0, 0, ui32Width, ui32Height );
		}

	case DP_END_PREVIEW:
		// make sure out message wont overflow to the system.
		return 0;

	}


	return DefWindowProc( hWnd, uMsg, wParam, lParam );
}



DX9DeviceC::DX9DeviceC() :
	m_hWnd( 0 ),
	m_pD3D( 0 ),
	m_pD3DDevice( 0 ),
	m_pStateBlockSaved( 0 ),
	m_pFeedback( 0 ),
	m_dwCurAdapter( 0 ),
	m_bD3DReady( false ),
	m_ui32Width( 0 ),
	m_ui32Height( 0 ),
	m_pBackBuffer( 0 ),
	m_pBackBuffer2( 0 ),
	m_pZBuffer( 0 ),
	m_pCurrentBuffer( 0 ),
	m_bLostDevice( false ),
	m_dwClearFlagMask( 0 ),
	m_ui32TempWidth( 1 ),
	m_ui32TempHeight( 1 ),
	m_bBuffersSaved( false )
{
	m_pInterface = DX9ViewportC::create_new( this );
	m_pGUIDrawInterface = DX9GUIDrawInterfaceC::create_new( this );
	ZeroMemory( &m_rD3Dpp, sizeof( m_rD3Dpp ) );
}

DX9DeviceC::~DX9DeviceC()
{
	if( m_hWnd )
		destroy();

	// Release temp buffers
	for( std::list<TempBufferS>::iterator TmpIt = m_lstTempBuffers.begin(); TmpIt != m_lstTempBuffers.end(); ++TmpIt )
	{
		TempBufferS&	Tmp = (*TmpIt);
		if( Tmp.pBuffer )
			Tmp.pBuffer->release();
	}

	if( m_pStateBlockSaved )
		m_pStateBlockSaved->Release();

	if( m_pInterface )
		m_pInterface->release();

	if( m_pGUIDrawInterface )
		m_pGUIDrawInterface->release();

	if( m_pD3DDevice )
		m_pD3DDevice->Release();

	if( m_pD3D )
		m_pD3D->Release();

	m_bD3DReady = false;
	m_bLostDevice = true;
}

DX9DeviceC*
DX9DeviceC::create_new()
{
	return new DX9DeviceC;
}

DataBlockI*
DX9DeviceC::create()
{
	return new DX9DeviceC;
}

ClassIdC
DX9DeviceC::get_class_id() const
{
	return CLASS_DX9_DEVICEDRIVER;
}

const char*
DX9DeviceC::get_class_name()
{
	return "DirectX 9 Device Driver";
}

uint32
DX9DeviceC::save( SaveC* pSave )
{
	return IO_OK;
}

uint32
DX9DeviceC::load( LoadC* pLoad )
{
	return IO_OK;
}

static
int
sort_modes_callback( const VOID* arg1, const VOID* arg2 )
{
	D3DDISPLAYMODE* p1 = (D3DDISPLAYMODE*)arg1;
	D3DDISPLAYMODE* p2 = (D3DDISPLAYMODE*)arg2;

	if( p1->Format > p2->Format )	return -1;
	if( p1->Format < p2->Format )	return +1;
	if( p1->Width  < p2->Width )	return -1;
	if( p1->Width  > p2->Width )	return +1;
	if( p1->Height < p2->Height )	return -1;
	if( p1->Height > p2->Height )	return +1;

	return 0;
}

/*
void
DX9DeviceC::build_device_list( uint32 ui32DefaultWidth, uint32 ui32DefaultHeight, uint32 ui32DefaultBPP )
{
	static DWORD      dwNumDeviceTypes = 2L;
	static TCHAR*     strDeviceDescs[] = { "HAL", "REF" };
	static D3DDEVTYPE DeviceTypes[]    = { D3DDEVTYPE_HAL, D3DDEVTYPE_REF };

	// Loop through all the adapters on the system (usually, there's just one
	// unless more than one graphics card is present).
	for( int iAdapter = 0; iAdapter < m_pD3D->GetAdapterCount(); iAdapter++ ) {

		AdapterS	rAdapter;

		// Get the adapter attributes
		D3DADAPTER_IDENTIFIER9 AdapterIdentifier;
		m_pD3D->GetAdapterIdentifier( iAdapter, 0, &AdapterIdentifier );
		
		// Copy adapter info
		rAdapter.iAdapter = iAdapter;
		rAdapter.rD3DAdapterIdentifier = AdapterIdentifier;
		rAdapter.dwCurrentDevice = 0;
		
		// Enumerate display modes
		D3DDISPLAYMODE	modes[100];
		D3DFORMAT		formats[20];
		DWORD			dwNumFormats = 0;
		DWORD			dwNumModes = 0;
		DWORD			dwNumAdapterModes = m_pD3D->GetAdapterModeCount( iAdapter );
		
		// Add the current desktop format to list of formats
		D3DDISPLAYMODE	DesktopMode;
		m_pD3D->GetAdapterDisplayMode( iAdapter, &DesktopMode );
		formats[dwNumFormats++] = DesktopMode.Format;
		
		for( UINT iMode = 0; iMode < dwNumAdapterModes; iMode++ ) {
			// Get the display mode attributes
			D3DDISPLAYMODE DisplayMode;
			m_pD3D->EnumAdapterModes( iAdapter, iMode, &DisplayMode );
			
			// Filter out low-resolution modes
			if( DisplayMode.Width < 320 || DisplayMode.Height < 240 ) 
				continue;
			
			// Check if the mode already exists (to filter out refresh rates)
			for( DWORD m = 0L; m < dwNumModes; m++ ) {
				if((modes[m].Width	== DisplayMode.Width) &&
					(modes[m].Height == DisplayMode.Height) &&
					(modes[m].Format == DisplayMode.Format)) 
					break;
			}
			
			// If we found a new mode, add it to the list of modes
			if( m == dwNumModes ) {
				modes[dwNumModes].Width 	  = DisplayMode.Width;
				modes[dwNumModes].Height	  = DisplayMode.Height;
				modes[dwNumModes].Format	  = DisplayMode.Format;
				modes[dwNumModes].RefreshRate = 0;
				dwNumModes++;
				
				// Check if the mode's format already exists
				for(DWORD f = 0; f < dwNumFormats; f++ )
					if( DisplayMode.Format == formats[f] )
						break;
				
				// If the format is new, add it to the list
				if( f == dwNumFormats )
					formats[dwNumFormats++] = DisplayMode.Format;
			}
		}
		
		// Sort the list of display modes (by format, then width, then height)
		qsort( modes, dwNumModes, sizeof( D3DDISPLAYMODE ), sort_modes_callback );
		
		// Add devices to adapter
		for( UINT iDevice = 0L; iDevice < dwNumDeviceTypes; iDevice++ ) {
			// Get the device attributes
			D3DCAPS9 d3dCaps;
			m_pD3D->GetDeviceCaps( iAdapter, DeviceTypes[iDevice], &d3dCaps );
			
			DeviceS rDevice;
			// Copy device info
			rDevice.rDeviceType = DeviceTypes[iDevice];
			rDevice.rD3DCaps = d3dCaps;
			rDevice.strDesc = strDeviceDescs[iDevice];
			rDevice.dwCurrentMode = 0L;
			rDevice.bCanDoWindowed = false;
			rDevice.bWindowed = false;
			rDevice.bStereo = false;
			
			// Call the app's ConfirmDevice() callback to see if the device
			// caps and the enumerated formats meet the app's requirements.
			BOOL  bFormatConfirmed[20];
			DWORD dwBehavior[20];
			D3DFORMAT fmtDepthStencil[20];
			
			for( DWORD f = 0; f < dwNumFormats; f++ ) {
				bFormatConfirmed[f] = FALSE;
				fmtDepthStencil[f] = D3DFMT_UNKNOWN;
				
				// Filter out modes incompatible with rendering
				if( FAILED( m_pD3D->CheckDeviceType( iAdapter, rDevice.rDeviceType, formats[f], formats[f], FALSE ) ) )
					continue;
				
				if( FAILED( m_pD3D->CheckDeviceFormat( iAdapter, rDevice.rDeviceType, 
					formats[f], D3DUSAGE_RENDERTARGET, 
					D3DRTYPE_SURFACE, formats[f] ) ) )
					continue;
				
				// Confirm the device for HW vertex processing
				if( d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) {
					dwBehavior[f] = D3DCREATE_HARDWARE_VERTEXPROCESSING;	// and purehal?;
					bFormatConfirmed[f] = TRUE;
				}
				
				// Confirm the device for SW vertex processing
				if( bFormatConfirmed[f] == FALSE ) {
					dwBehavior[f] = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
					bFormatConfirmed[f] = TRUE;
				}
				
				// Find an appropriate depth stencil format
				if( !find_depth_stencil_format( iAdapter, rDevice.rDeviceType, formats[f], &fmtDepthStencil[f] ) )
					bFormatConfirmed[f] = FALSE;
			}
			
			// Add all enumerated display modes with confirmed formats to the
			// device's list of valid modes
			for( DWORD m = 0L; m < dwNumModes; m++ ) {
				for( DWORD f = 0; f < dwNumFormats; f++ ) {
					if( modes[m].Format == formats[f] ) {
						if( bFormatConfirmed[f] == TRUE ) {
							// Add this mode to the device's list of valid modes
							ModeS	rMode;
							
							rMode.dwWidth = modes[m].Width;
							rMode.dwHeight = modes[m].Height;
							rMode.rFormat = modes[m].Format;
							rMode.dwBehavior = dwBehavior[f];
							rMode.rDepthStencilFormat = fmtDepthStencil[f];

							rDevice.rModes.push_back( rMode );
						}
					}
				}
			}
			
			for( m = 0; m < rDevice.rModes.size(); m++ ) {
				if( rDevice.rModes[m].dwWidth >= ui32DefaultWidth && rDevice.rModes[m].dwHeight >= ui32DefaultHeight ) {
					rDevice.dwCurrentMode = m;
					if( ui32DefaultBPP == 16 ) {
						if( rDevice.rModes[m].rFormat == D3DFMT_R5G6B5 )
							break;
						if( rDevice.rModes[m].rFormat == D3DFMT_A1R5G5B5 )
							break;
						if( rDevice.rModes[m].rFormat == D3DFMT_X1R5G5B5 )
							break;
					}
					if( ui32DefaultBPP == 24 ) {
						if( rDevice.rModes[m].rFormat == D3DFMT_R8G8B8 )
							break;
						if( rDevice.rModes[m].rFormat == D3DFMT_X8R8G8B8 )
							break;
					}
					if( ui32DefaultBPP == 32 ) {
						if( rDevice.rModes[m].rFormat == D3DFMT_A8R8G8B8 )
							break;
						if( rDevice.rModes[m].rFormat == D3DFMT_X8R8G8B8 )
							break;
					}
				}
			}
			
			// Check if the device is compatible with the desktop display mode
			// (which was added initially as formats[0])
			if( bFormatConfirmed[0] ) {
				rDevice.bCanDoWindowed = true;
				rDevice.bWindowed = true;
			}
			
			// If valid modes were found, keep this device
			if( rDevice.rModes.size() > 0 )
				rAdapter.rDevices.push_back( rDevice );
		}
		
		// If valid devices were found, keep this adapter
		if( rAdapter.rDevices.size() )
			m_rAdapters.push_back( rAdapter );
	}
	
	// Return an error if no compatible devices were found
	if( m_rAdapters.size() == 0 ) {
		MessageBox( NULL, "No hardware devices found.", "Error", MB_OK );
		return;
	}
	
	// Pick a default device that can render into a window
	for( DWORD a = 0; a < m_rAdapters.size(); a++ ) {
		for( DWORD d = 0; d < m_rAdapters[a].rDevices.size(); d++ ) {
			if( m_rAdapters[a].rDevices[d].bWindowed ) {
				m_rAdapters[a].dwCurrentDevice = d;
				m_dwCurAdapter = a;
				
				// Display a warning message
				if( m_rAdapters[a].rDevices[d].rDeviceType == D3DDEVTYPE_REF ) {
					MessageBox( NULL, "Default device cannot render to window. Switching to reference rasteriser", "Warning", MB_OK );
				}
				
				return;
			}
		}
	}
	
	return;
}
*/

bool
DX9DeviceC::find_depth_stencil_format( UINT iAdapter, D3DDEVTYPE rDeviceType,
									   D3DFORMAT rTargetFormat, D3DFORMAT* pDepthStencilFormat )
{
	DWORD	dwMinDepthBits = 15;
	DWORD	dwMinStencilBits = 0;

 
	if( dwMinDepthBits <= 24 && dwMinStencilBits <= 8 ) {
		if( SUCCEEDED( m_pD3D->CheckDeviceFormat( iAdapter, rDeviceType,
			rTargetFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24S8 ) ) ) {
			if( SUCCEEDED( m_pD3D->CheckDepthStencilMatch( iAdapter, rDeviceType,
				rTargetFormat, rTargetFormat, D3DFMT_D24S8 ) ) ) {
				*pDepthStencilFormat = D3DFMT_D24S8;
				return true;
			}
		}
	}
	
	if( dwMinDepthBits <= 24 && dwMinStencilBits <= 4 ) {
		if( SUCCEEDED( m_pD3D->CheckDeviceFormat( iAdapter, rDeviceType,
			rTargetFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24X4S4 ) ) ) {
			if( SUCCEEDED( m_pD3D->CheckDepthStencilMatch( iAdapter, rDeviceType,
				rTargetFormat, rTargetFormat, D3DFMT_D24X4S4 ) ) ) {
				*pDepthStencilFormat = D3DFMT_D24X4S4;
				return true;
			}
		}
	}

	if( dwMinDepthBits <= 15 && dwMinStencilBits <= 1 ) {
		if( SUCCEEDED( m_pD3D->CheckDeviceFormat( iAdapter, rDeviceType,
			rTargetFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D15S1 ) ) ) {
			if( SUCCEEDED( m_pD3D->CheckDepthStencilMatch( iAdapter, rDeviceType,
				rTargetFormat, rTargetFormat, D3DFMT_D15S1 ) ) ) {
				*pDepthStencilFormat = D3DFMT_D15S1;
				return true;
			}
		}
	}
		

	return false;
}


bool
DX9DeviceC::init( HINSTANCE hInstance, HWND hParent, int32 i32ID, uint32 ui32Flags,
					   uint32 ui32Width, uint32 ui32Height, uint32 ui32BPP, DeviceFeedbackC* pFeedback )
{
	HRESULT hr;

	m_ui32Width = ui32Width;
	m_ui32Height = ui32Height;

	TRACE( "%d x %d\n", ui32Width, ui32Height );

	//
	// Register window class.
	//
	if( !m_bClassCreated )
	{
		WNDCLASS	rWndClass;

		// Set window infos
		memset( &rWndClass, 0, sizeof( rWndClass ) );
		rWndClass.style = CS_CLASSDC;
		rWndClass.lpfnWndProc = &stub_window_proc;
		rWndClass.cbClsExtra = 0;
		rWndClass.cbWndExtra = 0;
		rWndClass.hInstance = hInstance;
		rWndClass.hIcon = NULL;
		rWndClass.hCursor = NULL;
		rWndClass.hbrBackground = NULL;
		rWndClass.lpszMenuName = NULL;
		rWndClass.lpszClassName = "DemopajaDX9Device";

		// register window
		if( !RegisterClass( &rWndClass ) )
			return false;

		m_bClassCreated = true;

		OutputDebugString( "class ok\n" );
	}


	if( ui32Flags == GRAPHICSDEVICE_CREATE_EDITOR_CHILD )
	{
		// Create child window
		m_hWnd = CreateWindowEx( WS_EX_TRANSPARENT, "DemopajaDX9Device", (LPSTR)NULL,
								WS_VISIBLE | WS_CHILD,
								0, 0, ui32Width, ui32Height,
								hParent, (HMENU)(int)i32ID, hInstance, (LPVOID)this );

		if( m_hWnd == 0 ) {
			OutputDebugString( "CreateWindowEx failed\n" );
			return false;
		}
	}
	else if( ui32Flags == GRAPHICSDEVICE_CREATE_CHILD )
	{
		// Create child window
		m_hWnd = CreateWindowEx( WS_EX_TRANSPARENT, "DemopajaDX9Device", (LPSTR)NULL,
								WS_VISIBLE | WS_CHILD,
								0, 0, 300, 300,
								hParent, (HMENU)(int)i32ID, hInstance, (LPVOID)this );

		if( m_hWnd == 0 ) {
			OutputDebugString( "CreateWindowEx failed\n" );
			return false;
		}
	}
	else if( ui32Flags == GRAPHICSDEVICE_CREATE_WINDOWED )
	{
		RECT rc;
		SetRect( &rc, 0, 0, ui32Width, ui32Height );        
		AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, false );


		// Create popup window
		m_hWnd = CreateWindowEx( 0, "DemopajaDX9Device", "Demopaja Player",
								WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
								CW_USEDEFAULT, CW_USEDEFAULT,
								(rc.right-rc.left), (rc.bottom-rc.top),
								hParent, NULL, hInstance, (LPVOID)this );

		if( m_hWnd == 0 ) {
			OutputDebugString( "CreateWindowEx failed\n" );
			return false;
		}

		ShowWindow( m_hWnd, SW_SHOW );
	}
	else if( ui32Flags == GRAPHICSDEVICE_CREATE_FULLSCREEN )
	{

		// Create fullscreen window
		m_hWnd = CreateWindowEx( WS_EX_TOPMOST, "DemopajaDX9Device", "Demopaja Player",
								WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
								0, 0, ui32Width, ui32Height,
								hParent, NULL, hInstance, (LPVOID)this );

		if( m_hWnd == 0 ) {
			OutputDebugString( "CreateWindowEx failed\n" );
			return false;
		}

		ShowWindow( m_hWnd, SW_MAXIMIZE );
		SetFocus( m_hWnd );
		BringWindowToTop( m_hWnd );
		SetForegroundWindow( m_hWnd );
		UpdateWindow( m_hWnd );
	}


	bool				bWindowed = (ui32Flags == GRAPHICSDEVICE_CREATE_FULLSCREEN) ? false : true;
	D3DDEVTYPE	deviceType = D3DDEVTYPE_HAL;

	// Step 1: Create the IDirect3D9 object.
	IDirect3D9* m_pD3D = 0;
	m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
	if( !m_pD3D )
	{
		OutputDebugString( "Create D3D failed: " );
//		OutputDebugString( DXGetErrorString9( hr ) );
		OutputDebugString( "\n" );
		return false;
	}


	uint32	ui32Mode = 0;

	D3DFORMAT	Formats[3] = { D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_R5G6B5 };

	m_lstModes.clear();

	for( uint32 i = 0; i < 3; i++ )
	{
		for( ui32Mode = 0; ui32Mode < m_pD3D->GetAdapterModeCount( D3DADAPTER_DEFAULT, Formats[i] ); ui32Mode++ )
		{
			D3DDISPLAYMODE displayMode;
			m_pD3D->EnumAdapterModes( D3DADAPTER_DEFAULT, Formats[i], ui32Mode, &displayMode );

			// Filter out low-resolutions
			if( displayMode.Width < 640 || displayMode.Height < 400 )
				continue;

			D3DFORMAT	DepthStencilFormat = D3DFMT_D24S8;
//			if( !find_depth_stencil_format( D3DADAPTER_DEFAULT, deviceType, displayMode.Format, &DepthStencilFormat ) )
//				continue;

			// Check to see if it is already in the list (to filter out refresh rates)
			bool bFound = FALSE;
			std::list<ModeS>::iterator it;
			for( it = m_lstModes.begin(); it != m_lstModes.end(); ++it )
			{
				ModeS&	Mode = (*it);
				if( Mode.ui32Width == displayMode.Width &&
					Mode.ui32Height == displayMode.Height &&
					Mode.TargetFormat == displayMode.Format )
				{
					bFound = true;
					break;
				}
			}

			if( !bFound )
			{
				ModeS	Mode;
				Mode.ui32Width = displayMode.Width;
				Mode.ui32Height = displayMode.Height;
				Mode.TargetFormat = displayMode.Format;
				Mode.DepthStencilFormat = DepthStencilFormat;
				m_lstModes.push_back( Mode );
			}

		}
	}

	// Step 2: Check for hardware vp.
	D3DCAPS9	caps;
	m_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, deviceType, &caps );
	int vp = 0;
	if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	// Step 3: Fill out the D3DPRESENT_PARAMETERS structure.

	// Find nice fullscreen resolution.
	if( !bWindowed )
	{
		ModeS*	pFoundMode = 0;

		std::list<ModeS>::iterator it;
		for( it = m_lstModes.begin(); it != m_lstModes.end(); ++it )
		{
			ModeS&	Mode = (*it);
			if( Mode.ui32Width >= ui32Width && Mode.ui32Height >= ui32Height )
			{
				pFoundMode = &Mode;
				break;
			}
		}

		if( !pFoundMode )
			return false;

		m_rD3Dpp.BackBufferWidth = pFoundMode->ui32Width;
		m_rD3Dpp.BackBufferHeight = pFoundMode->ui32Height;
		m_rD3Dpp.BackBufferFormat =  pFoundMode->TargetFormat;
		m_rD3Dpp.BackBufferCount = 0;
		m_rD3Dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
		m_rD3Dpp.MultiSampleQuality = 0;
		m_rD3Dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
		m_rD3Dpp.hDeviceWindow = m_hWnd;
		m_rD3Dpp.Windowed = false;
		m_rD3Dpp.EnableAutoDepthStencil = true;
		m_rD3Dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
		m_rD3Dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
		m_rD3Dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
		m_rD3Dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	}
	else
	{
		m_rD3Dpp.BackBufferWidth = ui32Width;
		m_rD3Dpp.BackBufferHeight = ui32Height;
		m_rD3Dpp.BackBufferFormat = D3DFMT_UNKNOWN;
		m_rD3Dpp.BackBufferCount = 0;
		m_rD3Dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
		m_rD3Dpp.MultiSampleQuality = 0;
		m_rD3Dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
		m_rD3Dpp.hDeviceWindow = m_hWnd;
		m_rD3Dpp.Windowed = bWindowed;
		m_rD3Dpp.EnableAutoDepthStencil = true;
		m_rD3Dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
		m_rD3Dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL | D3DPRESENTFLAG_DEVICECLIP;
		m_rD3Dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
		m_rD3Dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	}

	// Step 4: Create the device.
	hr = m_pD3D->CreateDevice(	D3DADAPTER_DEFAULT, // primary adapter
														deviceType, // device type
														m_hWnd, // window associated with device
														vp, // vertex processing
														&m_rD3Dpp, // present parameters
														&m_pD3DDevice ); // return created device
	if( FAILED( hr ) )
	{
		TRACE( "trying 16bit depth\n" );
		// try again using a safer configuration.
		m_rD3Dpp.AutoDepthStencilFormat = D3DFMT_D16;
		hr = m_pD3D->CreateDevice(
			D3DADAPTER_DEFAULT,
			deviceType,
			m_hWnd,
			D3DCREATE_SOFTWARE_VERTEXPROCESSING,
			&m_rD3Dpp,
			&m_pD3DDevice );

		m_dwClearFlagMask = D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER;

		if( FAILED(hr) )
		{
			m_pD3D->Release(); // done with d3d9 object
			OutputDebugString( "CreateDevice failed: " );
			OutputDebugString( DXGetErrorString9( hr ) );
			OutputDebugString( "\n" );
			return false;
		}
	}
	else
		m_dwClearFlagMask = D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL;

	if( m_ui32CreateFlags == GRAPHICSDEVICE_CREATE_EDITOR_CHILD )
	{
		//
		// On child just enalrge the viewport (no scaling).
		//
		Vector2C	rDelta( (float32)((int32)ui32Width - m_pInterface->get_width()) / 2.0f, (float32)((int32)ui32Height - m_pInterface->get_height()) / 2.0f );
		rDelta = m_pInterface->delta_client_to_layout( rDelta );

		BBox2C	rViewport = m_pInterface->get_viewport();

		rViewport[0] -= rDelta;
		rViewport[1] += rDelta;

		m_pInterface->set_viewport( rViewport );
	}
	else
	{
		//
		// The windowed and fullscreen are maximized.
		//
		BBox2C	rLayout = m_pInterface->get_layout();

		float32	f32LayoutWidth = rLayout.width();
		float32	f32LayoutHeight = rLayout.height();

		float64	f64AspectX = (float64)ui32Width / (float64)f32LayoutWidth;
		float64	f64AspectY = (float64)ui32Height / (float64)f32LayoutHeight;

		if( f64AspectX < f64AspectY )
			f32LayoutHeight = f32LayoutWidth / (float32)ui32Width * (float32)ui32Height;
		else
			f32LayoutWidth = f32LayoutHeight / (float32)ui32Height * (float32)ui32Width;

		float32	f32LayoutPosX = (f32LayoutWidth * 0.5f) - (rLayout.width() * 0.5f);
		float32	f32LayoutPosY = (f32LayoutHeight * 0.5f) - (rLayout.height() * 0.5f);


		BBox2C	rViewport( Vector2C( -f32LayoutPosX, -f32LayoutPosY ),
						   Vector2C( -f32LayoutPosX + f32LayoutWidth, -f32LayoutPosY + f32LayoutHeight ) );

		m_pInterface->set_viewport( rViewport );
	}

	m_pInterface->set_dimension( 0, 0, ui32Width, ui32Height );

	D3DVIEWPORT9	rDXViewport;
	rDXViewport.X = 0;
	rDXViewport.Y = 0;
	rDXViewport.Width = ui32Width;
	rDXViewport.Height = ui32Height;
	rDXViewport.MinZ = 0.0f;
	rDXViewport.MaxZ = 1.0f;
	if( FAILED( m_pD3DDevice->SetViewport( &rDXViewport ) ) )
		TRACE( "set viewport failed\n" );

	if( FAILED( m_pD3DDevice->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0 ) ) )
		TRACE( "clear failed\n" );

	if( FAILED( m_pD3DDevice->BeginScene() ) )
		TRACE( "begin scene failed\n" );
	if( FAILED( m_pD3DDevice->EndScene() ) )
		TRACE( "end scene failed\n" );

	if( FAILED( m_pD3DDevice->Present( NULL, NULL, NULL, NULL ) ) )
		TRACE( "present failed\n" );


	TRACE( "- BackBufferWidth: %d\n", m_rD3Dpp.BackBufferWidth );
	TRACE( "- BackBufferHeight: %d\n", m_rD3Dpp.BackBufferHeight );
	TRACE( "- BackBufferFormat: %d\n", m_rD3Dpp.BackBufferFormat );
	TRACE( "- BackBufferCount: %d\n", m_rD3Dpp.BackBufferCount );
	TRACE( "- MultiSampleType: %d\n", m_rD3Dpp.MultiSampleType );
	TRACE( "- MultiSampleQuality: %d\n", m_rD3Dpp.MultiSampleQuality );
	TRACE( "- SwapEffect: %d\n", m_rD3Dpp.SwapEffect );
	TRACE( "- hDeviceWindow: %d\n", m_rD3Dpp.hDeviceWindow );
	TRACE( "- Windowed: %d\n", m_rD3Dpp.Windowed );
	TRACE( "- EnableAutoDepthStencil: %d\n", m_rD3Dpp.EnableAutoDepthStencil );
	TRACE( "- AutoDepthStencilFormat: %d\n", m_rD3Dpp.AutoDepthStencilFormat );
	TRACE( "- Flags: %d\n", m_rD3Dpp.Flags );
	TRACE( "- FullScreen_RefreshRateInHz: %d\n", m_rD3Dpp.FullScreen_RefreshRateInHz );
	TRACE( "- PresentationInterval: %d\n", m_rD3Dpp.PresentationInterval );


	// store creation params
	m_ui32CreateFlags = ui32Flags;
	m_pFeedback = pFeedback;

	m_bD3DReady = true;

	HRESULT	hRes;
	if( FAILED( hRes = m_pD3DDevice->TestCooperativeLevel() ) )
		m_bLostDevice = true;
	else
		m_bLostDevice = false;
	

	init_validate();

	return true;
}

DeviceInterfaceI*
DX9DeviceC::query_interface( const SuperClassIdC& rSuperClassId )
{
	if( rSuperClassId == GRAPHICSDEVICE_VIEWPORT_INTERFACE ) {
		if( m_pCurrentBuffer )
			return m_pCurrentBuffer->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
		return m_pInterface;
	}
	else if( rSuperClassId == GRAPHICSDEVICE_GUIDRAW_INTERFACE ) {
		return m_pGUIDrawInterface;
	}

	return 0;
}


void
DX9DeviceC::activate()
{
	if( m_bLostDevice )
		return;

	D3DVIEWPORT9	rDXViewport;
	rDXViewport.X = 0;
	rDXViewport.Y = 0;
	rDXViewport.Width = m_pInterface->get_width();
	rDXViewport.Height = m_pInterface->get_height();
	rDXViewport.MinZ = 0.0f;
	rDXViewport.MaxZ = 1.0f;
	if( FAILED( m_pD3DDevice->SetViewport( &rDXViewport ) ) )
		TRACE( "set viewport failed\n" );

	// Reset matrices.
	m_pInterface->activate();
}


void
DX9DeviceC::destroy()
{
	DestroyWindow( m_hWnd );
}

void
DX9DeviceC::flush()
{
	if( !m_pD3DDevice )
		return;
	if( !m_bD3DReady )
		return;
	if( m_bLostDevice )
		return;

	if( m_pCurrentBuffer )
		m_pCurrentBuffer->flush();
	else
	{
		if( FAILED( m_pD3DDevice->Present( NULL, NULL, NULL, NULL ) ) )
			TRACE( "present failed\n" );
	}

//	TRACE( "Present\n" );
}


extern HINSTANCE	g_hInstance;


// Mesage handler for about box.
LRESULT CALLBACK
ConfigDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
	switch( message ) {
	case WM_INITDIALOG:
		{
			return TRUE;
		}

	case WM_COMMAND:
		if( LOWORD( wParam ) == IDOK || LOWORD( wParam ) == IDCANCEL ) {
			EndDialog( hDlg, LOWORD( wParam ) );
			return TRUE;
		}
	}
    return FALSE;
}



bool
DX9DeviceC::configure()
{
//	return DialogBox( g_hInstance, (LPCTSTR)IDD_CONFIG, NULL, (DLGPROC)ConfigDlgProc ) == IDOK ? true : false;
	return true;
}


void
DX9DeviceC::set_size( int32 i32X, int32 i32Y, int32 i32Width, int32 i32Height )
{
	TRACE( "set_size: (%d, %d) %dx%d\n", i32X, i32Y, i32Width, i32Height );

	BBox2C	rViewport;

	if( !m_bD3DReady )
		return;

	if( m_pInterface->get_width() == i32Width && m_pInterface->get_height() == i32Height )
		return;

	if( m_ui32CreateFlags == GRAPHICSDEVICE_CREATE_EDITOR_CHILD )
	{
		//
		// On child just enalrge the viewport (no scaling).
		//
		MoveWindow( m_hWnd, i32X, i32Y, i32Width, i32Height, TRUE);

		Vector2C	rDelta( (float32)(i32Width - m_pInterface->get_width()) / 2.0f, (float32)(i32Height - m_pInterface->get_height()) / 2.0f );
		rDelta = m_pInterface->delta_client_to_layout( rDelta );

		BBox2C	rViewport = m_pInterface->get_viewport();

		rViewport[0] -= rDelta;
		rViewport[1] += rDelta;

		m_pInterface->set_viewport( rViewport );
	}
	else
	{
		//
		// The windowed and fullscreen are maximized.
		//
		BBox2C	rLayout = m_pInterface->get_layout();

		float32	f32LayoutWidth = rLayout.width();
		float32	f32LayoutHeight = rLayout.height();

		float64	f64AspectX = (float64)i32Width / (float64)f32LayoutWidth;
		float64	f64AspectY = (float64)i32Height / (float64)f32LayoutHeight;

		if( f64AspectX < f64AspectY )
			f32LayoutHeight = f32LayoutWidth / (float32)i32Width * (float32)i32Height;
		else
			f32LayoutWidth = f32LayoutHeight / (float32)i32Height * (float32)i32Width;

		float32	f32LayoutPosX = (f32LayoutWidth * 0.5f) - (rLayout.width() * 0.5f);
		float32	f32LayoutPosY = (f32LayoutHeight * 0.5f) - (rLayout.height() * 0.5f);


		BBox2C	rViewport( Vector2C( -f32LayoutPosX, -f32LayoutPosY ),
						   Vector2C( -f32LayoutPosX + f32LayoutWidth, -f32LayoutPosY + f32LayoutHeight ) );

		m_pInterface->set_viewport( rViewport );
	}

	m_pInterface->set_dimension( i32X, i32Y, i32Width, i32Height );

	if( m_pD3DDevice )
	{
		init_invalidate();

		m_rD3Dpp.BackBufferWidth = i32Width;
		m_rD3Dpp.BackBufferHeight = i32Height;

		TRACE( "Reset (resize).\n" );

		HRESULT	hRes;

		if( FAILED( hRes = m_pD3DDevice->Reset( &m_rD3Dpp ) ) )
		{
			m_bLostDevice = true;

			TRACE( "Reset failed: %s\n", DXGetErrorString9( hRes ) );
			TRACE( "- BackBufferWidth: %d\n", m_rD3Dpp.BackBufferWidth );
			TRACE( "- BackBufferHeight: %d\n", m_rD3Dpp.BackBufferHeight );
			TRACE( "- BackBufferFormat: %d\n", m_rD3Dpp.BackBufferFormat );
			TRACE( "- BackBufferCount: %d\n", m_rD3Dpp.BackBufferCount );
			TRACE( "- MultiSampleType: %d\n", m_rD3Dpp.MultiSampleType );
			TRACE( "- MultiSampleQuality: %d\n", m_rD3Dpp.MultiSampleQuality );
			TRACE( "- SwapEffect: %d\n", m_rD3Dpp.SwapEffect );
			TRACE( "- hDeviceWindow: %d\n", m_rD3Dpp.hDeviceWindow );
			TRACE( "- Windowed: %d\n", m_rD3Dpp.Windowed );
			TRACE( "- EnableAutoDepthStencil: %d\n", m_rD3Dpp.EnableAutoDepthStencil );
			TRACE( "- AutoDepthStencilFormat: %d\n", m_rD3Dpp.AutoDepthStencilFormat );
			TRACE( "- Flags: %d\n", m_rD3Dpp.Flags );
			TRACE( "- FullScreen_RefreshRateInHz: %d\n", m_rD3Dpp.FullScreen_RefreshRateInHz );
			TRACE( "- PresentationInterval: %d\n", m_rD3Dpp.PresentationInterval );

			if( FAILED( hRes = m_pD3DDevice->TestCooperativeLevel() ) )
			{
				// If the device was lost, do not render until we get it back
				if( hRes == D3DERR_DEVICELOST )
					TRACE( "** device lost!\n" );
				else if( hRes == D3DERR_DEVICENOTRESET )
					TRACE( "** device need reset!\n" );
			}
			else
			{
				TRACE( "** co-operative device\n" );
			}

			return;
		}

		m_bLostDevice = false;

		D3DVIEWPORT9	rDXViewport;
		rDXViewport.X = i32X;
		rDXViewport.Y = i32Y;
		rDXViewport.Width = i32Width;
		rDXViewport.Height = i32Height;
		rDXViewport.MinZ = 0.0f;
		rDXViewport.MaxZ = 1.0f;
		if( FAILED( m_pD3DDevice->SetViewport( &rDXViewport ) ) )
			TRACE( "set viewport failed\n" );

		init_validate();
	}
}

HWND
DX9DeviceC::get_hwnd()
{
	return m_hWnd;
}


void
DX9DeviceC::clear_device( uint32 ui32Flags, const ColorC& rColor, float32 f32Depth, int32 i32Stencil )
{
	if( !m_pD3DDevice )
		return;
	if( !m_bD3DReady )
		return;
	if( m_bLostDevice )
		return;

	DWORD	dwFlags = 0;

	if( ui32Flags & GRAPHICSDEVICE_COLORBUFFER )
		dwFlags |= D3DCLEAR_TARGET;

	if( ui32Flags & GRAPHICSDEVICE_DEPTHBUFFER )
		dwFlags |= D3DCLEAR_ZBUFFER;

	if( ui32Flags & GRAPHICSDEVICE_STENCILBUFFER )
		dwFlags |= D3DCLEAR_STENCIL;

//	TRACE( "clear\n" );

	DWORD	dwColor = D3DCOLOR_ARGB( (int32)(rColor[3] * 255), (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );

	HRESULT	hRes;

	if( m_pCurrentBuffer )
		dwFlags &= m_pCurrentBuffer->get_clear_flag_mask();
	else
		dwFlags &= m_dwClearFlagMask;

	if( FAILED( hRes = m_pD3DDevice->SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE ) ) )
		TRACE( "scissor enable failed: %s\n", DXGetErrorString9( hRes ) );

	if( FAILED( hRes = m_pD3DDevice->Clear( 0, 0, dwFlags, dwColor, f32Depth, i32Stencil ) ) )
		TRACE( "clear failed: %s\n", DXGetErrorString9( hRes ) );
}

void
DX9DeviceC::begin_effects()
{
	if( !m_pD3DDevice )
		return;
	if( !m_bD3DReady )
		return;
	if( m_bLostDevice )
		return;

	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );

	if( m_pCurrentBuffer )
	{
		// Empty
	}
	else
	{
		if( m_pStateBlockSaved )
			m_pStateBlockSaved->Capture();
	}
}

void
DX9DeviceC::end_effects()
{
	if( !m_pD3DDevice )
		return;
	if( !m_bD3DReady )
		return;
	if( m_bLostDevice )
		return;

	if( m_pCurrentBuffer )
	{
		// Empty
	}
	else
	{
		if( m_pStateBlockSaved )
			m_pStateBlockSaved->Apply();
	}

	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );
}

bool
DX9DeviceC::begin_draw()
{
	if( !m_pD3DDevice )
		return false;
	if( !m_bD3DReady )
		return false;

	if( m_pCurrentBuffer )
	{
		m_pCurrentBuffer->begin_draw();
	}
	else
	{
		HRESULT	hRes;

		if( FAILED( hRes = m_pD3DDevice->TestCooperativeLevel() ) )
		{
			// If the device was lost, do not render until we get it back
			if( hRes == D3DERR_DEVICELOST )
			{
				TRACE( "device lost\n" );
				m_bLostDevice = true;
				return false;
			}
			
			// Check if the device needs to be resized.
			if( hRes == D3DERR_DEVICENOTRESET )
			{

				m_ui32State = DEVICE_STATE_LOST;

				// Call current demo shutdown function
				init_invalidate();
				
				TRACE( "Reset (begin)\n" );
				// Resize the device
				if( FAILED( hRes = m_pD3DDevice->Reset( &m_rD3Dpp ) ) )
				{
					TRACE( "Reset failed: %s\n", DXGetErrorString9( hRes ) );
					m_bLostDevice = true;
					return false;
				}

				m_bLostDevice = false;

				init_validate();


				m_ui32State = DEVICE_STATE_OK;
			}
		}

		if( FAILED( m_pD3DDevice->BeginScene() ) )
			TRACE( "begin scene failed\n" );

//		TRACE( "BeginScene\n" );
	}

	return true;
}

void
DX9DeviceC::end_draw()
{
	if( !m_pD3DDevice )
		return;
	if( !m_bD3DReady )
		return;
	if( m_bLostDevice )
		return;

	if( m_pCurrentBuffer )
	{
		m_pCurrentBuffer->end_draw();
	}
	else
	{
		if( FAILED( m_pD3DDevice->EndScene() ) )
			TRACE( "end scene failed\n" );

		// Check that no temp buffer is still locked, if is, unlock it.
		for( std::list<TempBufferS>::iterator TmpIt = m_lstTempBuffers.begin(); TmpIt != m_lstTempBuffers.end(); ++TmpIt )
		{
			TempBufferS&	Tmp = (*TmpIt);
			if( Tmp.bLocked && Tmp.pBuffer )
			{
				DX9ViewportC*	pViewport = (DX9ViewportC*)Tmp.pBuffer->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
				if( pViewport )
					TRACE( "Temp buffer still locked (%dx%d)!\n", pViewport->get_width(), pViewport->get_height() );
				else
					TRACE( "Temp buffer still locked!\n" );
				Tmp.bLocked = false;
			}
		}
	}
}

LPDIRECT3DDEVICE9
DX9DeviceC::get_d3ddevice()
{
	return m_pD3DDevice;
}


bool
DX9DeviceC::set_fullscreen( uint32 ui32Width, uint32 ui32Height, uint32 ui32BPP )
{
	if( !m_pD3DDevice )
		return false;

	ModeS*	pFoundMode = 0;

	std::list<ModeS>::iterator it;
	for( it = m_lstModes.begin(); it != m_lstModes.end(); ++it )
	{
		ModeS&	Mode = (*it);
		if( Mode.ui32Width >= ui32Width && Mode.ui32Height >= ui32Height )
		{
			pFoundMode = &Mode;
			break;
		}
	}

	if( !pFoundMode )
		return false;


	// Call current demo shutdown function
	m_ui32State = DEVICE_STATE_LOST;
	init_invalidate();

	// Set fullscreen-mode presentation parameters

	TRACE( "set_fullscreen: %dx%d (%d)\n", pFoundMode->ui32Width, pFoundMode->ui32Height, pFoundMode->TargetFormat );

	m_rSavedD3Dpp = m_rD3Dpp;

	m_rD3Dpp.Windowed         = FALSE;
	m_rD3Dpp.hDeviceWindow    = m_hWnd;
	m_rD3Dpp.BackBufferWidth  = pFoundMode->ui32Width;
	m_rD3Dpp.BackBufferHeight = pFoundMode->ui32Height;
	m_rD3Dpp.BackBufferFormat = pFoundMode->TargetFormat;
	m_rD3Dpp.AutoDepthStencilFormat = pFoundMode->DepthStencilFormat;
	m_rD3Dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;


	m_rSavedViewport = m_pInterface->get_viewport();
	m_ui32SavedCreateFlags = m_ui32CreateFlags;
	m_ui32CreateFlags = GRAPHICSDEVICE_CREATE_FULLSCREEN;

	// Save window state
	m_lSavedWindowStyle = GetWindowLong( m_hWnd, GWL_STYLE );
	m_lSavedExWindowStyle = GetWindowLong( m_hWnd, GWL_EXSTYLE );
	m_hSavedParent = GetParent( m_hWnd );
	GetWindowRect( m_hWnd, &m_rSavedWindowRect );

	SetWindowLong( m_hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE );
	SetWindowLong( m_hWnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW );
	SetParent( m_hWnd, NULL );

	// Reset the device
	HRESULT	hRes;
	if( FAILED( hRes = m_pD3DDevice->Reset( &m_rD3Dpp ) ) )
	{
		m_ui32CreateFlags = m_ui32SavedCreateFlags;
		SetWindowLong( m_hWnd, GWL_STYLE, m_lSavedWindowStyle );
		SetWindowLong( m_hWnd, GWL_EXSTYLE, m_lSavedExWindowStyle );
		SetParent( m_hWnd, m_hSavedParent );

		TRACE( "Reset failed: %s\n", DXGetErrorString9( hRes ) );

		TRACE( "- BackBufferWidth: %d\n", m_rD3Dpp.BackBufferWidth );
		TRACE( "- BackBufferHeight: %d\n", m_rD3Dpp.BackBufferHeight );
		TRACE( "- BackBufferFormat: %d\n", m_rD3Dpp.BackBufferFormat );
		TRACE( "- BackBufferCount: %d\n", m_rD3Dpp.BackBufferCount );
		TRACE( "- MultiSampleType: %d\n", m_rD3Dpp.MultiSampleType );
		TRACE( "- MultiSampleQuality: %d\n", m_rD3Dpp.MultiSampleQuality );
		TRACE( "- SwapEffect: %d\n", m_rD3Dpp.SwapEffect );
		TRACE( "- hDeviceWindow: %d\n", m_rD3Dpp.hDeviceWindow );
		TRACE( "- Windowed: %d\n", m_rD3Dpp.Windowed );
		TRACE( "- EnableAutoDepthStencil: %d\n", m_rD3Dpp.EnableAutoDepthStencil );
		TRACE( "- AutoDepthStencilFormat: %d\n", m_rD3Dpp.AutoDepthStencilFormat );
		TRACE( "- Flags: %d\n", m_rD3Dpp.Flags );
		TRACE( "- FullScreen_RefreshRateInHz: %d\n", m_rD3Dpp.FullScreen_RefreshRateInHz );
		TRACE( "- PresentationInterval: %d\n", m_rD3Dpp.PresentationInterval );

		if( FAILED( hRes = m_pD3DDevice->TestCooperativeLevel() ) )
		{
			// If the device was lost, do not render until we get it back
			if( hRes == D3DERR_DEVICELOST )
				TRACE( "** device lost!\n" );
			else if( hRes == D3DERR_DEVICENOTRESET )
				TRACE( "** device need reset!\n" );
		}
		else
		{
			TRACE( "** co-operative device\n" );
		}

		m_rD3Dpp = m_rSavedD3Dpp;

		m_bLostDevice = true;
		return false;
	}

	m_bLostDevice = false;

	// Call current demo initialise function
	init_validate();


	MoveWindow( m_hWnd, 0, 0, pFoundMode->ui32Width, pFoundMode->ui32Height, TRUE );
	UpdateWindow( m_hWnd );

//	set_size( 0, 0, ui32Width, ui32Height );

	//
	// The windowed and fullscreen are maximized.
	//
	BBox2C	rLayout = m_pInterface->get_layout();

	float32	f32LayoutWidth = rLayout.width();
	float32	f32LayoutHeight = rLayout.height();

	float64	f64AspectX = (float64)pFoundMode->ui32Width / (float64)f32LayoutWidth;
	float64	f64AspectY = (float64)pFoundMode->ui32Height / (float64)f32LayoutHeight;

	if( f64AspectX < f64AspectY )
		f32LayoutHeight = f32LayoutWidth / (float32)pFoundMode->ui32Width * (float32)pFoundMode->ui32Height;
	else
		f32LayoutWidth = f32LayoutHeight / (float32)pFoundMode->ui32Height * (float32)pFoundMode->ui32Width;

	float32	f32LayoutPosX = (f32LayoutWidth * 0.5f) - (rLayout.width() * 0.5f);
	float32	f32LayoutPosY = (f32LayoutHeight * 0.5f) - (rLayout.height() * 0.5f);


	BBox2C	rViewport( Vector2C( -f32LayoutPosX, -f32LayoutPosY ),
						  Vector2C( -f32LayoutPosX + f32LayoutWidth, -f32LayoutPosY + f32LayoutHeight ) );

	m_pInterface->set_viewport( rViewport );

	m_pInterface->set_dimension( 0, 0, pFoundMode->ui32Width, pFoundMode->ui32Height );

	D3DVIEWPORT9	rDXViewport;
	rDXViewport.X = 0;
	rDXViewport.Y = 0;
	rDXViewport.Width = pFoundMode->ui32Width;
	rDXViewport.Height = pFoundMode->ui32Height;
	rDXViewport.MinZ = 0.0f;
	rDXViewport.MaxZ = 1.0f;
	if( FAILED( m_pD3DDevice->SetViewport( &rDXViewport ) ) )
		TRACE( "Set viewport failed\n" );

	m_ui32State = DEVICE_STATE_OK;

	return true;
}

bool
DX9DeviceC::set_windowed()
{
	if( !m_pD3DDevice )
		return false;

	OutputDebugString( "set_windowed()\n" );

	// Restore windowed
	m_rD3Dpp = m_rSavedD3Dpp;

	// Restore window state
	m_ui32CreateFlags = m_ui32SavedCreateFlags;
	SetWindowLong( m_hWnd, GWL_STYLE, m_lSavedWindowStyle );
	SetWindowLong( m_hWnd, GWL_EXSTYLE, m_lSavedExWindowStyle );
	SetParent( m_hWnd, m_hSavedParent );

	// Call current demo shutdown function
	m_ui32State = DEVICE_STATE_LOST;
	init_invalidate();

	// Reset the device
	if( FAILED( m_pD3DDevice->Reset( &m_rD3Dpp ) ) )
	{
		m_ui32CreateFlags = m_ui32SavedCreateFlags;
		SetWindowLong( m_hWnd, GWL_STYLE, m_lSavedWindowStyle );
		SetWindowLong( m_hWnd, GWL_EXSTYLE, m_lSavedExWindowStyle );
		SetParent( m_hWnd, m_hSavedParent );
		m_pInterface->set_viewport( m_rSavedViewport );
		TRACE( "reset failed.\n" );
		m_bLostDevice = true;
		return false;
	}

	m_bLostDevice = false;

	// Call current demo initialise function
	init_validate();

	m_ui32CreateFlags = m_ui32SavedCreateFlags;

	RECT	rRect;
	GetClientRect( m_hSavedParent, &rRect );
	MoveWindow( m_hWnd, rRect.left, rRect.top, (rRect.right - rRect.left), (rRect.bottom - rRect.top), TRUE );
	UpdateWindow( m_hWnd );

	m_pInterface->set_dimension( rRect.left, rRect.top, (rRect.right - rRect.left), (rRect.bottom - rRect.top) );
	m_pInterface->set_viewport( m_rSavedViewport );

//	set_size( rRect.left, rRect.top, (rRect.right - rRect.left), (rRect.bottom - rRect.top) );

	D3DVIEWPORT9	rDXViewport;
	rDXViewport.X = rRect.left;
	rDXViewport.Y = rRect.top;
	rDXViewport.Width = (rRect.right - rRect.left);
	rDXViewport.Height = (rRect.bottom - rRect.top);
	rDXViewport.MinZ = 0.0f;
	rDXViewport.MaxZ = 1.0f;
	if( FAILED( m_pD3DDevice->SetViewport( &rDXViewport ) ) )
		TRACE( "set viewport failed\n" );

	m_ui32State = DEVICE_STATE_OK;

	return true;
}

GraphicsBufferI*
DX9DeviceC::create_graphicsbuffer()
{
	DX9BufferC* pBuffer = DX9BufferC::create_new( this );
	register_buffer( pBuffer );
	return pBuffer;
}

void
DX9DeviceC::register_buffer( DX9BufferC* pBuffer )
{
	m_lstBuffers.push_back( pBuffer );
}

void
DX9DeviceC::unregister_buffer( DX9BufferC* pBuffer )
{
	for( std::list<DX9BufferC*>::iterator BufIt = m_lstBuffers.begin(); BufIt != m_lstBuffers.end(); ++BufIt )
	{
		if( (*BufIt) == pBuffer )
		{
			m_lstBuffers.erase( BufIt );
			return;
		}
	}
}

void
DX9DeviceC::set_temp_graphicsbuffer_size( uint32 ui32Width, uint32 ui32Height )
{
	if( m_ui32TempHeight != ui32Width || m_ui32TempHeight != ui32Height )
	{
		m_ui32TempWidth = ui32Width;
		m_ui32TempHeight = ui32Height;

		// Re-initialise buffers.
		for( std::list<TempBufferS>::iterator TmpIt = m_lstTempBuffers.begin(); TmpIt != m_lstTempBuffers.end(); )
		{
			TempBufferS&	Tmp = (*TmpIt);

			if( Tmp.pBuffer )
				Tmp.pBuffer->release();

			uint32	ui32NewWidth = m_ui32TempWidth;
			uint32	ui32NewHeight = m_ui32TempHeight;

			if( Tmp.ui32Flags & GRAPHICSDEVICE_TEMPBUF_HALF_SIZE )
			{
				ui32NewWidth /= 2;
				ui32NewHeight /= 2;
			}
			else if( Tmp.ui32Flags & GRAPHICSDEVICE_TEMPBUF_QUARTER_SIZE )
			{
				ui32NewWidth /= 4;
				ui32NewHeight /= 4;
			}

			DX9BufferC*	pTempGBuffer = (DX9BufferC*)create_graphicsbuffer();
			if( pTempGBuffer )
			{
				if( !pTempGBuffer->init( Tmp.ui32CreationFlags, ui32NewWidth, ui32NewHeight ) )
				{
					pTempGBuffer->release();
					pTempGBuffer = 0;
				}
			}

			Tmp.pBuffer = pTempGBuffer;

			if( !pTempGBuffer )
				TmpIt = m_lstTempBuffers.erase( TmpIt );
			else
				++TmpIt;
		}
	}
}

GraphicsBufferI*
DX9DeviceC::get_temp_graphicsbuffer( PajaTypes::uint32 ui32Flags )
{
	// Check if there's unused buffer matching the flags.
	for( std::list<TempBufferS>::iterator TmpIt = m_lstTempBuffers.begin(); TmpIt != m_lstTempBuffers.end(); ++TmpIt )
	{
		TempBufferS&	Tmp = (*TmpIt);
		if( !Tmp.bLocked && Tmp.ui32Flags == ui32Flags )
		{
			Tmp.bLocked = true;
			return Tmp.pBuffer;
		}
	}

	TRACE( "New buffer\n" );

	// We get here, if not buffer was available, create new.

	uint32	ui32Width = m_ui32TempWidth;
	uint32	ui32Height = m_ui32TempHeight;

	if( ui32Flags & GRAPHICSDEVICE_TEMPBUF_HALF_SIZE )
	{
		ui32Width /= 2;
		ui32Height /= 2;
	}
	else if( ui32Flags & GRAPHICSDEVICE_TEMPBUF_QUARTER_SIZE )
	{
		ui32Width /= 4;
		ui32Height /= 4;
	}

	uint32	ui32CreationFlags = GRAPHICSBUFFER_INIT_TEXTURE;

	if( ui32Flags & GRAPHICSDEVICE_TEMPBUF_COLOR )
		ui32CreationFlags |= GRAPHICSBUFFER_INIT_COLOR;
	if( ui32Flags & GRAPHICSDEVICE_TEMPBUF_DEPTH )
		ui32CreationFlags |= GRAPHICSBUFFER_INIT_DEPTH;
	if( ui32Flags & GRAPHICSDEVICE_TEMPBUF_EXTRA )
		ui32CreationFlags |= GRAPHICSBUFFER_INIT_EXTRA;

	DX9BufferC*	pTempGBuffer = (DX9BufferC*)create_graphicsbuffer();
	if( !pTempGBuffer )
		return 0;

	if( !pTempGBuffer->init( ui32CreationFlags, ui32Width, ui32Height ) )
	{
		pTempGBuffer->release();
		return 0;
	}

	TempBufferS	Tmp;
	Tmp.pBuffer = pTempGBuffer;
	Tmp.ui32CreationFlags = ui32CreationFlags;
	Tmp.ui32Flags = ui32Flags;
	Tmp.bLocked = true;
	m_lstTempBuffers.push_back( Tmp );

	return (GraphicsBufferI*)pTempGBuffer;
}

void
DX9DeviceC::free_temp_graphicsbuffer( GraphicsBufferI* pGBuf )
{
	// Clear the locked flag, so that the buffer can be reused.
	for( std::list<TempBufferS>::iterator TmpIt = m_lstTempBuffers.begin(); TmpIt != m_lstTempBuffers.end(); ++TmpIt )
	{
		TempBufferS&	Tmp = (*TmpIt);
		if( Tmp.pBuffer == pGBuf )
		{
			Tmp.bLocked = false;
			return;
		}
	}
	TRACE( "Could not free buffer\n" );
}


GraphicsBufferI*
DX9DeviceC::set_render_target( GraphicsBufferI* pBuffer )
{
	if( !m_pD3DDevice )
		return 0;
	if( !m_bD3DReady )
		return 0;
	if( m_bLostDevice )
		return 0;

	if( pBuffer )
	{
//		TRACE( "buffer get render target\n" );
		if( !m_bBuffersSaved )
		{
/*			if( FAILED( m_pD3DDevice->GetRenderTarget( 0, &m_pBackBuffer ) ) )
				TRACE( "get render target failed\n ");
			if( FAILED( m_pD3DDevice->GetRenderTarget( 1, &m_pBackBuffer2 ) ) )
				TRACE( "get render target failed\n ");
			if( FAILED( m_pD3DDevice->GetDepthStencilSurface( &m_pZBuffer ) ) )
				TRACE( "get depth stencil failed\n ");*/
			m_pD3DDevice->GetRenderTarget( 0, &m_pBackBuffer );
			m_pD3DDevice->GetRenderTarget( 1, &m_pBackBuffer2 );
			m_pD3DDevice->GetDepthStencilSurface( &m_pZBuffer );
			m_bBuffersSaved = true;
		}

		DX9BufferC*	pPrevBuffer = m_pCurrentBuffer;
		m_pCurrentBuffer = (DX9BufferC*)pBuffer;
		m_pCurrentBuffer->activate();
		return pPrevBuffer;
	}

//	TRACE( "dev render target\n" );

	if( m_bBuffersSaved )
	{
		// Restore output.
		if( FAILED( m_pD3DDevice->SetRenderTarget( 0, m_pBackBuffer ) ) )
			TRACE( "set render target failed\n" );
		if( m_pBackBuffer )
		{
			m_pBackBuffer->Release();
			m_pBackBuffer = 0;
		}

		if( FAILED( m_pD3DDevice->SetRenderTarget( 1, m_pBackBuffer2 ) ) )
			TRACE( "set render target failed\n" );
		if( m_pBackBuffer2 )
		{
			m_pBackBuffer2->Release();
			m_pBackBuffer2 = 0;
		}

		if( FAILED( m_pD3DDevice->SetDepthStencilSurface( m_pZBuffer ) ) )
			TRACE( "set depth stencil failed\n" );
		if( m_pZBuffer )
		{
			m_pZBuffer->Release();
			m_pZBuffer = 0;
		}

		m_bBuffersSaved = false;
	}

	// use this buffer
	m_pCurrentBuffer = 0;
	activate();

	return 0;
}

GraphicsBufferI*
DX9DeviceC::get_render_target()
{
	return m_pCurrentBuffer;
}

void
DX9DeviceC::init_invalidate()
{
//	m_ui32State = DEVICE_STATE_LOST;
//	if( m_bLostDevice )
//		return;

	if( m_pFeedback )
		m_pFeedback->send_init( INIT_DEVICE_INVALIDATE );

	m_pGUIDrawInterface->init_invalidate();

	if( m_pStateBlockSaved )
		m_pStateBlockSaved->Release();
	m_pStateBlockSaved = 0;

	for( std::list<DX9BufferC*>::iterator BufIt = m_lstBuffers.begin(); BufIt != m_lstBuffers.end(); ++BufIt )
	{
		DX9BufferC*	pBuf = (*BufIt);
		if( pBuf )
			pBuf->init_invalidate();
	}
}

void
DX9DeviceC::init_validate()
{
//	m_ui32State = DEVICE_STATE_OK;
	if( m_bLostDevice )
		return;

	if( m_pFeedback )
		m_pFeedback->send_init( INIT_DEVICE_VALIDATE );

	m_pGUIDrawInterface->init_validate();

	if( !m_pStateBlockSaved )
	{
		HRESULT	hr;
		if( FAILED( hr = m_pD3DDevice->CreateStateBlock( D3DSBT_ALL, &m_pStateBlockSaved ) ) )
			TRACE( "CreateStateBlock failed\n ");
	}

	for( std::list<DX9BufferC*>::iterator BufIt = m_lstBuffers.begin(); BufIt != m_lstBuffers.end(); ++BufIt )
	{
		DX9BufferC*	pBuf = (*BufIt);
		if( pBuf )
			pBuf->init_validate();
	}
}

uint32
DX9DeviceC::get_flags()
{
	if( m_pCurrentBuffer )
		return m_pCurrentBuffer->get_flags();
	return GRAPHICSBUFFER_INIT_COLOR | GRAPHICSBUFFER_INIT_DEPTH;
}