//////////////////////////////////////////////////////////////////////////////////////////////////
// 
// File: d3dUtility.cpp
// 
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 
//
// Desc: Provides utility functions for simplifying common tasks.
//          
//////////////////////////////////////////////////////////////////////////////////////////////////

#include "d3dUtility.h"

namespace d3d
{
	bool InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device, D3DPRESENT_PARAMETERS* params, HWND* oHwnd)
	{
		//
		// Create the main application window.
		//

		WNDCLASSEX wc;

		wc.cbSize			= sizeof(wc);
		wc.style			= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
		wc.lpfnWndProc		= (WNDPROC)d3d::WndProc; 
		wc.cbClsExtra		= 0;
		wc.cbWndExtra		= 0;
		wc.hInstance		= hInstance;
		wc.hIcon			= LoadIcon(0, IDI_APPLICATION);
		wc.hCursor			= LoadCursor(0, IDC_ARROW);
		wc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
		wc.lpszMenuName		= 0;
		wc.lpszClassName	= "MainAppWindow";
		wc.hIconSm			= 0;

		if( !RegisterClassEx(&wc) ) 
		{
			::MessageBox(0, "RegisterClass() - FAILED", 0, 0);
			return false;
		}
			
		HWND hwnd = 0;
		if (!windowed)
			hwnd = ::CreateWindowEx(WS_EX_APPWINDOW, "MainAppWindow", "Test", WS_POPUP, 0, 0, width, height, 0, 0, hInstance, 0); 
		else
			hwnd = ::CreateWindowEx(WS_EX_APPWINDOW, "MainAppWindow", "Test", WS_OVERLAPPEDWINDOW, 0, 0, width, height, 0, 0, hInstance, 0); 

		if( !hwnd )
		{
			::MessageBox(0, "CreateWindow() - FAILED", 0, 0);
			return false;
		}

		::ShowWindow(hwnd, SW_SHOW);
		::UpdateWindow(hwnd);

		//
		// Init D3D: 
		//

		HRESULT hr = 0;

		// Step 1: Create the IDirect3D9 object.

		IDirect3D9* d3d9 = 0;
		d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

		if( !d3d9 )
		{
			::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0);
			return false;
		}

		// Step 2: Check for hardware vp.

		D3DCAPS9 caps;
		d3d9->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.
	 
		D3DPRESENT_PARAMETERS d3dpp, paramsUsed;
		d3dpp.BackBufferWidth            = width;
		d3dpp.BackBufferHeight           = height;
		d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;
		d3dpp.BackBufferCount            = 1;
		d3dpp.MultiSampleType            = D3DMULTISAMPLE_NONE;//D3DMULTISAMPLE_4_SAMPLES;
		d3dpp.MultiSampleQuality         = 0;
		d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD; 
		d3dpp.hDeviceWindow              = hwnd;
		d3dpp.Windowed                   = windowed;
		d3dpp.EnableAutoDepthStencil     = true; 
		d3dpp.AutoDepthStencilFormat     = D3DFMT_D32;
		d3dpp.Flags                      = 0;
		d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
		d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;//D3DPRESENT_INTERVAL_IMMEDIATE;

		// Step 4: Create the device.

		paramsUsed = d3dpp;
		hr = d3d9->CreateDevice(
			D3DADAPTER_DEFAULT, // primary adapter
			deviceType,         // device type
			hwnd,               // window associated with device
			vp,                 // vertex processing
			&d3dpp,             // present parameters
			device);            // return created device

		if( FAILED(hr) )
		{
			// try again using a 24/8-bit depth buffer
			d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
			
			paramsUsed = d3dpp;
			hr = d3d9->CreateDevice(
				D3DADAPTER_DEFAULT,
				deviceType,
				hwnd,
				vp,
				&d3dpp,
				device);

			if( FAILED(hr) )
			{
				d3d9->Release(); // done with d3d9 object
				::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
				::PostQuitMessage(1);
				return false;
			}
		}

		d3d9->Release(); // done with d3d9 object

		if (!windowed)
		{
			RECT clipCursorRect;
			clipCursorRect.left = 0;
			clipCursorRect.top = 0;
			clipCursorRect.right = width;
			clipCursorRect.bottom = height;
			ClipCursor(&clipCursorRect);
		}
		
		if (oHwnd != 0)
			*oHwnd = hwnd;

		if (params != 0)
			*params = paramsUsed;
		
		return true;
	}

	int EnterMsgLoop( bool (*ptr_display)(float timeDelta) )
	{
		MSG msg;
		::ZeroMemory(&msg, sizeof(MSG));

		static DWORD lastTime = timeGetTime(); 

		while(msg.message != WM_QUIT)
		{
			if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
			{
				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}

			DWORD currTime  = timeGetTime();
			float timeDelta = float(currTime - lastTime)*0.001f;

			ptr_display(timeDelta);

			lastTime = currTime;
		}
		return int(msg.wParam);
	}

	float Lerp(float a, float b, float t)
	{
		return a - ((a - b) * t);
	}
}