#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <string>
#include <d3d8.h>
#include <d3dx8math.h>
#include <dxerr8.h>

//#include "res\resource.h"

#include "PajaTypes.h"
#include "ColorC.h"
#include "DX8DeviceC.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 );
}




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



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

	// the usual window procedure...

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

	case WM_DESTROY:
		OutputDebugString( "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 );
		return 0;

	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_CHILD )
			return PostMessage( GetParent( hWnd ), uMsg, wParam, lParam );
		break;

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

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

	case WM_SIZE:
		if( pDevice->m_pD3DDevice && pDevice->m_ui32CreateFlags != GRAPHICSDEVICE_CREATE_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 );
}



DX8DeviceC::DX8DeviceC() :
	m_hWnd( 0 ),
	m_pD3D( 0 ),
	m_pD3DDevice( 0 ),
	m_dwSavedState( 0 ),
	m_pFeedback( 0 ),
	m_dwCurAdapter( 0 ),
	m_bD3DReady( false )
{
	m_pInterface = DX8ViewportC::create_new( this );
	m_pGUIDrawInterface = DX8GUIDrawInterfaceC::create_new( this );
	ZeroMemory( &m_rD3Dpp, sizeof( m_rD3Dpp ) );
}

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

    if( m_dwSavedState )
        m_pD3DDevice->DeleteStateBlock( m_dwSavedState );

	if( m_pInterface )
		m_pInterface->release();
	if( m_pGUIDrawInterface )
		m_pGUIDrawInterface->release();

    if( m_pD3DDevice != NULL )
        m_pD3DDevice->Release();
	
    if( m_pD3D != NULL )
        m_pD3D->Release();

	m_bD3DReady = false;
}

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

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

ClassIdC
DX8DeviceC::get_class_id() const
{
	return CLASS_DX8_DEVICEDRIVER;
}

const char*
DX8DeviceC::get_class_name()
{
	return "DirectX 8 Device Driver";
}

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

uint32
DX8DeviceC::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
DX8DeviceC::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_IDENTIFIER8 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
			D3DCAPS8 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
DX8DeviceC::find_depth_stencil_format( UINT iAdapter, D3DDEVTYPE rDeviceType,
									   D3DFORMAT rTargetFormat, D3DFORMAT* pDepthStencilFormat )
{
/*	DWORD	dwMinDepthBits;
	DWORD	dwMinStencilBits;

	if( dwMinDepthBits <= 16 && dwMinStencilBits == 0 ) {
		if( SUCCEEDED( m_pD3D->CheckDeviceFormat( iAdapter, DeviceType,
			TargetFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D16 ) ) ) {
			if( SUCCEEDED( m_pD3D->CheckDepthStencilMatch( iAdapter, rDeviceType,
				rTargetFormat, TargetFormat, D3DFMT_D16 ) ) ) {
				*pDepthStencilFormat = D3DFMT_D16;
				return true;
			}
		}
	}
*/	
	
/*	if( dwMinDepthBits <= 24 && dwMinStencilBits == 0 ) {
		if( SUCCEEDED( m_pD3D->CheckDeviceFormat( iAdapter, DeviceType,
			TargetFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24X8 ) ) ) {
			if( SUCCEEDED( m_pD3D->CheckDepthStencilMatch( iAdapter, rDeviceType,
				rTargetFormat, TargetFormat, D3DFMT_D24X8 ) ) ) {
				*pDepthStencilFormat = D3DFMT_D24X8;
				return true;
			}
		}
	}
*/
  
//	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;
			}
		}
//	}
		
/*	if( dwMinDepthBits <= 32 && dwMinStencilBits == 0 ) {
		if( SUCCEEDED( m_pD3D->CheckDeviceFormat( iAdapter, DeviceType,
			TargetFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D32 ) ) ) {
			if( SUCCEEDED( m_pD3D->CheckDepthStencilMatch( iAdapter, rDeviceType,
				rTargetFormat, TargetFormat, D3DFMT_D32 ) ) ) {
				*pDepthStencilFormat = D3DFMT_D32;
				return true;
			}
		}
	}*/
	
	return false;
}


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

	//
	// 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 = "DemopajaDX8Device";

		// 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, "DemopajaDX8Device", (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_CHILD ) {
		// Create child window
		m_hWnd = CreateWindowEx( WS_EX_TRANSPARENT, "DemopajaDX8Device", (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 ) {

		uint32	ui32Caption = GetSystemMetrics( SM_CYCAPTION );
		uint32	ui32BorderX = GetSystemMetrics( SM_CXBORDER );
		uint32	ui32BorderY = GetSystemMetrics( SM_CXBORDER );

		// Create popup window
		m_hWnd = CreateWindowEx( 0, "DemopajaOpenGLDevice", "Demopaja Player",
								WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
								0, 0, ui32Width + 2 * ui32BorderX, ui32Height + ui32Caption + ui32BorderY * 2,
								hParent, NULL, hInstance, (LPVOID)this );

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

		ShowWindow( m_hWnd, SW_SHOW );

		m_pInterface->set_dimension( 0, 0, ui32Width, ui32Height );
	}
	else if( ui32Flags == GRAPHICSDEVICE_CREATE_FULLSCREEN ) {

		// Create fullscreen window
		m_hWnd = CreateWindowEx( WS_EX_TOPMOST, "DemopajaOpenGLDevice", "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;
		}

		if( ui32BPP == 0 ) {
			// Get desktop BPP
			HDC dc = GetDC( 0 ); // desktop dc
			ui32BPP = GetDeviceCaps( dc, BITSPIXEL );
			ReleaseDC( 0, dc );
		}

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

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


	// Create the D3D object, which is needed to create the D3DDevice.
	if( ( m_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) == NULL ) {
		OutputDebugString( "Direct3DCreate8 failed\n" );
		return false;
	}
	
	build_device_list( ui32Width, ui32Height, ui32BPP );

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

	// Get the current desktop display mode
	D3DDISPLAYMODE	rDesktopMode;
	D3DFORMAT		rDesktopDepthStencilFormat;
	D3DCAPS8		rDesktopCaps;
	D3DDEVTYPE		rDesktopDevType;
	DWORD			dwBehavior;
	D3DDEVTYPE		rDevType;

	ZeroMemory( &m_rD3Dpp, sizeof( m_rD3Dpp ) );
	m_rD3Dpp.Windowed = bWindowed ? TRUE : FALSE;
	m_rD3Dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	m_rD3Dpp.EnableAutoDepthStencil = TRUE;

	if( bWindowed ) {
		if( FAILED( m_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &rDesktopMode ) ) ) {
			OutputDebugString( "GetAdapterDisplayMode failed\n" );
			return false;
		}
		if( SUCCEEDED( m_pD3D->CheckDeviceType( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, rDesktopMode.Format, rDesktopMode.Format, TRUE ) ) )
			rDesktopDevType = D3DDEVTYPE_HAL;
		else
			rDesktopDevType = D3DDEVTYPE_REF;

		if( !find_depth_stencil_format( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, rDesktopMode.Format, &rDesktopDepthStencilFormat ) )
			return false;

		m_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, rDesktopDevType, &rDesktopCaps );

		m_rD3Dpp.BackBufferFormat = rDesktopMode.Format;
		m_rD3Dpp.AutoDepthStencilFormat = rDesktopDepthStencilFormat;

		if( rDesktopCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
			dwBehavior = D3DCREATE_HARDWARE_VERTEXPROCESSING;	// and purehal?;
		else
			dwBehavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

		rDevType = rDesktopDevType;
	}
	else {
		const DeviceS&	rDevice = m_rAdapters[m_dwCurAdapter].rDevices[m_rAdapters[m_dwCurAdapter].dwCurrentDevice];
		const ModeS&	rMode = rDevice.rModes[rDevice.dwCurrentMode];
		m_rD3Dpp.AutoDepthStencilFormat	= rMode.rDepthStencilFormat;
		m_rD3Dpp.BackBufferWidth = rMode.dwWidth;
        m_rD3Dpp.BackBufferHeight = rMode.dwHeight;
        m_rD3Dpp.BackBufferFormat = rMode.rFormat;
		m_rD3Dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;  // Don't lock to vsync
		dwBehavior = rMode.dwBehavior;
		rDevType = rDevice.rDeviceType;
	}

	HRESULT	hRes;
	
	if( FAILED( hRes = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, rDevType, m_hWnd,
									  dwBehavior, &m_rD3Dpp, &m_pD3DDevice ) ) )
	{
		OutputDebugString( "CreateDevice failed: " );
		OutputDebugString( DXGetErrorString8( hRes ) );
		OutputDebugString( "\n" );

		return false;
	}


	// Create state block
	m_pD3DDevice->CreateStateBlock( D3DSBT_ALL, &m_dwSavedState );


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

	m_bD3DReady = true;

	return true;
}

DeviceInterfaceI*
DX8DeviceC::query_interface( const SuperClassIdC& rSuperClassId )
{
	if( rSuperClassId == GRAPHICSDEVICE_VIEWPORT_INTERFACE )
		return m_pInterface;
	else if( rSuperClassId == GRAPHICSDEVICE_GUIDRAW_INTERFACE )
		return m_pGUIDrawInterface;

	return 0;
}


void
DX8DeviceC::activate()
{
}


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

void
DX8DeviceC::flush()
{
	if( !m_pD3DDevice )
		return;

	if( !m_bD3DReady )
		return;

	m_pD3DDevice->Present( NULL, NULL, NULL, NULL );
}



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
DX8DeviceC::configure()
{
//	return DialogBox( g_hInstance, (LPCTSTR)IDD_CONFIG, NULL, (DLGPROC)ConfigDlgProc ) == IDOK ? true : false;
	return true;
}


void
DX8DeviceC::set_size( int32 i32X, int32 i32Y, int32 i32Width, int32 i32Height )
{

	BBox2C	rViewport;

	if( !m_bD3DReady )
		return;

	if( m_ui32CreateFlags == GRAPHICSDEVICE_CREATE_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 );

		rViewport = m_pInterface->get_viewport();

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

	}
	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);

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

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

	if( m_pD3DDevice ) {

		m_ui32State = DEVICE_STATE_LOST;
		if( m_pFeedback )
			m_pFeedback->send_init( INIT_DEVICE_INVALIDATE );
		m_pGUIDrawInterface->init_invalidate();
		if( m_dwSavedState )
			m_pD3DDevice->DeleteStateBlock( m_dwSavedState );
		m_dwSavedState = 0;

		m_pD3DDevice->Reset( &m_rD3Dpp );

		D3DVIEWPORT8	rViewport;
		rViewport.X = i32X;
		rViewport.Y = i32Y;
		rViewport.Width = i32Width;
		rViewport.Height = i32Height;
		rViewport.MinZ = 0.0f;
		rViewport.MaxZ = 1.0f;
		m_pD3DDevice->SetViewport( &rViewport );

		m_ui32State = DEVICE_STATE_OK;
		if( m_pFeedback )
			m_pFeedback->send_init( INIT_DEVICE_VALIDATE );
		m_pGUIDrawInterface->init_validate();
		m_pD3DDevice->CreateStateBlock( D3DSBT_ALL, &m_dwSavedState );
	}
}

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


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

	if( !m_bD3DReady )
		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;

	m_pD3DDevice->Clear( 0, NULL, dwFlags,
		D3DCOLOR_ARGB( (int32)(rColor[3] * 255), (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) ),
		f32Depth, i32Stencil );
}

void
DX8DeviceC::begin_effects()
{
	if( !m_pD3DDevice )
		return;

	if( !m_bD3DReady )
		return;

	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
	m_pD3DDevice->CaptureStateBlock( m_dwSavedState );




	D3DXMATRIX	rIdentity;
	D3DXMatrixIdentity( &rIdentity );

	D3DXMATRIX	rOrtho;
	float32		f32Left, f32Right, f32Top, f32Bottom, f32Near, f32Far;

	BBox2C	rViewport = m_pInterface->get_viewport();

	f32Left = rViewport[0][0];
	f32Right = rViewport[1][0];
	f32Bottom = rViewport[0][1];
	f32Top = rViewport[1][1];
	f32Near = -1.0f;
	f32Far = 1.0f;

	D3DXMatrixOrthoOffCenterLH( &rOrtho, f32Left, f32Right, f32Bottom, f32Top, f32Near, f32Far );

	m_pD3DDevice->SetTransform( D3DTS_PROJECTION, &rOrtho );
	m_pD3DDevice->SetTransform( D3DTS_VIEW, &rIdentity );
	m_pD3DDevice->SetTransform( D3DTS_WORLD, &rIdentity );

	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );


struct XYZWCOLOR_VERTEX {
	float	x, y, z, w;
	DWORD	color;
};
#define FVF_XYZWCOLOR_VERTEX			D3DFVF_XYZRHW | D3DFVF_DIFFUSE

	BBox2C	rLayout = m_pInterface->get_layout();
	Vector2C	rStart = m_pInterface->layout_to_client( rLayout[0] );
	Vector2C	rEnd = m_pInterface->layout_to_client( rLayout[1] );

	XYZWCOLOR_VERTEX	rLine[2];
	rLine[0].x = rStart[0];
	rLine[0].y = m_pInterface->get_height() - 1 - rStart[1];
	rLine[0].z = 0;
	rLine[0].w = 1;
	rLine[0].color = 0xffffffff;

	rLine[1].x = rEnd[0];
	rLine[1].y = m_pInterface->get_height() - 1 - rEnd[1];
	rLine[1].z = 0;
	rLine[1].w = 1;
	rLine[1].color = 0xffffffff;

	m_pD3DDevice->SetVertexShader( FVF_XYZWCOLOR_VERTEX );
	m_pD3DDevice->DrawPrimitiveUP( D3DPT_LINELIST, 1, rLine, sizeof( XYZWCOLOR_VERTEX ) );


	static	int	i = 0;
	i++;
	char	szMsg[256];
	_snprintf( szMsg, 255, "[%d]", i );
	m_pGUIDrawInterface->set_color( ColorC( 1, 1, 1, 1 ) );
	m_pGUIDrawInterface->draw_text( Vector2C( 10, 10 ), szMsg );

	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
}

void
DX8DeviceC::end_effects()
{
	if( !m_pD3DDevice )
		return;

	if( !m_bD3DReady )
		return;

	m_pD3DDevice->ApplyStateBlock( m_dwSavedState );
	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );
}

void
DX8DeviceC::begin_draw()
{
	if( !m_pD3DDevice )
		return;

	if( !m_bD3DReady )
		return;

	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 )
			return;
		
		// Check if the device needs to be resized.
		if( hRes == D3DERR_DEVICENOTRESET ) {   
			// Call current demo shutdown function
			m_ui32State = DEVICE_STATE_LOST;
			if( m_pFeedback )
				m_pFeedback->send_init( INIT_DEVICE_INVALIDATE );
			m_pGUIDrawInterface->init_invalidate();
			if( m_dwSavedState )
				m_pD3DDevice->DeleteStateBlock( m_dwSavedState );
			m_dwSavedState = 0;

			
			// Resize the device
			if( FAILED( m_pD3DDevice->Reset( &m_rD3Dpp ) ) )
				return;
			
			// Call current demo initialise function
			m_ui32State = DEVICE_STATE_OK;
			if( m_pFeedback )
				m_pFeedback->send_init( INIT_DEVICE_VALIDATE );
			m_pGUIDrawInterface->init_validate();
			m_pD3DDevice->CreateStateBlock( D3DSBT_ALL, &m_dwSavedState );
		}
		return;
	}


	m_pD3DDevice->BeginScene();
}

void
DX8DeviceC::end_draw()
{
	if( !m_pD3DDevice )
		return;

	if( !m_bD3DReady )
		return;




/*
	D3DXMATRIX	rIdentity;
	D3DXMatrixIdentity( &rIdentity );

	D3DXMATRIX	rOrtho;
	float32		f32Left, f32Right, f32Top, f32Bottom, f32Near, f32Far;

	BBox2C	rViewport = m_pInterface->get_viewport();

	f32Left = rViewport[0][0];
	f32Right = rViewport[1][0];
	f32Bottom = rViewport[0][1];
	f32Top = rViewport[1][1];
	f32Near = -1.0f;
	f32Far = 1.0f;

	D3DXMatrixOrthoOffCenterLH( &rOrtho, f32Left, f32Right, f32Bottom, f32Top, f32Near, f32Far );

	m_pD3DDevice->SetTransform( D3DTS_PROJECTION, &rOrtho );
	m_pD3DDevice->SetTransform( D3DTS_VIEW, &rIdentity );
	m_pD3DDevice->SetTransform( D3DTS_WORLD, &rIdentity );

	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );


struct XYZWCOLOR_VERTEX {
	float	x, y, z, w;
	DWORD	color;
};
#define FVF_XYZWCOLOR_VERTEX			D3DFVF_XYZRHW | D3DFVF_DIFFUSE

	BBox2C	rLayout = m_pInterface->get_layout();
	Vector2C	rStart = m_pInterface->layout_to_client( rLayout[0] );
	Vector2C	rEnd = m_pInterface->layout_to_client( rLayout[1] );

	XYZWCOLOR_VERTEX	rLine[2];
	rLine[0].x = rStart[0];
	rLine[0].y = m_pInterface->get_height() - 1 - rStart[1];
	rLine[0].z = 0;
	rLine[0].w = 1;
	rLine[0].color = 0xffffffff;

	rLine[1].x = rEnd[0];
	rLine[1].y = m_pInterface->get_height() - 1 - rEnd[1];
	rLine[1].z = 0;
	rLine[1].w = 1;
	rLine[1].color = 0xffffffff;

	m_pD3DDevice->SetVertexShader( FVF_XYZWCOLOR_VERTEX );
	m_pD3DDevice->DrawPrimitiveUP( D3DPT_LINELIST, 1, rLine, sizeof( XYZWCOLOR_VERTEX ) );


	static	int	i = 0;
	i++;
	char	szMsg[256];
	_snprintf( szMsg, 255, "[%d]", i );
	m_pGUIDrawInterface->set_color( ColorC( 1, 1, 1, 1 ) );
	m_pGUIDrawInterface->draw_text( Vector2C( 10, 10 ), szMsg );

	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
*/


	m_pD3DDevice->EndScene();
}

LPDIRECT3DDEVICE8
DX8DeviceC::get_d3ddevice()
{
	return m_pD3DDevice;
}


bool
DX8DeviceC::set_fullscreen( uint32 ui32Width, uint32 ui32Height, uint32 ui32BPP )
{

	if( !m_rAdapters.size() )
		return false;

	OutputDebugString( "set_fullscreen()\n" );

	// Get access to current adapter, device, and mode
	DeviceS&	rDevice = m_rAdapters[m_dwCurAdapter].rDevices[m_rAdapters[m_dwCurAdapter].dwCurrentDevice];
	ModeS&		rMode = rDevice.rModes[rDevice.dwCurrentMode];


	// Find correct mode
	for( uint32 m = 0; m < rDevice.rModes.size(); m++ ) {
		if( rDevice.rModes[m].dwWidth >= ui32Width && rDevice.rModes[m].dwHeight >= ui32Height ) {
			rDevice.dwCurrentMode = m;
			if( ui32BPP == 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( ui32BPP == 24 ) {
				if( rDevice.rModes[m].rFormat == D3DFMT_R8G8B8 )
					break;
				if( rDevice.rModes[m].rFormat == D3DFMT_X8R8G8B8 )
					break;
			}
			if( ui32BPP == 32 ) {
				if( rDevice.rModes[m].rFormat == D3DFMT_A8R8G8B8 )
					break;
				if( rDevice.rModes[m].rFormat == D3DFMT_X8R8G8B8 )
					break;
			}
		}
	}

	m_bD3DReady = false;

	// Set fullscreen-mode presentation parameters

	m_rSavedD3Dpp = m_rD3Dpp;

	m_rD3Dpp.Windowed         = FALSE;
	m_rD3Dpp.hDeviceWindow    = m_hWnd;
	m_rD3Dpp.BackBufferWidth  = rDevice.rModes[m].dwWidth;
	m_rD3Dpp.BackBufferHeight = rDevice.rModes[m].dwHeight;
	m_rD3Dpp.BackBufferFormat = rDevice.rModes[m].rFormat;


	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 );

	// Call current demo shutdown function
	m_ui32State = DEVICE_STATE_LOST;
	if( m_pFeedback )
		m_pFeedback->send_init( INIT_DEVICE_INVALIDATE );
	m_pGUIDrawInterface->init_invalidate();
	if( m_dwSavedState )
		m_pD3DDevice->DeleteStateBlock( m_dwSavedState );
	m_dwSavedState = 0;


	// 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 );
		OutputDebugString( "DX8DeviceC::set_fullscreen:  reset failed.\n" );
		return false;
	}

	// Call current demo initialise function
	m_ui32State = DEVICE_STATE_OK;
	if( m_pFeedback )
		m_pFeedback->send_init( INIT_DEVICE_VALIDATE );
	m_pGUIDrawInterface->init_validate();
	m_pD3DDevice->CreateStateBlock( D3DSBT_ALL, &m_dwSavedState );

	m_bD3DReady = true;

	MoveWindow( m_hWnd, 0, 0, rDevice.rModes[m].dwWidth, rDevice.rModes[m].dwHeight, TRUE );
	UpdateWindow( m_hWnd );

	set_size( 0, 0, rDevice.rModes[m].dwWidth, rDevice.rModes[m].dwHeight );

	return true;
}

bool
DX8DeviceC::set_windowed()
{
	OutputDebugString( "set_windowed()\n" );

	m_bD3DReady = false;

	// 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;
	if( m_pFeedback )
		m_pFeedback->send_init( INIT_DEVICE_INVALIDATE );
	m_pGUIDrawInterface->init_invalidate();
	if( m_dwSavedState )
		m_pD3DDevice->DeleteStateBlock( m_dwSavedState );
	m_dwSavedState = 0;


	// 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 );
		OutputDebugString( "DX8DeviceC::set_windowed:  reset failed.\n" );
		return false;
	}

	// Call current demo initialise function
	m_ui32State = DEVICE_STATE_OK;
	if( m_pFeedback )
		m_pFeedback->send_init( INIT_DEVICE_VALIDATE );
	m_pGUIDrawInterface->init_validate();
	m_pD3DDevice->CreateStateBlock( D3DSBT_ALL, &m_dwSavedState );


	m_bD3DReady = true;

	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) );

	return true;
}
