//============================================================
// Program:		GameFrame
// Description:	Class for simplifying the DirectX setup process
// Author:		Benjamin Gottemoller
// Website:		http://www.particlefield.com
// Date:		4/20/01
// Legal:		Licensed under the gnu GPL (see gpl.txt for details)
//============================================================
//Link Files:	dxguid.lib dxerr8.lib dinput8.lib d3dx8dt.lib 
//				d3dx8.lib d3d8.lib d3dxof.lib winmm.lib 

#include "GameFrame.h"
#include "GFDebugHandler.h"

GameFrame::GameFrame(char *program_name, bool debug_detail):WindowFrame(program_name, debug_detail)
{
	D3DObject = NULL;
	D3DDevice = NULL;
	ZeroMemory( &D3DDisplayInfo, sizeof(D3DDisplayInfo) );
	DIObject = NULL;
	DIDeviceKeyboard = NULL;
	state_toggle = 0;
	ZeroMemory(&KEYBOARD, sizeof(KEYBOARD));
	ZeroMemory(&MOUSE, sizeof(MOUSE));
	SmoothState = 0;
	ZeroMemory(MouseXBuffer, sizeof(MouseXBuffer));
	ZeroMemory(MouseYBuffer, sizeof(MouseYBuffer));
	W2HRatio = 0;
	H2WRatio = 0;
	FPS = 0;
	IsLightingEnabled = 0;
	memset(&Settings, 0, sizeof(SETUP_DATA));
}

GameFrame::~GameFrame()
{
	if(state_toggle)
	{
		DestroyDirectX();	
		state_toggle = 0;
	}
}

void GameFrame::BeginDirectX(HINSTANCE hInst, char *config_file, char *name)
{
	int use_compression = 0;
	memset(&Settings, 0, sizeof(SETUP_DATA));

	if(state_toggle)
	{
		DestroyDirectX();	
		state_toggle = 0;
	}

	int depth = 0;
	FILE *fp = NULL;
	fp = fopen(config_file, "rt");
	if(fp != NULL)
	{
		fscanf(fp, "Adapter: %d\n", &(Settings.adapter));
		fscanf(fp, "Width: %d\n", &(Settings.width));
		fscanf(fp, "Height: %d\n", &(Settings.height));
		fscanf(fp, "Depth: %d, (D3DFORMAT: %d)\n", &depth, &(Settings.depth));
		fscanf(fp, "D3DDEVTYPE: %d\n", &(Settings.device_type));
		fscanf(fp, "Filter Type: %d\n", &(Settings.filter_type));
		fscanf(fp, "Enable Vertex Lighting: %d\n", &(Settings.is_vertex_lighting_enabled));
		fscanf(fp, "Mesh Geometry Detail: %d\n", &(Settings.mesh_geometry_detail));
		fscanf(fp, "Texture Map Detail: %d\n", &(Settings.texture_maps_detail));

		fclose(fp);
	}
	else
	{
		char path[MAX_PATH+256];

		#ifdef RUNNING_FROM_COMPILER
		sprintf(path, "Configuration Utility.exe");
		#else
		sprintf(path, "%s%s", LocalPath, "Configuration Utility.exe");
		#endif

		ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOW);
		exit(0);
	}

	BeginWindow(hInst, name, MsgProc, Settings.width, Settings.height);
	ShowWindow(WinHandle, SW_SHOWMAXIMIZED);
	ShowCursor(0);

	MSG msg;
	ZeroMemory(&msg, sizeof(msg));
	PostMessage(WinHandle, WM_PAINT, 0, 0);
	while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}


    D3DObject = Direct3DCreate8( D3D_SDK_VERSION );

    ZeroMemory( &D3DDisplayInfo, sizeof(D3DDisplayInfo) );
	D3DDisplayInfo.SwapEffect						= D3DSWAPEFFECT_FLIP;
	D3DDisplayInfo.Windowed							= FALSE;
	D3DDisplayInfo.BackBufferFormat					= Settings.depth;
	D3DDisplayInfo.BackBufferWidth					= Settings.width;
	D3DDisplayInfo.BackBufferHeight					= Settings.height;
	D3DDisplayInfo.BackBufferCount					= 1;
	D3DDisplayInfo.EnableAutoDepthStencil			= TRUE;
    D3DDisplayInfo.AutoDepthStencilFormat			= D3DFMT_D16;
	D3DDisplayInfo.FullScreen_RefreshRateInHz		= D3DPRESENT_RATE_DEFAULT;
	D3DDisplayInfo.FullScreen_PresentationInterval	= D3DPRESENT_INTERVAL_DEFAULT;
    
	if(D3D_OK == D3DObject->CheckDeviceFormat(Settings.adapter, Settings.device_type, 
	   Settings.depth, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT2))
	{
		Message("DXT2 explicit alpha texture compression support detected.");

		if(D3D_OK == D3DObject->CheckDeviceFormat(Settings.adapter, Settings.device_type, 
		   Settings.depth, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT4))
		{
			Message("DXT4 interpolated alpha texture compression support detected.");
			use_compression = 1;
		}
	}

	if(S_OK == D3DObject->CreateDevice(Settings.adapter, Settings.device_type, WinHandle,
											 D3DCREATE_HARDWARE_VERTEXPROCESSING,
											 &D3DDisplayInfo, &D3DDevice) )
	{
		Message("Device creation for D3DCREATE_HARDWARE_VERTEXPROCESSING successful.");
	}
	else
	{
		Confirm( S_OK == D3DObject->CreateDevice(Settings.adapter, Settings.device_type, WinHandle,
											     D3DCREATE_SOFTWARE_VERTEXPROCESSING,
											     &D3DDisplayInfo, &D3DDevice) );
	}

	ShowWindow(WinHandle, SW_SHOWMAXIMIZED);

	W2HRatio = (float)Width / (float)Height;
	H2WRatio = (float)Height / (float)Width;
	D3DXMatrixPerspectiveFovLH(&ProjectionMatrix, 60 * (D3DX_PI/180.0f), W2HRatio, 1.0f, 3.3E38f);
	D3DXMatrixOrthoLH(&OrthoProjectionMatrix, 100.0f, H2WRatio * 100.0f, 1.0f, 3.3E38f);
	Confirm( D3D_OK == D3DDevice->SetTransform( D3DTS_PROJECTION, &ProjectionMatrix ) );

    Confirm( D3D_OK == D3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ) );
    Confirm( D3D_OK == D3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE ) );
	Confirm( D3D_OK == D3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE) );
	Confirm( D3D_OK == D3DDevice->SetRenderState( D3DRS_SPECULARENABLE, FALSE) );
	
	if(Settings.filter_type == 1)
	{
		Confirm( D3D_OK == D3DDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR) );
		Confirm( D3D_OK == D3DDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR) );
		
		Confirm( D3D_OK == D3DDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR) );
	}
	else
	if(Settings.filter_type == 2)
	{
		Confirm( D3D_OK == D3DDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_ANISOTROPIC) );
		Confirm( D3D_OK == D3DDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_ANISOTROPIC) );
		
		Confirm( D3D_OK == D3DDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_ANISOTROPIC) );
	}
   
	Confirm( DI_OK == DirectInput8Create(WinInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, 
										 (void**)&DIObject, NULL) ); 
    Confirm( DI_OK == DIObject->CreateDevice(GUID_SysKeyboard, &DIDeviceKeyboard, NULL) ); 
    Confirm( DI_OK == DIDeviceKeyboard->SetDataFormat(&c_dfDIKeyboard) );
    Confirm( DI_OK == DIDeviceKeyboard->SetCooperativeLevel(WinHandle, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE | DISCL_NOWINKEY) ); 
    Confirm( DI_OK == DIDeviceKeyboard->Acquire() ); 

	Confirm( DI_OK == DIObject->CreateDevice(GUID_SysMouse, &DIDeviceMouse, NULL) ); 
    Confirm( DI_OK == DIDeviceMouse->SetDataFormat(&c_dfDIMouse) );
    Confirm( DI_OK == DIDeviceMouse->SetCooperativeLevel(WinHandle, DISCL_FOREGROUND | DISCL_EXCLUSIVE) ); 
    Confirm( DI_OK == DIDeviceMouse->Acquire() ); 

	IsLightingEnabled = Settings.is_vertex_lighting_enabled;
	MeshDetailLevel = Settings.mesh_geometry_detail;
	TextureDetailLevel = Settings.texture_maps_detail;

	Confirm( VertexLib.Initialize(this, 50) );
	Confirm( TextureLib.Initialize(this, 50) );
	Confirm( MaterialLib.Initialize(this, 50) );
	Confirm( ObjectLib.Initialize(this, 50) );
	Confirm( FontLib.Initialize(this, 50) );
	
	Confirm( ParticleSystem.Initialize(this, 50, 10000) );

	if(use_compression)
	{
		TextureLib.EnableTextureCompression(1);
	}

	HFONT font = CreateFont(12, 12, 0, 0, FW_MEDIUM, 0, 0, 0, ANSI_CHARSET, 
							OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 
							FIXED_PITCH, "Verdana"); 
	FontLib.AddFont(font, 12, 12);

	state_toggle = 1;
}

void GameFrame::DestroyDirectX()
{
	if(state_toggle)
	{
		VertexLib.Destroy();
		TextureLib.Destroy();
		MaterialLib.Destroy();
		ObjectLib.Destroy();
		FontLib.Destroy();
		ParticleSystem.Destroy();
		Sound.DestroyDirectSound();

		if(DIObject != NULL)
		{
			if(DIDeviceKeyboard != NULL)
			{
				DIDeviceKeyboard->Unacquire(); 
				DIDeviceKeyboard->Release();
				DIDeviceKeyboard = NULL; 
			}

			if(DIDeviceMouse != NULL)
			{
				DIDeviceMouse->Unacquire();
				DIDeviceMouse->Release();
				DIDeviceMouse = NULL;
			}
			DIObject->Release();
			DIObject = NULL;
		}
  
		if( D3DObject != NULL )
		{
			if( D3DDevice != NULL )
			{
				D3DDevice->Release();
				D3DDevice = NULL;
			}
			D3DObject->Release();
			D3DObject = NULL;
		}
	
		DestroyWindow();
		state_toggle = 0;
	}
}

void GameFrame::Present(void)
{
	HRESULT hr = 0;
    hr = D3DDevice->Present(NULL, NULL, NULL, NULL);

	if(hr == D3DERR_DEVICELOST)
	{
		IsGameLocked = 1;
		TestRestore();
	}
	else
	{
		IsGameLocked = 0;
	}
}

void GameFrame::TakeScreenShot(char* file)
{
	char FullPath[MAX_PATH + 256];
	IDirect3DSurface8* FrontBuffer; 

	D3DDevice->CreateImageSurface(Width, Height, D3DFMT_A8R8G8B8, &FrontBuffer);

	HRESULT hr = D3DDevice->GetFrontBuffer(FrontBuffer);

	if(hr != D3D_OK)
	{
		FrontBuffer->Release(); 
		return;
	}

	#ifdef RUNNING_FROM_COMPILER
	sprintf(FullPath, "%s..\\Screen Shots\\%s", LocalPath, file);
	#else
	sprintf(FullPath, "%sScreen Shots\\%s", LocalPath, file);
	#endif

	D3DXSaveSurfaceToFile(FullPath, D3DXIFF_BMP, FrontBuffer, NULL, NULL);
	FrontBuffer->Release();
}

void GameFrame::TestRestore(void)
{
	MSG msg;
	ZeroMemory(&msg, sizeof(MSG));
	HRESULT hr = D3DDevice->TestCooperativeLevel();

	if(hr == D3DERR_DEVICELOST)
	{
		FontLib.OnLostDevice();

		WaitMessage();

		while(hr != D3D_OK)
		{
			if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				DispatchMessage(&msg);
				hr = D3DDevice->TestCooperativeLevel();
				if(hr == D3DERR_DEVICENOTRESET)
				{
					D3DDevice->Reset(&D3DDisplayInfo);
				}
			}
		}

		D3DDevice->SetTransform(D3DTS_PROJECTION, &ProjectionMatrix);

		D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
		D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
		D3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
		D3DDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
	
		if(Settings.filter_type == 1)
		{
			D3DDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
			D3DDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
			D3DDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
		}
		else
		if(Settings.filter_type == 2)
		{
			D3DDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_ANISOTROPIC);
			D3DDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_ANISOTROPIC);
			D3DDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_ANISOTROPIC);
		}
   
		FontLib.OnResetDevice();
	}
}

float GameFrame::GetTime(void)
{
	__int64 tmp = 0;
	QueryPerformanceCounter((LARGE_INTEGER*)&tmp);
	return (float)((double)tmp / (double)counter_frequency);
}

void GameFrame::UpdateKeyInfo(void)
{
    if(DI_OK != DIDeviceKeyboard->GetDeviceState(sizeof(KEYBOARD),(LPVOID)&KEYBOARD))
	{
		DIDeviceKeyboard->Acquire();
	}
}

int GameFrame::KEYDOWN(UCHAR key)
{
	if(key < 0) return 0;
	return (KEYBOARD[key] & 0x80);
}

void GameFrame::SetMouseSmoothing(int state)
{
	SmoothState = state;
}

void GameFrame::UpdateMouseInfo(void)
{
	if(DI_OK != DIDeviceMouse->GetDeviceState(sizeof(MOUSE),(LPVOID)&MOUSE))
	{
		DIDeviceMouse->Acquire();
	}
}

float GameFrame::MOUSEMESSAGE(UINT type)
{
	float RetValue = 0;
	static int x_index = 0, y_index = 0; 

	if(type == 0) return 0;

	if(type & MOUSE_X)
	{
		RetValue = (float) MOUSE.lX;

		if(SmoothState)
		{
			MouseXBuffer[x_index] = RetValue;
			x_index++;
			if(x_index >= MOUSE_BUFFER_SIZE) x_index = 0;
		}
	}
	else
	if(type & MOUSE_Y)
	{
		RetValue = (float) MOUSE.lY;

		if(SmoothState)
		{
			MouseYBuffer[y_index] = RetValue;
			y_index++;
			if(y_index >= MOUSE_BUFFER_SIZE) y_index = 0;
		}
	}
	else
	if(type & MOUSE_Z)
	{
		return (float)(MOUSE.lZ / 120);
	}
	else
	if(type & MOUSE_LBUTTON)
	{
		if(MOUSE.rgbButtons[0] & 0x80) return 1;
	}
	else
	if(type & MOUSE_RBUTTON)
	{
		if(MOUSE.rgbButtons[1] & 0x80) return 1;
	}
	else
	if(type & MOUSE_BUTTON3)
	{
		if(MOUSE.rgbButtons[2] & 0x80) return 1;
	}
	else
	if(type & MOUSE_BUTTON4)
	{
		if(MOUSE.rgbButtons[3] & 0x80) return 1;
	}

	if(SmoothState)
	{
		if(type & MOUSE_X)
		{
			RetValue = 0;
			for(int i=0; i<MOUSE_BUFFER_SIZE; i++)
			{
				RetValue += MouseXBuffer[i];
			}
			RetValue /= MOUSE_BUFFER_SIZE;
		}
		else
		if(type & MOUSE_Y)
		{
			RetValue = 0;
			for(int i=0; i<MOUSE_BUFFER_SIZE; i++)
			{
				RetValue += MouseYBuffer[i];
			}
			RetValue /= MOUSE_BUFFER_SIZE;
		}
	}
	
	return RetValue;
}