/*  LilyPad - Pad plugin for PS2 Emulator
 *  Copyright (C) 2002-2014  PCSX2 Dev Team/ChickenLiver
 *
 *  PCSX2 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 Found- ation, either version 3 of the License, or (at your option)
 *  any later version.
 *
 *  PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with PCSX2.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Global.h"
#include "InputManager.h"
#include "Config.h"

#include "KeyboardHook.h"
#include "WindowsKeyboard.h"
#include "VKey.h"
#include "WndProcEater.h"

extern HINSTANCE hInst;
LRESULT CALLBACK IgnoreKeyboardHook(int code, WPARAM wParam, LPARAM lParam);

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

class IgnoreKeyboardHookDevice;
static IgnoreKeyboardHookDevice *ikhd = 0;

class IgnoreKeyboardHookDevice : public WindowsKeyboard {
public:
	HHOOK hHook;
	// When binding, eat all keys
	int binding;

	IgnoreKeyboardHookDevice() : WindowsKeyboard(IGNORE_KEYBOARD, L"Ignore Keyboard") {
		// Don't want to send keypress/keyrelease messages.
		for (int i=0; i<numPhysicalControls; i++) {
			physicalControls[i].vkey = 0;
		}
		hHook = 0;
	}

	int Update() {
		// So I'll bind to left/right control/shift instead of generic ones.
		virtualControlState[VK_SHIFT] = 0;
		virtualControlState[VK_CONTROL] = 0;
		virtualControlState[VK_MENU] = 0;
		return 1;
	}

	int Activate(InitInfo *initInfo) {
		if (ikhd) ikhd->Deactivate();
		binding = initInfo->bindingIgnore;
		hWndProc = initInfo->hWndProc;

		return 0;
		printf( "(Lilypad) StartHook\n" );
		hWndProc->Eat(StartHooksWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES);

		InitState();
		ikhd = this;
		active = 1;
		return 1;
	}

	void Deactivate() {
		printf( "(Lilypad) KillHook\n" );

		FreeState();
		if (active) {
			if (hHook) {
				UnhookWindowsHookEx(hHook);
				hHook = 0;
			}
			if (ikhd == this) ikhd = 0;
			active = 0;
		}
	}
};

void EnumHookDevices() {
	dm->AddDevice(new IgnoreKeyboardHookDevice());
}

// Makes sure hooks are started in correct thread.
ExtraWndProcResult StartHooksWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) {
	// Don't remove too quickly - could be other things happening in other threads.
	// Attempted fix for keyboard input occasionally not working.
	static int counter = 0;
	if (ikhd && !ikhd->hHook && ikhd->active) {
		counter = 0;
		printf( "(Lilypad) SetHook\n" );
		ikhd->hHook = SetWindowsHookEx(WH_KEYBOARD_LL, IgnoreKeyboardHook, hInst, 0);//GetCurrentThreadId());
		if (ikhd->hHook == 0) ikhd->Deactivate();
	}
	printf( "(Lilypad) HookThinking!\n" );
	counter ++;
	if (counter % 1000 == 0)
		return CONTINUE_BLISSFULLY_AND_RELEASE_PROC;
	else
		return CONTINUE_BLISSFULLY;
}

LRESULT CALLBACK IgnoreKeyboardHook(int code, WPARAM wParam, LPARAM lParam) {
	HHOOK hHook = 0;
	if (ikhd) {
		printf( "(Lilypad) Hooked!!\n" );
		hHook = ikhd->hHook;
		if (hHook) {
			if (code == HC_ACTION) {
				if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
					KBDLLHOOKSTRUCT* key = (KBDLLHOOKSTRUCT*) lParam;
					if (key->vkCode < 256) {
						for (int i=0; i<ikhd->pads[0][0].numBindings; i++) {
							if (ikhd->pads[0][0].bindings[i].controlIndex == key->vkCode) {
								return 1;
							}
						}
						if (ikhd->binding){
							ikhd->UpdateKey(key->vkCode, 1);
							return 1;
						}
					}
					if (config.vistaVolume) {
						if (key->vkCode == VK_VOLUME_DOWN) {
							SetVolume(config.volume-3);
							return 1;
						}
						if (key->vkCode == VK_VOLUME_UP) {
							SetVolume(config.volume+3);
							return 1;
						}
					}
				}
				else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
					KBDLLHOOKSTRUCT* key = (KBDLLHOOKSTRUCT*) lParam;
					if (ikhd->binding && key->vkCode < 256)
						ikhd->UpdateKey(key->vkCode, 0);
				}
			}
			return CallNextHookEx(hHook, code, wParam, lParam);
		}
	}
	return CallNextHookEx(hHook, code, wParam, lParam);
}

