/* Copyright (C) 2006-2017 Alexander Barker.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
*/

#ifdef HAVE_CONFIG_H
	#include <config.h>
#endif

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>

#include "../iohook.h"
#include "../logger_c.h"
#include "input.h"

static const uint16_t keycode_scancode_table[][2] = {
	/* idx		{ vk_code,				scancode				}, */
	/*   0 */	{ VC_UNDEFINED,			0x0000					},	// 0x00
	/*   1 */	{ MOUSE_BUTTON1,		VK_ESCAPE				},	// 0x01
	/*   2 */	{ MOUSE_BUTTON2,		0x0031					},	// 0x02
	/*   3 */	{ VC_UNDEFINED,			0x0032					},	// 0x03 VK_CANCEL
	/*   4 */	{ MOUSE_BUTTON3,		0x0033					},	// 0x04
	/*   5 */	{ MOUSE_BUTTON4,		0x0034					},	// 0x05
	/*   6 */	{ MOUSE_BUTTON5,		0x0035					},	// 0x06
	/*   7 */	{ VC_UNDEFINED,			0x0036					},	// 0x07							Undefined
	/*   8 */	{ VC_BACKSPACE,			0x0037					},	// 0x08 VK_BACK
	/*   9 */	{ VC_TAB,				0x0038					},	// 0x09 VK_TAB
	/*  10 */	{ VC_UNDEFINED,			0x0039					},	// 0x0A							Reserved
	/*  11 */	{ VC_UNDEFINED,			0x0030					},	// 0x0B							Reserved
	/*  12 */	{ VC_CLEAR,				VK_OEM_MINUS			},	// 0x0C VK_CLEAR
	/*  13 */	{ VC_ENTER,				VK_OEM_PLUS				},	// 0x0D VK_RETURN
	/*  14 */	{ VC_UNDEFINED,			VK_BACK					},	// 0x0E							Undefined
	/*  15 */	{ VC_UNDEFINED,			VK_TAB					},	// 0x0F							Undefined
	/*  16 */	{ VC_SHIFT_L,			0x0051					},	// 0x10 VK_SHIFT
	/*  17 */	{ VC_CONTROL_L,			0x0057					},	// 0x11 VK_CONTROL
	/*  18 */	{ VC_ALT_L,				0x0045					},	// 0x12 VK_MENU					ALT key
	/*  19 */	{ VC_PAUSE,				0x0052					},	// 0x13 VK_PAUSE
	/*  20 */	{ VC_CAPS_LOCK,			0x0054					},	// 0x14 VK_CAPITAL				CAPS LOCK key
	/*  21 */	{ VC_KATAKANA,			0x0059					},	// 0x15 VK_KANA					IME Kana mode
	/*  22 */	{ VC_UNDEFINED,			0x0055					},	// 0x16							Undefined
	/*  23 */	{ VC_UNDEFINED,			0x0049					},	// 0x17 VK_JUNJA				IME Junja mode
	/*  24 */	{ VC_UNDEFINED,			0x004F					},	// 0x18 VK_FINAL
	/*  25 */	{ VC_KANJI,				0x0050					},	// 0x19 VK_KANJI / VK_HANJA		IME Kanji / Hanja mode
	/*  26 */	{ VC_UNDEFINED,			0x00DB					},	// 0x1A Undefined
	/*  27 */	{ VC_ESCAPE,			0x00DD					},	// 0x1B	VK_ESCAPE				ESC key
	/*  28 */	{ VC_UNDEFINED,			VK_RETURN				},	// 0x1C VK_CONVERT				IME convert// 0x1C
	/*  29 */	{ VC_UNDEFINED,			VK_LCONTROL				},	// 0x1D VK_NONCONVERT			IME nonconvert
	/*  30 */	{ VC_UNDEFINED,			0x0041					},	// 0x1E VK_ACCEPT				IME accept
	/*  31 */	{ VC_UNDEFINED,			0x0053					},	// 0x1F VK_MODECHANGE			IME mode change request
	/*  32 */	{ VC_SPACE,				0x0044					},	// 0x20 VK_SPACE				SPACEBAR
	/*  33 */	{ VC_PAGE_UP,			0x0046					},	// 0x21 VK_PRIOR				PAGE UP key
	/*  34 */	{ VC_PAGE_DOWN,			0x0047					},	// 0x22 VK_NEXT					PAGE DOWN key
	/*  35 */	{ VC_END,				0x0048					},	// 0x23 VK_END					END key
	/*  36 */	{ VC_HOME,				0x004A					},	// 0x24 VK_HOME					HOME key
	/*  37 */	{ VC_LEFT,				0x004B					},	// 0x25 VK_LEFT					LEFT ARROW key
	/*  38 */	{ VC_UP,				0x004C					},	// 0x26 VK_UP					UP ARROW key
	/*  39 */	{ VC_RIGHT,				VK_OEM_1				},	// 0x27 VK_RIGHT				RIGHT ARROW key
	/*  40 */	{ VC_DOWN,				VK_OEM_7				},	// 0x28 VK_DOWN					DOWN ARROW key
	/*  41 */	{ VC_UNDEFINED,			VK_OEM_3				},	// 0x29 VK_SELECT				SELECT key
	/*  42 */	{ VC_UNDEFINED,			VK_LSHIFT				},	// 0x2A VK_PRINT				PRINT key
	/*  43 */	{ VC_UNDEFINED,			VK_OEM_5				},	// 0x2B VK_EXECUTE				EXECUTE key
	/*  44 */	{ VC_PRINTSCREEN,		0x005A					},	// 0x2C VK_SNAPSHOT				PRINT SCREEN key
	/*  45 */	{ VC_INSERT,			0x0058					},	// 0x2D VK_INSERT				INS key
	/*  46 */	{ VC_DELETE,			0x0043					},	// 0x2E VK_DELETE				DEL key
	/*  47 */	{ VC_UNDEFINED,			0x0056					},	// 0x2F VK_HELP					HELP key
	/*  48 */	{ VC_0,					0x0042					},	// 0x30							0 key
	/*  49 */	{ VC_1,					0x004E					},	// 0x31							1 key
	/*  50 */	{ VC_2,					0x004D					},	// 0x32							2 key
	/*  51 */	{ VC_3,					VK_OEM_COMMA			},	// 0x33							3 key
	/*  52 */	{ VC_4,					VK_OEM_PERIOD			},	// 0x34							4 key
	/*  53 */	{ VC_5,					VK_OEM_2				},	// 0x35							5 key
	/*  54 */	{ VC_6,					VK_RSHIFT				},	// 0x36							6 key
	/*  55 */	{ VC_7,					VK_MULTIPLY				},	// 0x37							7 key
	/*  56 */	{ VC_8,					VK_LMENU				},	// 0x38							8 key
	/*  57 */	{ VC_9,					VK_SPACE				},	// 0x39							9 key
	/*  58 */	{ VC_UNDEFINED,			VK_CAPITAL				},	// 0x3A							Undefined
	/*  59 */	{ VC_UNDEFINED,			VK_F1					},	// 0x3B							Undefined
	/*  60 */	{ VC_UNDEFINED,			VK_F2					},	// 0x3C							Undefined
	/*  61 */	{ VC_UNDEFINED,			VK_F3					},	// 0x3D							Undefined
	/*  62 */	{ VC_UNDEFINED,			VK_F4					},	// 0x3E							Undefined
	/*  63 */	{ VC_UNDEFINED,			VK_F5					},	// 0x3F							Undefined
	/*  64 */	{ VC_UNDEFINED,			VK_F6					},	// 0x40							Undefined
	/*  65 */	{ VC_A,					VK_F7					},	// 0x41							A key
	/*  66 */	{ VC_B,					VK_F8					},	// 0x42							B key
	/*  67 */	{ VC_C,					VK_F9					},	// 0x43							C key
	/*  68 */	{ VC_D,					VK_F10					},	// 0x44							D key
	/*  69 */	{ VC_E,					VK_NUMLOCK				},	// 0x45							E key
	/*  70 */	{ VC_F,					VK_SCROLL				},	// 0x46							F key
	/*  71 */	{ VC_G,					VK_NUMPAD7				},	// 0x47							G key
	/*  72 */	{ VC_H,					VK_NUMPAD8				},	// 0x48							H key
	/*  73 */	{ VC_I,					VK_NUMPAD9				},	// 0x49							I key
	/*  74 */	{ VC_J,					VK_SUBTRACT				},	// 0x4A							J key
	/*  75 */	{ VC_K,					VK_NUMPAD4				},	// 0x4B							K key
	/*  76 */	{ VC_L,					VK_NUMPAD5				},	// 0x4C							L key
	/*  77 */	{ VC_M,					VK_NUMPAD6				},	// 0x4D							M key
	/*  78 */	{ VC_N,					VK_ADD					},	// 0x4E							N key
	/*  79 */	{ VC_O,					VK_NUMPAD1				},	// 0x4F							O key
	/*  80 */	{ VC_P,					VK_NUMPAD2				},	// 0x50							P key
	/*  81 */	{ VC_Q,					VK_NUMPAD3				},	// 0x51							Q key
	/*  82 */	{ VC_R,					VK_NUMPAD0				},	// 0x52							R key
	/*  83 */	{ VC_S,					VK_DECIMAL				},	// 0x53							S key
	/*  84 */	{ VC_T,					0x0000					},	// 0x54							T key
	/*  85 */	{ VC_U,					0x0000					},	// 0x55							U key
	/*  86 */	{ VC_V,					0x0000					},	// 0x56							V key
	/*  87 */	{ VC_W,					VK_F11					},	// 0x57							W key
	/*  88 */	{ VC_X,					VK_F12					},	// 0x58							X key
	/*  89 */	{ VC_Y,					0x0000					},	// 0x59							Y key
	/*  90 */	{ VC_Z,					0x0000					},	// 0x5A							Z key
	/*  91 */	{ VC_META_L,			VK_F13					},	// 0x5B VK_LWIN 				Left Windows key (Natural keyboard)
	/*  92 */	{ VC_META_R,			VK_F14					},	// 0x5C VK_RWIN					Right Windows key (Natural keyboard)
	/*  93 */	{ VC_CONTEXT_MENU,		VK_F15					},	// 0x5D VK_APPS					Applications key (Natural keyboard)
	/*  94 */	{ VC_UNDEFINED,			0x0000					},	// 0x5E Reserved
	/*  95 */	{ VC_SLEEP,				0x0000					},	// 0x5F VK_SLEEP				Computer Sleep key
	/*  96 */	{ VC_KP_0,				0x0000					},	// 0x60 VK_NUMPAD0				Numeric keypad 0 key
	/*  97 */	{ VC_KP_1,				0x0000					},	// 0x61 VK_NUMPAD1				Numeric keypad 1 key
	/*  98 */	{ VC_KP_2,				0x0000					},	// 0x62 VK_NUMPAD2				Numeric keypad 2 key
	/*  99 */	{ VC_KP_3,				VK_F16					},	// 0x63 VK_NUMPAD3				Numeric keypad 3 key
	/* 100 */	{ VC_KP_4,				VK_F17					},	// 0x64 VK_NUMPAD4				Numeric keypad 4 key
	/* 101 */	{ VC_KP_5,				VK_F18					},	// 0x65 VK_NUMPAD5				Numeric keypad 5 key
	/* 102 */	{ VC_KP_6,				VK_F19					},	// 0x66 VK_NUMPAD6				Numeric keypad 6 key
	/* 103 */	{ VC_KP_7,				VK_F20					},	// 0x67 VK_NUMPAD7				Numeric keypad 7 key
	/* 104 */	{ VC_KP_8,				VK_F21					},	// 0x68 VK_NUMPAD8				Numeric keypad 8 key
	/* 105 */	{ VC_KP_9,				VK_F22					},	// 0x69 VK_NUMPAD9				Numeric keypad 9 key
	/* 106 */	{ VC_KP_MULTIPLY,		VK_F23					},	// 0x6A VK_MULTIPLY				Multiply key
	/* 107 */	{ VC_KP_ADD,			VK_F24					},	// 0x6B VK_ADD					Add key
	/* 108 */	{ VC_UNDEFINED,			0x0000					},	// 0x6C VK_SEPARATOR			Separator key
	/* 109 */	{ VC_KP_SUBTRACT,		0x0000					},	// 0x6D VK_SUBTRACT				Subtract key
	/* 110 */	{ VC_KP_SEPARATOR,		0x0000					},	// 0x6E VK_DECIMAL				Decimal key
	/* 111 */	{ VC_KP_DIVIDE,			0x0000					},	// 0x6F VK_DIVIDE				Divide key
	/* 112 */	{ VC_F1,				VK_KANA					},	// 0x70 VK_F1					F1 key
	/* 113 */	{ VC_F2,				0x0000					},	// 0x71 VK_F2					F2 key
	/* 114 */	{ VC_F3,				0x0000					},	// 0x72 VK_F3					F3 key
	/* 115 */	{ VC_F4,				0x0000					},	// 0x73 VK_F4					F4 key
	/* 116 */	{ VC_F5,				0x0000					},	// 0x74 VK_F5					F5 key
	/* 117 */	{ VC_F6,				0x0000					},	// 0x75 VK_F6					F6 key
	/* 118 */	{ VC_F7,				0x0000					},	// 0x76 VK_F7					F7 key
	/* 119 */	{ VC_F8,				0x0000					},	// 0x77 VK_F8					F8 key
	/* 120 */	{ VC_F9,				0x0000					},	// 0x78 VK_F9					F9 key
	/* 121 */	{ VC_F10,				VK_KANJI				},	// 0x79 VK_F10					F10 key
	/* 122 */	{ VC_F11,				0x0000					},	// 0x7A VK_F11					F11 key
	/* 123 */	{ VC_F12,				0x0000					},	// 0x7B VK_F12					F12 key
	/* 124 */	{ VC_F13,				0x0000					},	// 0x7C VK_F13					F13 key
	/* 125 */	{ VC_F14,				VK_OEM_8				},	// 0x7D VK_F14					F14 key
	/* 126 */	{ VC_F15,				0x0000					},	// 0x7E VK_F15					F15 key
	/* 127 */	{ VC_F16,				0x0000					},	// 0x7F VK_F16					F16 key

	//			No Offset				Offset (i & 0x007F) | 0x80

	/* 128 */	{ VC_F17,				0x0000					},	// 0x80 VK_F17					F17 key
	/* 129 */	{ VC_F18,				0x0000					},	// 0x81 VK_F18					F18 key
	/* 130 */	{ VC_F19,				0x0000					},	// 0x82 VK_F19					F19 key
	/* 131 */	{ VC_F20,				0x0000					},	// 0x83 VK_F20					F20 key
	/* 132 */	{ VC_F21,				0x0000					},	// 0x84 VK_F21					F21 key
	/* 133 */	{ VC_F22,				0x0000					},	// 0x85 VK_F22					F22 key
	/* 134 */	{ VC_F23,				0x0000					},	// 0x86 VK_F23					F23 key
	/* 135 */	{ VC_F24,				0x0000					},	// 0x87 VK_F24					F24 key
	/* 136 */	{ VC_UNDEFINED,			0x0000					},	// 0x88							Unassigned
	/* 137 */	{ VC_UNDEFINED,			0x0000					},	// 0x89							Unassigned
	/* 138 */	{ VC_UNDEFINED,			0x0000					},	// 0x8A							Unassigned
	/* 139 */	{ VC_UNDEFINED,			0x0000					},	// 0x8B							Unassigned
	/* 140 */	{ VC_UNDEFINED,			0x0000					},	// 0x8C							Unassigned
	/* 141 */	{ VC_UNDEFINED,			0x0000					},	// 0x8D							Unassigned
	/* 142 */	{ VC_UNDEFINED,			0x0000					},	// 0x8E							Unassigned
	/* 143 */	{ VC_UNDEFINED,			0x0000					},	// 0x8F							Unassigned
	/* 144 */	{ VC_NUM_LOCK,			VK_MEDIA_PREV_TRACK		},	// 0x90 VK_NUMLOCK				NUM LOCK key
	/* 145 */	{ VC_SCROLL_LOCK,		0x0000					},	// 0x91 VK_SCROLL				SCROLL LOCK key
	/* 146 */	{ VC_UNDEFINED,			0x0000					},	// 0x92							OEM specific
	/* 147 */	{ VC_UNDEFINED,			0x0000					},	// 0x93							OEM specific
	/* 148 */	{ VC_UNDEFINED,			0x0000					},	// 0x94							OEM specific
	/* 149 */	{ VC_UNDEFINED,			0x0000					},	// 0x95							OEM specific
	/* 150 */	{ VC_UNDEFINED,			0x0000					},	// 0x96							OEM specific
	/* 151 */	{ VC_UNDEFINED,			0x0000					},	// 0x97							Unassigned
	/* 152 */	{ VC_UNDEFINED,			0x0000					},	// 0x98							Unassigned
	/* 153 */	{ VC_UNDEFINED,			VK_MEDIA_NEXT_TRACK		},	// 0x99							Unassigned
	/* 154 */	{ VC_UNDEFINED,			0x0000					},	// 0x9A							Unassigned
	/* 155 */	{ VC_UNDEFINED,			0x0000					},	// 0x9B							Unassigned
	/* 156 */	{ VC_UNDEFINED,			0x0000					},	// 0x9C							Unassigned
	/* 157 */	{ VC_UNDEFINED,			VK_RCONTROL				},	// 0x9D							Unassigned
	/* 158 */	{ VC_UNDEFINED,			0x0000					},	// 0x9E							Unassigned
	/* 159 */	{ VC_UNDEFINED,			0x0000					},	// 0x9F							Unassigned
	/* 160 */	{ VC_SHIFT_L,			VK_VOLUME_MUTE			},	// 0xA0 VK_LSHIFT				Left SHIFT key
	/* 161 */	{ VC_SHIFT_R,			VK_LAUNCH_APP2			},	// 0xA1 VK_RSHIFT				Right SHIFT key
	/* 162 */	{ VC_CONTROL_L,			VK_MEDIA_PLAY_PAUSE		},	// 0xA2 VK_LCONTROL				Left CONTROL key
	/* 163 */	{ VC_CONTROL_R,			0x0000					},	// 0xA3 VK_RCONTROL				Right CONTROL key
	/* 164 */	{ VC_ALT_L,				VK_MEDIA_STOP			},	// 0xA4 VK_LMENU				Left MENU key
	/* 165 */	{ VC_ALT_R,				0x0000					},	// 0xA5 VK_RMENU				Right MENU key
	/* 166 */	{ VC_BROWSER_BACK,		0x0000					},	// 0xA6 VK_BROWSER_BACK			Browser Back key
	/* 167 */	{ VC_BROWSER_FORWARD,	0x0000					},	// 0xA7 VK_BROWSER_FORWARD		Browser Forward key
	/* 168 */	{ VC_BROWSER_REFRESH,	0x0000					},	// 0xA8 VK_BROWSER_REFRESH		Browser Refresh key
	/* 169 */	{ VC_BROWSER_STOP,		0x0000					},	// 0xA9 VK_BROWSER_STOP			Browser Stop key
	/* 170 */	{ VC_BROWSER_SEARCH,	0x0000					},	// 0xAA VK_BROWSER_SEARCH		Browser Search key
	/* 171 */	{ VC_BROWSER_FAVORITES,	0x0000					},	// 0xAB VK_BROWSER_FAVORITES	Browser Favorites key
	/* 172 */	{ VC_BROWSER_HOME,		0x0000					},	// 0xAC VK_BROWSER_HOME			Browser Start and Home key
	/* 173 */	{ VC_VOLUME_MUTE,		0x0000					},	// 0xAD VK_VOLUME_MUTE			Volume Mute key
	/* 174 */	{ VC_VOLUME_DOWN,		VK_VOLUME_DOWN			},	// 0xAE VK_VOLUME_DOWN			Volume Down key
	/* 175 */	{ VC_VOLUME_UP,			0x0000					},	// 0xAF VK_VOLUME_UP			Volume Up key
	/* 176 */	{ VC_MEDIA_NEXT,		VK_VOLUME_UP			},	// 0xB0 VK_MEDIA_NEXT_TRACK		Next Track key
	/* 177 */	{ VC_MEDIA_PREVIOUS,	0x0000					},	// 0xB1 VK_MEDIA_PREV_TRACK		Previous Track key
	/* 178 */	{ VC_MEDIA_STOP,		VK_BROWSER_HOME			},	// 0xB2 VK_MEDIA_STOP			Stop Media key
	/* 179 */	{ VC_MEDIA_PLAY,		0x0000					},	// 0xB3 VK_MEDIA_PLAY_PAUSE		Play/Pause Media key
	/* 180 */	{ VC_UNDEFINED,			0x0000					},	// 0xB4 VK_LAUNCH_MAIL			Start Mail key
	/* 181 */	{ VC_MEDIA_SELECT,		VK_DIVIDE				},	// 0xB5 VK_LAUNCH_MEDIA_SELECT	Select Media key
	/* 182 */	{ VC_APP_MAIL,			0x0000					},	// 0xB6 VK_LAUNCH_APP1			Start Application 1 key
	/* 183 */	{ VC_APP_CALCULATOR,	VK_SNAPSHOT				},	// 0xB7 VK_LAUNCH_APP2			Start Application 2 key
	/* 184 */	{ VC_UNDEFINED,			VK_RMENU				},	// 0xB8							Reserved
	/* 185 */	{ VC_UNDEFINED,			0x0000					},	// 0xB9							Reserved
	/* 186 */	{ VC_SEMICOLON,			0x0000					},	// 0xBA VK_OEM_1				Varies by keyboard. For the US standard keyboard, the ';:' key
	/* 187 */	{ VC_EQUALS,			0x0000					},	// 0xBB VK_OEM_PLUS				For any country/region, the '+' key
	/* 188 */	{ VC_COMMA,				0x00E6					},	// 0xBC VK_OEM_COMMA			For any country/region, the ',' key
	/* 189 */	{ VC_MINUS,				0x0000					},	// 0xBD VK_OEM_MINUS			For any country/region, the '-' key
	/* 190 */	{ VC_PERIOD,			0x0000					},	// 0xBE VK_OEM_PERIOD			For any country/region, the '.' key
	/* 191 */	{ VC_SLASH,				0x0000					},	// 0xBF VK_OEM_2				Varies by keyboard. For the US standard keyboard, the '/?' key
	/* 192 */	{ VC_BACKQUOTE,			0x0000					},	// 0xC0 VK_OEM_3				Varies by keyboard. For the US standard keyboard, the '`~' key
	/* 193 */	{ VC_UNDEFINED,			0x0000					},	// 0xC1							Reserved
	/* 194 */	{ VC_UNDEFINED,			0x0000					},	// 0xC2							Reserved
	/* 195 */	{ VC_UNDEFINED,			0x0000					},	// 0xC3							Reserved
	/* 196 */	{ VC_UNDEFINED,			0x0000					},	// 0xC4							Reserved
	/* 197 */	{ VC_UNDEFINED,			VK_PAUSE				},	// 0xC5							Reserved
	/* 198 */	{ VC_UNDEFINED,			0x0000					},	// 0xC6							Reserved
	/* 199 */	{ VC_UNDEFINED,			VK_HOME					},	// 0xC7							Reserved
	/* 200 */	{ VC_UNDEFINED,			VK_UP					},	// 0xC8							Reserved
	/* 201 */	{ VC_UNDEFINED,			VK_PRIOR				},	// 0xC9							Reserved
	/* 202 */	{ VC_UNDEFINED,			0x0000					},	// 0xCA							Reserved
	/* 203 */	{ VC_UNDEFINED,			VK_LEFT					},	// 0xCB							Reserved
	/* 204 */	{ VC_UNDEFINED,			VK_CLEAR				},	// 0xCC							Reserved
	/* 205 */	{ VC_UNDEFINED,			VK_RIGHT				},	// 0xCD							Reserved
	/* 206 */	{ VC_UNDEFINED,			0x0000					},	// 0xCE							Reserved
	/* 207 */	{ VC_UNDEFINED,			VK_END					},	// 0xCF							Reserved
	/* 208 */	{ VC_UNDEFINED,			VK_DOWN					},	// 0xD0							Reserved
	/* 209 */	{ VC_UNDEFINED,			VK_NEXT					},	// 0xD1							Reserved
	/* 210 */	{ VC_UNDEFINED,			VK_INSERT				},	// 0xD2							Reserved
	/* 211 */	{ VC_UNDEFINED,			VK_DELETE				},	// 0xD3							Reserved
	/* 212 */	{ VC_UNDEFINED,			0x0000					},	// 0xD4							Reserved
	/* 213 */	{ VC_UNDEFINED,			0x0000					},	// 0xD5							Reserved
	/* 214 */	{ VC_UNDEFINED,			0x0000					},	// 0xD6							Reserved
	/* 215 */	{ VC_UNDEFINED,			0x0000					},	// 0xD7							Reserved
	/* 216 */	{ VC_UNDEFINED,			0x0000					},	// 0xD8							Unassigned
	/* 217 */	{ VC_UNDEFINED,			0x0000					},	// 0xD9							Unassigned
	/* 218 */	{ VC_UNDEFINED,			0x0000					},	// 0xDA							Unassigned
	/* 219 */	{ VC_OPEN_BRACKET,		VK_LWIN					},	// 0xDB VK_OEM_4				Varies by keyboard. For the US standard keyboard, the '[{' key
	/* 220 */	{ VC_BACK_SLASH,		VK_RWIN					},	// 0xDC VK_OEM_5				Varies by keyboard. For the US standard keyboard, the '\|' key
	/* 221 */	{ VC_CLOSE_BRACKET,		VK_APPS					},	// 0xDD VK_OEM_6				Varies by keyboard. For the US standard keyboard, the ']}' key
	/* 222 */	{ VC_QUOTE,				0x0000					},	// 0xDE VK_OEM_7				Varies by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
	/* 223 */	{ VC_YEN,				VK_SLEEP				},	// 0xDF VK_OEM_8				Varies by keyboard.
	/* 224 */	{ VC_UNDEFINED,			0x0000					},	// 0xE0							Reserved
	/* 225 */	{ VC_UNDEFINED,			0x0000					},	// 0xE1							OEM specific
	/* 226 */	{ VC_UNDEFINED,			0x0000					},	// 0xE2 VK_OEM_102				Either the angle bracket key or the backslash key on the RT 102-key keyboard
	/* 227 */	{ VC_UNDEFINED,			0x0000					},	// 0xE3							OEM specific
	/* 228 */	{ VC_UNDEFINED,			0x00E5					},	// 0xE4	VC_APP_PICTURES 		OEM specific
	/* 229 */	{ VC_APP_PICTURES,		VK_BROWSER_SEARCH		},	// 0xE5 VK_PROCESSKEY			IME PROCESS key
	/* 230 */	{ VC_APP_MUSIC,			VK_BROWSER_FAVORITES	},	// 0xE6							OEM specific
	/* 231 */	{ VC_UNDEFINED,			VK_BROWSER_REFRESH		},	// 0xE7 VK_PACKET				Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods.
	/* 232 */	{ VC_UNDEFINED,			VK_BROWSER_STOP			},	// 0xE8							Unassigned
	/* 233 */	{ VC_UNDEFINED,			VK_BROWSER_FORWARD		},	// 0xE9							OEM specific
	/* 234 */	{ VC_UNDEFINED,			VK_BROWSER_BACK			},	// 0xEA							OEM specific
	/* 235 */	{ VC_UNDEFINED,			0x0000					},	// 0xEB							OEM specific
	/* 236 */	{ VC_UNDEFINED,			VK_LAUNCH_APP1			},	// 0xEC							OEM specific
	/* 237 */	{ VC_UNDEFINED,			VK_LAUNCH_MEDIA_SELECT	},	// 0xED							OEM specific
	/* 238 */	{ VC_UNDEFINED,			0x0000					},	// 0xEE							OEM specific
	/* 239 */	{ VC_UNDEFINED,			0x0000					},	// 0xEF							OEM specific
	/* 240 */	{ VC_UNDEFINED,			0x0000					},	// 0xF0							OEM specific
	/* 241 */	{ VC_UNDEFINED,			0x0000					},	// 0xF1							OEM specific
	/* 242 */	{ VC_UNDEFINED,			0x0000					},	// 0xF2							OEM specific
	/* 243 */	{ VC_UNDEFINED,			0x0000					},	// 0xF3							OEM specific
	/* 244 */	{ VC_UNDEFINED,			0x0000					},	// 0xF4							OEM specific
	/* 245 */	{ VC_UNDEFINED,			0x0000					},	// 0xF5							OEM specific
	/* 246 */	{ VC_UNDEFINED,			0x0000					},	// 0xF6 VK_ATTN					Attn key
	/* 247 */	{ VC_UNDEFINED,			0x0000					},	// 0xF7 VK_CRSEL				CrSel key
	/* 248 */	{ VC_UNDEFINED,			0x0000					},	// 0xF8 VK_EXSEL				ExSel key
	/* 249 */	{ VC_UNDEFINED,			0x0000					},	// 0xF9 VK_EREOF				Erase EOF key
	/* 250 */	{ VC_UNDEFINED,			0x0000					},	// 0xFA VK_PLAY					Play key
	/* 251 */	{ VC_UNDEFINED,			0x0000					},	// 0xFB VK_ZOOM					Zoom key
	/* 252 */	{ VC_UNDEFINED,			0x0000					},	// 0xFC VK_NONAME				Reserved
	/* 253 */	{ VC_UNDEFINED,			0x0000					},	// 0xFD
	/* 254 */	{ VC_CLEAR,				0x0000					},	// 0xFE VK_OEM_CLEAR			Clear key
	/* 255 */	{ VC_UNDEFINED,			0x0000					}	// 0xFE							Unassigned
};

unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) {
	unsigned short scancode = VC_UNDEFINED;

	// Check the vk_code is in range.
	// NOTE vk_code >= 0 is assumed because DWORD is unsigned.
	if (vk_code < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
		scancode = keycode_scancode_table[vk_code][0];

		if (flags & LLKHF_EXTENDED) {
//			logger(LOG_LEVEL_WARN,	"%s [%u]: EXTD2, vk_code %li\n",
//					__FUNCTION__, __LINE__, vk_code);

			switch (vk_code) {
				case VK_PRIOR:
				case VK_NEXT:
				case VK_END:
				case VK_HOME:
				case VK_LEFT:
				case VK_UP:
				case VK_RIGHT:
				case VK_DOWN:

				case VK_INSERT:
				case VK_DELETE:
					scancode |= 0xEE00;
					break;

				case VK_RETURN:
					scancode |= 0x0E00;
					break;
			}
		}
		else {
						// logger(LOG_LEVEL_WARN,	"%s [%u]: Test2, vk_code %li\n",
            			// 		__FUNCTION__, __LINE__, vk_code);
		}
	}

	return scancode;
}

DWORD scancode_to_keycode(unsigned short scancode) {
	unsigned short keycode = 0x0000;

	// Check the vk_code is in range.
	// NOTE vk_code >= 0 is assumed because the scancode is unsigned.
	if (scancode < 128) {
		keycode = keycode_scancode_table[scancode][1];
	}
	else {
		// Calculate the upper offset based on the lower half of the scancode + 128.
		unsigned short int i = (scancode & 0x007F) | 0x80;

		if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
			keycode = keycode_scancode_table[i][1];
		}
	}

	return keycode;
}


/************************************************************************/

// Structure and pointers for the keyboard locale cache.
typedef struct _KeyboardLocale {
	HKL id;									// Locale ID
	HINSTANCE library;						// Keyboard DLL instance.
	PVK_TO_BIT pVkToBit;					// Pointers struct arrays.
	PVK_TO_WCHAR_TABLE pVkToWcharTable;
	PDEADKEY pDeadKey;
	struct _KeyboardLocale* next;
} KeyboardLocale;

static KeyboardLocale* locale_first = NULL;
static KeyboardLocale* locale_current = NULL;
static WCHAR deadChar = WCH_NONE;

// Amount of pointer padding to apply for Wow64 instances.
static unsigned short int ptr_padding = 0;

#if defined(_WIN32) && !defined(_WIN64)
// Small function to check and see if we are executing under Wow64.
static BOOL is_wow64() {
	BOOL status = FALSE;

	LPFN_ISWOW64PROCESS pIsWow64Process = (LPFN_ISWOW64PROCESS)
			GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");

	if (pIsWow64Process != NULL) {
		HANDLE current_proc = GetCurrentProcess();

		if (!pIsWow64Process(current_proc, &status)) {
			status = FALSE;

			logger(LOG_LEVEL_DEBUG,	"%s [%u]: pIsWow64Process(%#p, %#p) failed!\n",
					__FUNCTION__, __LINE__, current_proc, &status);
		}
	}

	return status;
}
#endif

// Locate the DLL that contains the current keyboard layout.
static int get_keyboard_layout_file(char *layoutFile, DWORD bufferSize) {
	int status = IOHOOK_FAILURE;
	HKEY hKey;
	DWORD varType = REG_SZ;

	char kbdName[KL_NAMELENGTH];
	if (GetKeyboardLayoutName(kbdName)) {
		char kbdKeyPath[51 + KL_NAMELENGTH];
		snprintf(kbdKeyPath, 51 + KL_NAMELENGTH, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", kbdName);

		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR) kbdKeyPath, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
			if (RegQueryValueEx(hKey, "Layout File", NULL, &varType, (LPBYTE) layoutFile, &bufferSize) == ERROR_SUCCESS) {
				RegCloseKey(hKey);
				status = IOHOOK_SUCCESS;
			}
		}
	}

	return status;
}

static int refresh_locale_list() {
	int count = 0;

	// Get the number of layouts the user has activated.
	int hkl_size = GetKeyboardLayoutList(0, NULL);
	if (hkl_size > 0) {
		logger(LOG_LEVEL_INFO,	"%s [%u]: GetKeyboardLayoutList(0, NULL) found %i layouts.\n",
				__FUNCTION__, __LINE__, hkl_size);

		// Get the thread id that currently has focus for our default.
		DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
		HKL hlk_focus = GetKeyboardLayout(focus_pid);
		HKL hlk_default = GetKeyboardLayout(0);
		HKL *hkl_list = malloc(sizeof(HKL) * hkl_size);

		int new_size = GetKeyboardLayoutList(hkl_size, hkl_list);
		if (new_size > 0) {
			if (new_size != hkl_size) {
				logger(LOG_LEVEL_WARN,	"%s [%u]: Locale size mismatch!  "
						"Expected %i, received %i!\n",
						__FUNCTION__, __LINE__, hkl_size, new_size);
			}
			else {
				logger(LOG_LEVEL_INFO,	"%s [%u]: Received %i locales.\n",
						__FUNCTION__, __LINE__, new_size);
			}

			KeyboardLocale* locale_previous = NULL;
			KeyboardLocale* locale_item = locale_first;

			// Go though the linked list and remove KeyboardLocale's that are
			// no longer loaded.
			while (locale_item != NULL) {
				// Check to see if the old HKL is in the new list.
				bool is_loaded = false;
				int i;
				for (i = 0; i < new_size && !is_loaded; i++) {
					if (locale_item->id == hkl_list[i]) {
						// Flag and jump out of the loop.
						hkl_list[i] = NULL;
						is_loaded = true;
					}
				}


				if (is_loaded) {
					logger(LOG_LEVEL_DEBUG,	"%s [%u]: Found locale ID %#p in the cache.\n",
							__FUNCTION__, __LINE__, locale_item->id);

					// Set the previous local to the current locale.
					locale_previous = locale_item;

					// Check and see if the locale is our current active locale.
					if (locale_item->id == hlk_focus) {
						locale_current = locale_item;
					}

					count++;
				}
				else {
					logger(LOG_LEVEL_DEBUG,	"%s [%u]: Removing locale ID %#p from the cache.\n",
							__FUNCTION__, __LINE__, locale_item->id);

					// If the old id is not in the new list, remove it.
					locale_previous->next = locale_item->next;

					// Make sure the locale_current points NULL or something valid.
					if (locale_item == locale_current) {
						locale_current = NULL;
					}

					// Free the memory used by locale_item;
					free(locale_item);

					// Set the item to the pervious item to guarantee a next.
					locale_item = locale_previous;
				}

				// Iterate to the next linked list item.
				locale_item = locale_item->next;
			}


			// Insert anything new into the linked list.
			int i;
			for (i = 0; i < new_size; i++) {
				// Check to see if the item was already in the list.
				if (hkl_list[i] != NULL) {
					// Set the active keyboard layout for this thread to the HKL.
					ActivateKeyboardLayout(hkl_list[i], 0x00);

					// Try to pull the current keyboard layout DLL from the registry.
					char layoutFile[MAX_PATH];
					if (get_keyboard_layout_file(layoutFile, sizeof(layoutFile)) == IOHOOK_SUCCESS) {
						// You can't trust the %SYSPATH%, look it up manually.
						char systemDirectory[MAX_PATH];
						if (GetSystemDirectory(systemDirectory, MAX_PATH) != 0) {
							char kbdLayoutFilePath[MAX_PATH];
							snprintf(kbdLayoutFilePath, MAX_PATH, "%s\\%s", systemDirectory, layoutFile);

							logger(LOG_LEVEL_DEBUG,	"%s [%u]: Loading layout for %#p: %s.\n",
									__FUNCTION__, __LINE__, hkl_list[i], layoutFile);

							// Create the new locale item.
							locale_item = malloc(sizeof(KeyboardLocale));
							locale_item->id = hkl_list[i];
							locale_item->library = LoadLibrary(kbdLayoutFilePath);

							// Get the function pointer from the library to get the keyboard layer descriptor.
							KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(locale_item->library, "KbdLayerDescriptor");
							if (pKbdLayerDescriptor != NULL) {
								PKBDTABLES pKbd = pKbdLayerDescriptor();

								// Store the memory address of the following 3 structures.
								BYTE *base = (BYTE *) pKbd;

								// First element of each structure, no offset adjustment needed.
								locale_item->pVkToBit = pKbd->pCharModifiers->pVkToBit;

								// Second element of pKbd, +4 byte offset on wow64.
								locale_item->pVkToWcharTable = *((PVK_TO_WCHAR_TABLE *) (base + offsetof(KBDTABLES, pVkToWcharTable) + ptr_padding));

								// Third element of pKbd, +8 byte offset on wow64.
								locale_item->pDeadKey = *((PDEADKEY *) (base + offsetof(KBDTABLES, pDeadKey) + (ptr_padding * 2)));

								// This will always be added to the end of the list.
								locale_item->next = NULL;

								// Insert the item into the linked list.
								if (locale_previous == NULL) {
									// If nothing came before, the list is empty.
									locale_first = locale_item;
								}
								else {
									// Append the new locale to the end of the list.
									locale_previous->next = locale_item;
								}

								// Check and see if the locale is our current active locale.
								if (locale_item->id == hlk_focus) {
									locale_current = locale_item;
								}

								// Set the pervious locale item to the new one.
								locale_previous = locale_item;

								count++;
							}
							else {
								logger(LOG_LEVEL_ERROR,
										"%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n",
										__FUNCTION__, __LINE__);

								FreeLibrary(locale_item->library);
								free(locale_item);
								locale_item = NULL;
							}
						}
						else {
							logger(LOG_LEVEL_ERROR,
									"%s [%u]: GetSystemDirectory() failed!\n",
									__FUNCTION__, __LINE__);
						}
					}
					else {
						logger(LOG_LEVEL_ERROR,
								"%s [%u]: Could not find keyboard map for locale %#p!\n",
								__FUNCTION__, __LINE__, hkl_list[i]);
					}
				}
			}
		}
		else {
			logger(LOG_LEVEL_ERROR,
					"%s [%u]: GetKeyboardLayoutList() failed!\n",
					__FUNCTION__, __LINE__);

			// TODO Try and recover by using the current layout.
			// Hint: Use locale_id instead of hkl_list[i] in the loop above.
		}

		free(hkl_list);
		ActivateKeyboardLayout(hlk_default, 0x00);
	}

	return count;
}

SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) {
	// Get the thread id that currently has focus and ask for its current
	// locale.
	DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
	HKL locale_id = GetKeyboardLayout(focus_pid);

	// If the current Locale is not the new locale, search the linked list.
	if (locale_current == NULL || locale_current->id != locale_id) {
		locale_current = NULL;
		KeyboardLocale* locale_item = locale_first;

		// Search the linked list...
		while (locale_item != NULL && locale_item->id != locale_id) {
			locale_item = locale_item->next;
		}

		// You may already be a winner!
		if (locale_item != NULL && locale_item->id != locale_id) {
			logger(LOG_LEVEL_INFO,
					"%s [%u]: Activating keyboard layout %#p.\n",
					__FUNCTION__, __LINE__, locale_item->id);

			// Switch the current locale.
			locale_current = locale_item;
			locale_item = NULL;

			// If they layout changes the dead key state needs to be reset.
			// This is consistent with the way Windows handles locale changes.
			deadChar = WCH_NONE;
		}
		else {
			logger(LOG_LEVEL_DEBUG,
					"%s [%u]: Refreshing locale cache.\n",
					__FUNCTION__, __LINE__);

			refresh_locale_list();
		}
	}

	// Initialize to empty.
	SIZE_T charCount = 0;
	// buffer[i] = WCH_NONE;

	// Check and make sure the Unicode helper was loaded.
	if (locale_current != NULL) {
		logger(LOG_LEVEL_INFO,
				"%s [%u]: Using keyboard layout %#p.\n",
				__FUNCTION__, __LINE__, locale_current->id);

		int mod = 0;

		int capsLock = (GetKeyState(VK_CAPITAL) & 0x01);

		PVK_TO_BIT pVkToBit = locale_current->pVkToBit;
		PVK_TO_WCHAR_TABLE pVkToWcharTable = locale_current->pVkToWcharTable;
		PDEADKEY pDeadKey = locale_current->pDeadKey;

		/* Loop over the modifier keys for this locale and determine what is
		 * currently depressed.  Because this is only a structure of two
		 * bytes, we don't need to worry about the structure padding of __ptr64
		 * offsets on Wow64.
		 */
		bool is_shift = false, is_ctrl = false, is_alt = false;
		int i;
		for (i = 0; pVkToBit[i].Vk != 0; i++) {
			short state = GetAsyncKeyState(pVkToBit[i].Vk);

			// Check to see if the most significant bit is active.
			if (state & ~SHRT_MAX) {
				if (pVkToBit[i].Vk == VK_SHIFT) {
					is_shift = true;
				}
				else if (pVkToBit[i].Vk == VK_CONTROL) {
					is_ctrl = true;
				}
				else if (pVkToBit[i].Vk == VK_MENU) {
					is_alt = true;
				}
			}
		}

		// Check the Shift modifier.
		if (is_shift) {
			mod = 1;
		}

		// Check for the AltGr modifier.
		if (is_ctrl && is_alt) {
			mod += 3;
		}

		// Default 32 bit structure size should be 6 bytes (4 for the pointer and 2
		// additional byte fields) that are padded out to 8 bytes by the compiler.
		unsigned short sizeVkToWcharTable = sizeof(VK_TO_WCHAR_TABLE);
		#if defined(_WIN32) && !defined(_WIN64)
		if (is_wow64()) {
			// If we are running under Wow64 the size of the first pointer will be
			// 8 bringing the total size to 10 bytes padded out to 16.
			sizeVkToWcharTable = (sizeVkToWcharTable + ptr_padding + 7) & -8;
		}
		#endif

		BYTE *ptrCurrentVkToWcharTable = (BYTE *) pVkToWcharTable;

		int cbSize, n;
		do {
			// cbSize is used to calculate n, and n is used for the size of pVkToWchars[j].wch[n]
			cbSize = *(ptrCurrentVkToWcharTable + offsetof(VK_TO_WCHAR_TABLE, cbSize) + ptr_padding);
			n = (cbSize - 2) / 2;

			// Same as VK_TO_WCHARS pVkToWchars[] = pVkToWcharTable[i].pVkToWchars
			PVK_TO_WCHARS pVkToWchars = (PVK_TO_WCHARS) ((PVK_TO_WCHAR_TABLE) ptrCurrentVkToWcharTable)->pVkToWchars;

			if (pVkToWchars != NULL && mod < n) {
				// pVkToWchars[j].VirtualKey
				BYTE *pCurrentVkToWchars = (BYTE *) pVkToWchars;

				do {
					if (((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey == keycode) {
						if ((((PVK_TO_WCHARS) pCurrentVkToWchars)->Attributes == CAPLOK) && capsLock) {
							if (is_shift && mod > 0) {
								mod -= 1;
							}
							else {
								mod += 1;
							}
						}

						// Set the initial unicode char.
						WCHAR unicode = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];

						// Increment the pCurrentVkToWchars by the size of wch[n].
						pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);


						if (unicode == WCH_DEAD) {
							// The current unicode char is a dead key...
							if (deadChar == WCH_NONE) {
								// No previous dead key was set so cache the next
								// wchar so we know what to do next time its pressed.
								deadChar = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
							}
							else {
								if (size >= 2) {
									// Received a second dead key.
									memset(buffer, deadChar, 2);
									//buffer[0] = deadChar;
									//buffer[1] = deadChar;

									deadChar = WCH_NONE;
									charCount = 2;
								}
							}
						}
						else if (unicode != WCH_NONE) {
							// We are not WCH_NONE or WCH_DEAD
							if (size >= 1) {
								buffer[0] = unicode;
								charCount = 1;
							}
						}

						break;
					}
					else {
						// Add sizeof WCHAR because we are really an array of WCHAR[n] not WCHAR[]
						pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
					}
				} while ( ((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey != 0 );
			}

			// This is effectively the same as: ptrCurrentVkToWcharTable = pVkToWcharTable[++i];
			ptrCurrentVkToWcharTable += sizeVkToWcharTable;
		} while (cbSize != 0);


		// If the current local has a dead key set.
		if (deadChar != WCH_NONE) {
			// Loop over the pDeadKey lookup table for the locale.
			int i;
			for (i = 0; pDeadKey[i].dwBoth != 0; i++) {
				WCHAR baseChar = (WCHAR) pDeadKey[i].dwBoth;
				WCHAR diacritic = (WCHAR) (pDeadKey[i].dwBoth >> 16);

				// If we locate an extended dead char, set it.
				if (size >= 1 && baseChar == buffer[0] && diacritic == deadChar) {
					deadChar = WCH_NONE;

					if (charCount <= size) {
						memset(buffer, (WCHAR) pDeadKey[i].wchComposed, charCount);
						//buffer[i] = (WCHAR) pDeadKey[i].wchComposed;
					}
				}
			}
		}
	}

	return charCount;
}

int load_input_helper() {
	int count = 0;

	#if defined(_WIN32) && !defined(_WIN64)
	if (is_wow64()) {
		ptr_padding = sizeof(void *);
	}
	#endif

	count = refresh_locale_list();

	logger(LOG_LEVEL_INFO,
			"%s [%u]: refresh_locale_list() found %i locale(s).\n",
			__FUNCTION__, __LINE__, count);

	return count;
}

// This returns the number of locales that were removed.
int unload_input_helper() {
	int count = 0;

	// Cleanup and free memory from the old list.
	KeyboardLocale* locale_item = locale_first;
	while (locale_item != NULL) {
		// Remove the first item from the linked list.
		FreeLibrary(locale_item->library);
		locale_first = locale_item->next;
		free(locale_item);
		locale_item = locale_first;

		count++;
	}

	// Reset the current local.
	locale_current = NULL;

	return count;
}
