//---------------------------------------------------------------------------------------
//
// Simple Windows DirectInput Example Code
//
// Jim Shaw 15/3/2004
//
//---------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------
// Always include windows.h for windows programs
//---------------------------------------------------------------------------------------
#include <windows.h>

#define DIRECTINPUT_VERSION 0x800
#include <dinput.h>

#include <stdio.h>

//---------------------------------------------------------------------------------------
// Window dimensions
//---------------------------------------------------------------------------------------
#define SCREEN_X   640
#define SCREEN_Y   480

//---------------------------------------------------------------------------------------
// Variable to hold the address of the Windows window
//---------------------------------------------------------------------------------------
HWND game_window;

//---------------------------------------------------------------------------------------
// Name of our window class.  every class has a name.  every window is one of a class.
// this can be pretty much anything
//---------------------------------------------------------------------------------------
char game_class[]="DICPPWindow";
static int quit = 0;

//---------------------------------------------------------------------------------------
// Function prototypes
//---------------------------------------------------------------------------------------
int init_dinput(HINSTANCE);
void shutdown_dinput(void);
void show_dinput_status(HDC hdc);

//---------------------------------------------------------------------------------------
// DirectInput Variables
//---------------------------------------------------------------------------------------
IDirectInput8 *dinput;
IDirectInputDevice8 *keyboard;
IDirectInputDevice8 *mouse;
IDirectInputDevice8 *joystick;

unsigned char keymap[256];
DIMOUSESTATE2 mousedata;
DIJOYSTATE2 joydata;

//---------------------------------------------------------------------------------------
// Every window has a WindowProc which handles messages Windows sends to them.  These
// are things like key presses, mouse clicks, and thousands of other things you might
// be interested in.
//---------------------------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		//this message gets sent to the window when you click the close box on it
		case WM_DESTROY:
			//tell Windows to close the app
			PostQuitMessage(0);
			quit = 1;
			break;

		case WM_PAINT:
			{
			PAINTSTRUCT ps;
			BeginPaint(hwnd, &ps);
			show_dinput_status(ps.hdc);
			EndPaint(hwnd, &ps);
			}
			break;

		case WM_TIMER:
			InvalidateRect(hwnd, 0, TRUE);
			break;

		//this message gets sent when you press a key
		case WM_KEYDOWN:
			switch (wParam)
			{
				//pressed Escape key?
				case VK_ESCAPE:
					//tell Windows to close the app
					PostQuitMessage(0);
					quit = 1;
					break;
			}
			break;

		//any message you don't process gets passed to this function inside Windows.
		default:
			return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
	//any message you do process, return 0 (unless the message documentation says otherwise)
return 0;
}

//---------------------------------------------------------------------------------------
// WinMain is like main() in C programs, just for Windows apps.
//---------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX clas;
	MSG msg;
	int style;
	RECT rect;

	//Here we create the Class we named above
	clas.cbSize = sizeof(WNDCLASSEX);
	clas.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	clas.lpfnWndProc = WindowProc;//<- tell it where the WindowProc is
	clas.cbClsExtra = 0;
	clas.cbWndExtra = 0;
	clas.hInstance = hInstance;
	clas.hIcon = NULL;
	clas.hCursor = NULL;
	clas.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);//<- background colour of window
	clas.lpszMenuName = NULL;
	clas.lpszClassName = game_class;//<- the class name
	clas.hIconSm = 0;
	//do it!
	RegisterClassEx(&clas);

	//style of the window - what boxes do we need (close minimised etc)
	style = WS_CAPTION|WS_SYSMENU|WS_MAXIMIZEBOX|WS_MINIMIZEBOX;
	//create the window
	game_window = CreateWindowEx(0, game_class, "DirectInput", style, CW_USEDEFAULT, CW_USEDEFAULT, 1,1, NULL, NULL, hInstance, 0);

	//adjust the window size so that a SCREEN_X x SCREEN_Y window will fit inside its frame
	rect.left = rect.top = 0;
	rect.right = SCREEN_X;
	rect.bottom = SCREEN_Y;
	AdjustWindowRectEx(&rect, style , FALSE, 0);
	SetWindowPos(game_window, NULL, 0,0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE|SWP_NOZORDER);

	//show the window on the desktop
	ShowWindow(game_window, nCmdShow);

	if (init_dinput(hInstance) == 0)
	{
		MessageBox(NULL, "A problem occurred creating DirectInput.\nCheck your DirectX version.\n"
							"This demo requires DirectX9.0 or better.", "DirectX Initialisation Error", MB_ICONWARNING|MB_OK);
		shutdown_dinput();
		return 0;
	}

	// Create a timer to refresh the window
	SetTimer(game_window, 1, 1000/30, NULL);

	//message processing loop
	//all Windows programs have one of these.  It receives the messages Windows sends to the program and
	//passes them to the WindowProc in the Class we registered for the window.
	quit = 0;
	do
	{
		//Are there any messages waiting?
		while (GetMessage(&msg, game_window, 0,0) > 0 && !quit)
		{

			//pass the message to WindowProc
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

	} while (!quit);

	// Close everything down
	KillTimer(game_window, 1);

	shutdown_dinput();

	return 0;
}

//---------------------------------------------------------------------------------------
// Callback to look at DirectInput devices to see if we're interested in them
//---------------------------------------------------------------------------------------
BOOL CALLBACK dinput_enum_callback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
{
	HRESULT err;

	err = dinput->CreateDevice(lpddi->guidInstance, &joystick, NULL);
	if (err != DI_OK)
		return DIENUM_STOP;
	err = joystick->SetDataFormat(&c_dfDIJoystick2);
	if (err != DI_OK)
	{
		joystick->Release();
		joystick = NULL;
		return DIENUM_STOP;
	}
	err = joystick->SetCooperativeLevel(game_window, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
	err = joystick->Acquire();

	return DIENUM_STOP;
}

//---------------------------------------------------------------------------------------
// Create the DirectInput interface and access mouse, keyboard and joystick
//---------------------------------------------------------------------------------------
int init_dinput(HINSTANCE hInst)
{
	HRESULT err;

	// Create the DirectInput interface
	err = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, NULL);
	if (err != DI_OK)
		return 0;

	// Create the Keyboard device
	err = dinput->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);
	err = keyboard->SetDataFormat(&c_dfDIKeyboard);
	err = keyboard->SetCooperativeLevel(game_window, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
	err = keyboard->Acquire();

	// Create the mouse device
	err = dinput->CreateDevice(GUID_SysMouse, &mouse, NULL);
	err = mouse->SetDataFormat(&c_dfDIMouse2);
	err = mouse->SetCooperativeLevel(game_window, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
	err = mouse->Acquire();

	// Check for a joystick device, and create it if one exists
	err = dinput->EnumDevices(DI8DEVCLASS_GAMECTRL, dinput_enum_callback, 0, DIEDFL_ALLDEVICES);

	return 1;
}

//---------------------------------------------------------------------------------------
// Shutdown DirectInput
//---------------------------------------------------------------------------------------
void shutdown_dinput(void)
{
	if (joystick)
	{
		joystick->Unacquire();
		joystick->Release();
	}
	if (mouse)
	{
		mouse->Unacquire();
		mouse->Release();
	}
	if (keyboard)
	{
		keyboard->Unacquire();
		keyboard->Release();
	}
	if (dinput) dinput->Release();
}

//---------------------------------------------------------------------------------------
// Read data back from the controllers
//---------------------------------------------------------------------------------------
void read_dinput_status(void)
{
	HRESULT err;

	// Read the keyboard
	err = keyboard->Poll();
	err = keyboard->GetDeviceState(256, keymap);

	// Read the mouse
	err = mouse->Poll();
	err = mouse->GetDeviceState(sizeof mousedata, &mousedata);

	// Read the joystick
	if (joystick)
	{
		err = joystick->Poll();
		err = joystick->GetDeviceState(sizeof joydata, &joydata);
	}
}

//---------------------------------------------------------------------------------------
// Display current device inputs
//---------------------------------------------------------------------------------------
void show_dinput_status(HDC hdc)
{
	int x, y;
	char text[256];

	read_dinput_status();

	// Keymap status
	TextOut(hdc, 0,0, "Keyboard", 8);
	for (y = 0; y < 16; y++)
	{
		for (x = 0; x < 16; x++)
		{
			if (keymap[x+16*y])
				TextOut(hdc, x*10, (y+1)*16, "1", 1);
			else
				TextOut(hdc, x*10, (y+1)*16, "0", 1);

		}
	}

	// Mouse status
	TextOut(hdc, 260,0, "Mouse", 5);
	sprintf(text, "XYZ %d,%d,%d", mousedata.lX, mousedata.lY, mousedata.lZ);
	TextOut(hdc, 260, 16, text, strlen(text));
	for (x = 0; x < 8; x++)
	{
		if (mousedata.rgbButtons[x])
			TextOut(hdc, 260+x*10,32, "1", 1);
		else
			TextOut(hdc, 260+x*10,32, "0", 1);
	}

	//Joystick status
	TextOut(hdc, 400, 0, "Joystick", 8);
	if (joystick)
	{
		sprintf(text, "XYZ %d,%d,%d", joydata.lX, joydata.lY, joydata.lZ);
		TextOut(hdc, 400, 16, text, strlen(text));
	}
	else
	{
		TextOut(hdc, 400, 16, "Unattached", 10);
	}
}
