#include "Global.h"
#include "WindowsMessaging.h"
#include "VKey.h"
#include "DeviceEnumerator.h"
#include "WndProcEater.h"
#include "WindowsKeyboard.h"
#include "WindowsMouse.h"

ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output);

class WindowsMessagingKeyboard;
class WindowsMessagingMouse;

static WindowsMessagingKeyboard *wmk = 0;
static WindowsMessagingMouse *wmm = 0;

class WindowsMessagingKeyboard : public WindowsKeyboard {
public:

	WindowsMessagingKeyboard() : WindowsKeyboard(WM, L"WM Keyboard") {
	}

	int Activate(void *d) {
		InitInfo *info = (InitInfo*)d;
		// Redundant.  Should match the next line.
		// Deactivate();
		if (wmk) wmk->Deactivate();
		HWND hWnd = info->hWnd;
		if (info->hWndButton) {
			hWnd = info->hWndButton;
			// hWndDlg = info->hWnd;
		}
		if (!wmm && !EatWndProc(hWnd, WindowsMessagingWndProc)) {
			Deactivate();
			return 0;
		}

		wmk = this;
		InitState();

		active = 1;
		return 1;
	}

	void Deactivate() {
		FreeState();
		if (active) {
			if (!wmm)
				ReleaseExtraProc(WindowsMessagingWndProc);
			active = 0;
			wmk = 0;
		}
		// hWndDlg = 0;
	}


	void CheckKey(int vkey) {
		UpdateKey(vkey, 1&(((unsigned short)GetAsyncKeyState(vkey))>>15));
	}
};

class WindowsMessagingMouse : public WindowsMouse {
public:

	WindowsMessagingMouse() : WindowsMouse(WM, 1, L"WM Mouse") {
	}

	int Activate(void *d) {
		InitInfo *info = (InitInfo*)d;
		// Redundant.  Should match the next line.
		// Deactivate();
		if (wmm) wmm->Deactivate();
		HWND hWnd = info->hWnd;
		if (info->hWndButton) {
			hWnd = info->hWndButton;
		}

		if (!wmk && !EatWndProc(hWnd, WindowsMessagingWndProc)) {
			Deactivate();
			return 0;
		}

		SetCapture(hWnd);
		ShowCursor(0);

		GetCursorPos(&origCursorPos);
		active = 1;

		RECT r;
		GetWindowRect(hWnd, &r);
		ClipCursor(&r);
		center.x = (r.left + r.right)/2;
		center.y = (r.top + r.bottom)/2;
		SetCursorPos(center.x, center.y);

		wmm = this;
		AllocState();

		return 1;
	}

	void Deactivate() {
		FreeState();
		if (active) {
			ClipCursor(0);
			ReleaseCapture();
			ShowCursor(1);
			SetCursorPos(origCursorPos.x, origCursorPos.y);
			if (!wmk)
				ReleaseExtraProc(WindowsMessagingWndProc);
			active = 0;
			wmm = 0;
		}
	}
};

ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) {
	if (wmk) {
		if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) {
			if (wParam == VK_SHIFT) {
				wmk->CheckKey(VK_RSHIFT);
				wmk->CheckKey(VK_LSHIFT);
			}
			else if (wParam == VK_CONTROL) {
				wmk->CheckKey(VK_RCONTROL);
				wmk->CheckKey(VK_LCONTROL);
			}
			else if (wParam == VK_MENU) {
				wmk->CheckKey(VK_RMENU);
				wmk->CheckKey(VK_LMENU);
			}
			else 
				wmk->UpdateKey(wParam, (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN));
			return NO_WND_PROC;
		}
		// Needed to prevent default handling of keys in some situations.
		else if (uMsg == WM_CHAR || uMsg == WM_UNICHAR) {
			return NO_WND_PROC;
		}
		else if (uMsg == WM_ACTIVATE) {
			// Not really needed, but doesn't hurt.
			memset(wmk->physicalControlState, 0, sizeof(int) * wmk->numPhysicalControls);
		}
	}
	if (wmm) {
		if (uMsg == WM_MOUSEMOVE) {
			POINT p;
			GetCursorPos(&p);
			// Need check to prevent cursor movement cascade.
			if (p.x != wmm->center.x || p.y != wmm->center.y) {
				wmm->UpdateAxis(0, p.x - wmm->center.x);
				wmm->UpdateAxis(1, p.y - wmm->center.y);

				SetCursorPos(wmm->center.x, wmm->center.y);
			}
			return NO_WND_PROC;
		}
		else if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) {
			wmm->UpdateButton(0, uMsg == WM_LBUTTONDOWN);
			return NO_WND_PROC;
		}
		else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) {
			wmm->UpdateButton(1, uMsg == WM_RBUTTONDOWN);
			return NO_WND_PROC;
		}
		else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) {
			wmm->UpdateButton(2, uMsg == WM_MBUTTONDOWN);
			return NO_WND_PROC;
		}
		else if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) {
			wmm->UpdateButton(3+((wParam>>16) == XBUTTON2), uMsg == WM_XBUTTONDOWN);
			return NO_WND_PROC;
		}
		else if (uMsg == WM_MOUSEWHEEL) {
			wmm->UpdateAxis(2, ((int)wParam>>16)/WHEEL_DELTA);
			return NO_WND_PROC;
		}
		else if (uMsg == WM_MOUSEHWHEEL) {
			wmm->UpdateAxis(3, ((int)wParam>>16)/WHEEL_DELTA);
			return NO_WND_PROC;
		}
		// Taken care of elsewhere.  When binding, killing focus means stop reading input.
		// When running PCSX2, I release all mouse and keyboard input elsewhere.
		/*else if (uMsg == WM_KILLFOCUS) {
			wmm->Deactivate();
		}//*/
	}
	return CONTINUE_BLISSFULLY;
}

void EnumWindowsMessagingDevices() {
	dm->AddDevice(new WindowsMessagingKeyboard());
	dm->AddDevice(new WindowsMessagingMouse());
}
