#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <malloc.h>

const char g_szClassName[] = "myWindowClass";

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	DWORD datasize;
	RAWINPUT *raw;
	UINT r;

	switch(msg)
	{
		case WM_CLOSE:
			DestroyWindow(hwnd);
		break;
		case WM_DESTROY:
			PostQuitMessage(0);
		break;
		case WM_INPUT:
			r = GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &datasize, sizeof(RAWINPUTHEADER));
			assert(r == 0);
			raw = _malloca(datasize);
			r = GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw, &datasize, sizeof(RAWINPUTHEADER));
			assert(r != -1);
			if (raw->header.dwType == RIM_TYPEKEYBOARD)
			{
				int vcode = raw->data.keyboard.VKey;
				int repeat;
				int down = !((raw->data.keyboard.Flags) & 1);
				int extended = (raw->data.keyboard.Flags & 2) != 0;
				int scancode = raw->data.keyboard.MakeCode;// & 0xff;
				char name[50] = { '\0' };

				if (vcode == VK_CONTROL)
					vcode = (extended ? VK_RCONTROL : VK_LCONTROL);
				else if (vcode == VK_MENU)
					vcode = (extended ? VK_RMENU : VK_LMENU);
				else if (vcode == VK_SHIFT)
					vcode = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);

				repeat = (GetKeyState(vcode) >> 15) & 1;
				GetKeyNameText((scancode << 16) | (extended << 24), name, sizeof(name));
				printf("%s %s Flags=%04hx SC=%08x VK=%08x\n", name, !down ? "UP    " : repeat ? "REPEAT" : "DOWN  ",
				       raw->data.keyboard.Flags, scancode, vcode);
				//printf("MapVirtualKey returned %08x\n", MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX));
			}
			else
			{
				assert(0);
			}
			_freea(raw);
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wc;
	HWND hwnd;
	MSG Msg;
	RAWINPUTDEVICE rid;

	//Step 1: Registering the Window Class
	wc.cbSize		 = sizeof(WNDCLASSEX);
	wc.style		 = 0;
	wc.lpfnWndProc	 = WndProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName	 = NULL;
	wc.lpszClassName = g_szClassName;
	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION);

	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, "Window Registration Failed!", "Error!",
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	// Step 2: Creating the Window
	hwnd = CreateWindowEx(
		WS_EX_CLIENTEDGE,
		g_szClassName,
		"Raw input test",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, 300, 300,
		NULL, NULL, hInstance, NULL);

	if(hwnd == NULL)
	{
		MessageBox(NULL, "Window Creation Failed!", "Error!",
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	// Set up raw input for the keyboard	
	rid.usUsagePage = 0x01;
	rid.usUsage = 0x06;            // HID keyboard
	rid.dwFlags = 0;//RIDEV_NOLEGACY;  // ignore legacy keyboard messages
	rid.hwndTarget = NULL;
	if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
		printf("RegisterRawInputDevices failed!\n");

	// Step 3: The Message Loop
	while(GetMessage(&Msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return Msg.wParam;
}
