//#define WIN32_LEAN_AND_MEAN

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);

bool CreateMainWindow(HINSTANCE, int);
LRESULT WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);


const char CLASS_NAME[] = "WinMain";
const char APP_TITLE[] = "Hello World";
const int WINDOW_WIDTH = 400;
const int WINDOW_HEIGHT = 400;
char ch;
HDC hdc;
RECT rect;
PAINTSTRUCT ps;
bool vkKeys[256];

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;
	if (!CreateMainWindow(hInstance, nCmdShow))
		return false;
	bool done = false;
	while (!done) {
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT) {
				done = true;
			}
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}

bool CreateMainWindow(HINSTANCE hInstance, int nCmdShow)
{
	HWND hwnd;
	WNDCLASSEX wcx;

	wcx.cbSize = sizeof(wcx);
	wcx.style = CS_HREDRAW | CS_VREDRAW;
	wcx.lpfnWndProc = (WNDPROC)WinProc;
	wcx.cbClsExtra = 0;
	wcx.cbWndExtra = 0;
	wcx.hInstance = hInstance;
	wcx.hIcon = NULL;
	wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wcx.lpszMenuName = NULL;
	wcx.lpszClassName = CLASS_NAME;
	wcx.hIconSm = NULL;

	if (RegisterClassEx(&wcx) == 0) {
		return false;
	}

	hwnd = CreateWindow(
		CLASS_NAME,
		APP_TITLE,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		WINDOW_WIDTH,
		WINDOW_HEIGHT,
		(HWND)NULL,
		(HMENU)NULL,
		hInstance,
		(LPVOID)NULL);

	if (!hwnd) {
		return false;
	}

	ShowWindow(hwnd, nCmdShow);

	UpdateWindow(hwnd);

	return true;


	return false;
}

LRESULT WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	short nVirtualKey;
	const short SHIFTED = (short)0x8000;
	TEXTMETRIC tm;
	DWORD chWidth = 20;
	DWORD chHeight = 20;

	switch (msg) {
	case WM_CREATE:
		hdc = GetDC(hWnd);
		GetTextMetrics(hdc, &tm);
		ReleaseDC(hWnd, hdc);
		chWidth = tm.tmAveCharWidth;
		chHeight = tm.tmHeight;
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CHAR:
		switch(wParam) {
			case 0x08:
			case 0x09:
			case 0x0A:
			case 0x0d:
			case 0x1b:
				MessageBeep((UINT)-1);
				return 0;
			default:
				ch = (TCHAR)wParam;
				InvalidateRect(hWnd, NULL, TRUE);
				return 0;
		}
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc, 0, 0, &ch, 1);
		for (int r = 0; r < 16; r++) {
			for (int c = 0; c < 16; c++)
			{
				if (vkKeys[r * 16 + c]) {
					SetBkMode(hdc, OPAQUE);
					TextOut(hdc, c * chWidth + chWidth * 2, r* chHeight + chHeight * 2, "T ", 2);
				}
				else {
					SetBkMode(hdc, TRANSPARENT);
					TextOut(hdc, c * chWidth + chWidth * 2, r* chHeight + chHeight * 2, "F ", 2);
				}
			}
		}
		EndPaint(hWnd, &ps);
		return 0;
	case WM_KEYDOWN:
		vkKeys[wParam] = true;
		switch (wParam)
		{
		case VK_SHIFT:
			nVirtualKey = GetKeyState(VK_LSHIFT);
			if (nVirtualKey & SHIFTED) {
				vkKeys[VK_LSHIFT] = true;
			}
			nVirtualKey = GetKeyState(VK_RSHIFT);
			if (nVirtualKey & SHIFTED) {
				vkKeys[VK_RSHIFT] = true;
			}
			break;
		case VK_CONTROL:
			nVirtualKey = GetKeyState(VK_LCONTROL);
			if (nVirtualKey & SHIFTED) {
				vkKeys[VK_LCONTROL] = true;
			}
			nVirtualKey = GetKeyState(VK_RCONTROL);
			if (nVirtualKey & SHIFTED) {
				vkKeys[VK_RCONTROL] = true;
			}
			break;
		}
		InvalidateRect(hWnd, NULL, TRUE);
		return 0;
	case WM_KEYUP:
		vkKeys[wParam] = false;
		switch (wParam)
		{
		case VK_SHIFT:
			nVirtualKey = GetKeyState(VK_LSHIFT);
			if ((nVirtualKey & SHIFTED) == 0) {
				vkKeys[VK_LSHIFT] = false;
			}
			nVirtualKey = GetKeyState(VK_RSHIFT);
			if ((nVirtualKey & SHIFTED) == 0) {
				vkKeys[VK_RSHIFT] = false;
			}
			break;
		case VK_CONTROL:
			nVirtualKey = GetKeyState(VK_LCONTROL);
			if ((nVirtualKey & SHIFTED) == 0) {
				vkKeys[VK_LCONTROL] = false;
			}
			nVirtualKey = GetKeyState(VK_RCONTROL);
			if ((nVirtualKey & SHIFTED) == 0) {
				vkKeys[VK_RCONTROL] = false;
			}
			break;
		}
		InvalidateRect(hWnd, NULL, TRUE);
		return 0;

	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}
