/*
 *  psychlops_io_API_Win32.cpp
 *  Psychlops Standard Library (Win32)
 *
 *  Last Modified 2006/01/05 by Kenchi HOSOKAWA
 *  (C) 2005 Kenchi HOSOKAWA, Kazushi MARUYA and Takao SATO
 */

#include <string.h>
#include <stdio.h>

#include <windows.h>


#include "psychlops_io_API_Win32.h"
#define PSYCHLOPS_WINDOW_API_PLATFORM
#include "../../platform/psychlops_platform_selector.h"
#include "../../core/devices/psychlops_io_hid.h"
#include "../../core/ApplicationInterfaces/psychlops_code_exception.h"


namespace Psychlops {

	typedef HumanInterfaceDevice HID;

	HANDLE APIHIDProperties::io_thread;
	DWORD APIHIDProperties::io_threadID;
	HDC APIHIDProperties::the_display_;			//	Handle for dummy Device Context
	WNDCLASSEX APIHIDProperties::wcx;				//	Handle for dummy WindowClassEx
	HWND APIHIDProperties::hWnd;					//	Handle for dummy Window

	bool APIHIDProperties::refresh_needed = false;


	//  Event Based Keyboard-Accept Code
	void APIHIDProperties::initialize() {
        for(int i=0; i<4; i++) for(int j=0; j<keycnt_; j++) states_[i][j] = 0;
		bindKeyCode();
//		io_thread = CreateThread(NULL, 0, &setupListener, NULL, 0, &io_threadID);
	}
	void APIHIDProperties::finalize() {
//		io_thread = CreateThread(NULL, 0, &setupListener, NULL, 0, &io_threadID);
	}
/*	DWORD WINAPI APIHIDProperties::setupListener(LPVOID vdParam) {
		wcx.cbSize        = sizeof(WNDCLASSEX);
		wcx.style         = NULL;
		wcx.lpfnWndProc   = (WNDPROC)&(APIApplicationProperties::proc);
		wcx.cbClsExtra    = 0;
		wcx.cbWndExtra    = 0;
		wcx.hInstance     = APIApplicationProperties::startupinfo.hInstance_;
		wcx.hIcon         = NULL;
		wcx.hIconSm       = NULL;
		wcx.hCursor       = NULL;
		wcx.hbrBackground = NULL;
		wcx.lpszMenuName  = "Psychlops Input Listner";
		wcx.lpszClassName = "PsychlopsInputListner";
		if (!RegisterClassEx(&wcx)) throw Exception(typeid(APIHIDProperties), "API ERROR", "Failed to regist control message listner.");

		hWnd = CreateWindowEx( NULL, "PsychlopsInputListner", "Psychlops Dummy Window", NULL, 0,0,0,0, HWND_MESSAGE, NULL, wcx.hInstance, NULL);
		if(hWnd==NULL) throw Exception(typeid(APIHIDProperties), "API ERROR", "Failed to regist control message listner.");

		MSG msg;
		PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

		Keyboard::initialize();
		Mouse::initialize();

		SetThreadPriority(io_thread, THREAD_PRIORITY_LOWEST);

		loopListener();
	}*/
	void APIHIDProperties::startListener() {
//		SetThreadPriority(io_thread, THREAD_PRIORITY_BELOW_NORMAL);
	}
	void APIHIDProperties::stopListener() {
//		SetThreadPriority(io_thread, THREAD_PRIORITY_LOWEST);
	}
	void APIHIDProperties::listen() {
		MSG msg;
		while(0!=PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)){
		GetMessage(&msg, NULL, 0 ,0);
		switch( msg.message ) {
			case WM_SYSKEYDOWN:
				if ( msg.wParam==VK_F4 ) PostQuitMessage(0);
				break;
			case WM_MOUSEMOVE:
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_LBUTTONDOWN:
				HID::state[HID::pushed].button[Mouse::left.code] = true;
				HID::state[HID::pressed].button[Mouse::left.code] = true;
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_LBUTTONUP:
				HID::state[HID::released].button[Mouse::left.code] = true;
				HID::state[HID::pressed].button[Mouse::left.code] = false;
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_RBUTTONDOWN:
				HID::state[HID::pushed].button[Mouse::right.code] = true;
				HID::state[HID::pressed].button[Mouse::right.code] = true;
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_RBUTTONUP:
				HID::state[HID::released].button[Mouse::right.code] = true;
				HID::state[HID::pressed].button[Mouse::right.code] = false;
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_MBUTTONDOWN:
				HID::state[HID::pushed].button[Mouse::middle.code] = true;
				HID::state[HID::pressed].button[Mouse::middle.code] = true;
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_MBUTTONUP:
				HID::state[HID::released].button[Mouse::middle.code] = true;
				HID::state[HID::pressed].button[Mouse::middle.code] = false;
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_MOUSEHOVER:
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_MOUSELEAVE:
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_KEYDOWN:
				HID::state[HID::pushed].key[msg.wParam] = true;
				HID::state[HID::pressed].key[msg.wParam] = true;
				break;
			case WM_KEYUP:
				HID::state[HID::released].key[msg.wParam] = true;
				HID::state[HID::pressed].key[msg.wParam] = false;
				break;
			case WM_MOUSEWHEEL:
				Mouse::wheelDelta.y -= (short)HIWORD(msg.wParam);
				Mouse::uniX.val_ = (SHORT)LOWORD(msg.lParam);
				Mouse::uniY.val_ = (SHORT)HIWORD(msg.lParam);
				break;
			case WM_QUIT:
			case WM_CLOSE:
			case WM_DESTROY:
				ExitProcess(TRUE);
				break;
			default:
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				break;
		}
		}
		//update();
	}
	void APIHIDProperties::update()
	{
		listen();
	}
	/*
	void updateOld() {
		BYTE tmp;
		memcpy(reinterpret_cast<void *>(states_[HID::hadpressed]), reinterpret_cast<const void *>(states_[HID::pressed]), keycnt_);
		GetKeyboardState(states_[Keyboard::pressed]);
		for(int i=0; i<keycnt_; i++) {
			tmp = states_[Keyboard::pressed][i] ^ states_[Keyboard::hadpressed][i];
			states_[HID::pushed][i] |= tmp & states_[HID::pressed][i];
			states_[HID::released][i] |= tmp & states_[HID::hadpressed][i];
		}

		::POINT point;
		GetCursorPos(&point);
		Mouse::uniX.val_ = point.x;
		Mouse::uniY.val_ = point.y;
	}
	*/
	void APIHIDProperties::updatePointerPosition() {
		::POINT point;
		GetCursorPos(&point);
		Mouse::uniX.val_ = point.x;
		Mouse::uniY.val_ = point.y;
	}


	bool APIHIDProperties::get(Mouse::Button button, Mouse::ButtonState state) {
		APIHIDProperties::listen();
		bool val = HID::state[state].button[button.code];
		if(state!=Mouse::pressed) HID::state[(HID::ButtonState)state].button[button.code] = false;
		return val;
	}
	bool APIHIDProperties::get(Keyboard::Key key, Keyboard::KeyState state) {
		APIHIDProperties::listen();
		bool val = HID::state[state].key[keybinder_[key.code]];
		if(state!=Keyboard::pressed) HID::state[(HID::ButtonState)state].key[keybinder_[key.code]] = false;
		return val;
		/*
		bool tmp;
		if(state==Keyboard::pressed) {
			return pressedbit_ & states_[state][keybinder_[key.code]];
		}
		else {
			tmp = pressedbit_ & states_[state][keybinder_[key.code]];
			states_[state][keybinder_[key.code]] = 0;
			return tmp;
		}*/
	}
	void APIHIDProperties::setPointerPosition(int x, int y) {
		SetCursorPos(x,y);
	}

	struct Keyboard_ {
		enum KeyCode{
			one=0,two,three,four,five,six,seven,eight,nine,zero,
			a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,
			comma,period,slash,
			pad0,pad1,pad2,pad3,pad4,pad5,pad6,pad7,pad8,pad9,
			rtn,spc,esc,tab,up,down,left,right,
			//	key modulator
			shift=100, shift_r, ctrl, alt,
			//  not recommended
			hyphen=59,equal,backslash,caret,
			underscore,semicolon,colon,leftbracket,rightbracket,at,   // because these keys laid on different location in differnt keyboard-lacalization.
			padcomma,padperiod,padenter,padplus,padminus,padasterisk,padslash,padequal,padclear,	//  because these keys are OS-specific.
			nfer,xfer   // because these keys exist only on Japanese keyboard
		};
	};
	void APIHIDProperties::bindKeyCode() {
		keybinder_[Keyboard_::shift] = VK_SHIFT;
		keybinder_[Keyboard_::ctrl]  = 0x11;
		keybinder_[Keyboard_::alt]   = VK_MENU;


		keybinder_[Keyboard_::pad0] = 0x60;
		keybinder_[Keyboard_::pad1] = 0x61;
		keybinder_[Keyboard_::pad2] = 0x62;
		keybinder_[Keyboard_::pad3] = 0x63;
		keybinder_[Keyboard_::pad4] = 0x64;
		keybinder_[Keyboard_::pad5] = 0x65;
		keybinder_[Keyboard_::pad6] = 0x66;
		keybinder_[Keyboard_::pad7] = 0x67;
		keybinder_[Keyboard_::pad8] = 0x68;
		keybinder_[Keyboard_::pad9] = 0x69;
		keybinder_[Keyboard_::padslash] = 0x6F;
		keybinder_[Keyboard_::padplus] = 0x6B;
		keybinder_[Keyboard_::padminus] = 0x6D;
		keybinder_[Keyboard_::padasterisk] = 0x6A;

		//keybinder_[Keyboard_::padcomma] = 2;
		//keybinder_[Keyboard_::padcomma] = 128;
		//keybinder_[Keyboard_::padperiod] = 2;
		//keybinder_[Keyboard_::padperiod] = 33554432L;
		//keybinder_[Keyboard_::padenter] = 2;
		//keybinder_[Keyboard_::padenter] = 1048576L;

		//keybinder_[Keyboard_::padequal] = 2;
		//keybinder_[Keyboard_::padequal] = 512;
		//keybinder_[Keyboard_::padclear] = 2;
		//keybinder_[Keyboard_::padclear] = 2147483648UL;

		keybinder_[Keyboard_::one] = 0x31;
		keybinder_[Keyboard_::two] = 0x32;
		keybinder_[Keyboard_::three] = 0x33;
		keybinder_[Keyboard_::four] = 0x34;
		keybinder_[Keyboard_::five] = 0x35;
		keybinder_[Keyboard_::six] = 0x36;
		keybinder_[Keyboard_::seven] = 0x37;
		keybinder_[Keyboard_::eight] = 0x38;
		keybinder_[Keyboard_::nine] = 0x39;
		keybinder_[Keyboard_::zero] = 0x30;
		keybinder_[Keyboard_::hyphen] = 0xBD;
		keybinder_[Keyboard_::caret] = 1;
		keybinder_[Keyboard_::backslash] = 0xDC;

		keybinder_[Keyboard_::q] = 0x51;
		keybinder_[Keyboard_::w] = 0x57;
		keybinder_[Keyboard_::e] = 0x45;
		keybinder_[Keyboard_::r] = 0x52;
		keybinder_[Keyboard_::t] = 0x54;
		keybinder_[Keyboard_::y] = 0x59;
		keybinder_[Keyboard_::u] = 0x55;
		keybinder_[Keyboard_::i] = 0x49;
		keybinder_[Keyboard_::o] = 0x4F;
		keybinder_[Keyboard_::p] = 0x50;
		keybinder_[Keyboard_::at] = 33554432L;
		keybinder_[Keyboard_::leftbracket] = 0xDB;

		keybinder_[Keyboard_::a] = 0x41;
		keybinder_[Keyboard_::s] = 0x53;
		keybinder_[Keyboard_::d] = 0x44;
		keybinder_[Keyboard_::f] = 0x46;
		keybinder_[Keyboard_::g] = 0x47;
		keybinder_[Keyboard_::h] = 0x48;
		keybinder_[Keyboard_::j] = 0x4A;
		keybinder_[Keyboard_::k] = 0x4B;
		keybinder_[Keyboard_::l] = 0x4C;
		keybinder_[Keyboard_::semicolon] = 0xBA;
		keybinder_[Keyboard_::colon] = 0xBA;
		keybinder_[Keyboard_::rightbracket] = 0xDD;

		keybinder_[Keyboard_::z] = 0x5A;
		keybinder_[Keyboard_::x] = 0x58;
		keybinder_[Keyboard_::c] = 0x43;
		keybinder_[Keyboard_::v] = 0x56;
		keybinder_[Keyboard_::b] = 0x42;
		keybinder_[Keyboard_::n] = 0x4E;
		keybinder_[Keyboard_::m] = 0x4D;
		keybinder_[Keyboard_::comma] = 0xBC;
		keybinder_[Keyboard_::period] = 0xBE;
		keybinder_[Keyboard_::slash] = 0xBF;
		keybinder_[Keyboard_::underscore] = 64;

		keybinder_[Keyboard_::esc] = 0x1B;
		keybinder_[Keyboard_::rtn] = 0x0D;
		keybinder_[Keyboard_::spc] = 0x20;
		keybinder_[Keyboard_::up] = 0x26;
		keybinder_[Keyboard_::down] = 0x28;
		keybinder_[Keyboard_::left] = 0x25;
		keybinder_[Keyboard_::right] = 0x27;
	}

	unsigned long APIHIDProperties::keybinder_[keycnt_];
	BYTE APIHIDProperties::states_[4][keycnt_];


/*
	unsigned long APIKeyboardProperties::keybinder_[keycnt_];
	BYTE APIKeyboardProperties::states_[4][keycnt_];

	bool APIKeyboardProperties::get(Keyboard::Key key, Keyboard::KeyState state) {
APIHIDProperties::listen();
		bool tmp;
		if(state==Keyboard::pressed) {
			return pressedbit_ & states_[state][keybinder_[key.code]];
		}
		else {
			tmp = pressedbit_ & states_[state][keybinder_[key.code]];
			states_[state][keybinder_[key.code]] = 0;
			return tmp;
		}
	}

	void APIKeyboardProperties::update() {
		BYTE tmp;
		memcpy(reinterpret_cast<void *>(states_[Keyboard::hadpressed]), reinterpret_cast<const void *>(states_[Keyboard::pressed]), keycnt_);
		GetKeyboardState(states_[Keyboard::pressed]);
		for(int i=0; i<keycnt_; i++) {
			tmp = states_[Keyboard::pressed][i] ^ states_[Keyboard::hadpressed][i];
			states_[Keyboard::pushed][i] |= tmp & states_[Keyboard::pressed][i];
			states_[Keyboard::released][i] |= tmp & states_[Keyboard::hadpressed][i];
		}
	}

	void APIKeyboardProperties::refresh() {
		for(int i=0; i<keycnt_; i++) {
			states_[Keyboard::pushed][i] = 0;
			states_[Keyboard::released][i] = 0;
		}
	}



    void APIKeyboardProperties::initialize() {
        //memset(states_, 4*keycnt_, 0);
        for(int i=0; i<4; i++) for(int j=0; j<keycnt_; j++) states_[i][j] = 0;
        bindKeyCode();
    }


	struct Keyboard_ {
		enum KeyCode{
			one,two,three,four,five,six,seven,eight,nine,zero,
			a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,
			comma,period,slash,
			pad0,pad1,pad2,pad3,pad4,pad5,pad6,pad7,pad8,pad9,
			rtn,spc,esc,up,down,left,right,
			//	key modulator
			shift, ctrl, alt,
			//  not recommended
			hyphen,equal,backslash,caret,
			underscore,semicolon,colon,leftbracket,rightbracket,at,   // because these keys laid on different location in differnt keyboard-lacalization.
			padcomma,padperiod,padenter,padplus,padminus,padasterisk,padslash,padequal,padclear	//  because these keys are OS-specific.
		};
	};
	void APIKeyboardProperties::bindKeyCode() {
		keybinder_[Keyboard_::shift] = VK_SHIFT;
		keybinder_[Keyboard_::ctrl]  = 0x11;
		keybinder_[Keyboard_::alt]   = VK_MENU;


		keybinder_[Keyboard_::pad0] = 0x60;
		keybinder_[Keyboard_::pad1] = 0x61;
		keybinder_[Keyboard_::pad2] = 0x62;
		keybinder_[Keyboard_::pad3] = 0x63;
		keybinder_[Keyboard_::pad4] = 0x64;
		keybinder_[Keyboard_::pad5] = 0x65;
		keybinder_[Keyboard_::pad6] = 0x66;
		keybinder_[Keyboard_::pad7] = 0x67;
		keybinder_[Keyboard_::pad8] = 0x68;
		keybinder_[Keyboard_::pad9] = 0x69;
		keybinder_[Keyboard_::padslash] = 0x6F;
		keybinder_[Keyboard_::padplus] = 0x6B;
		keybinder_[Keyboard_::padminus] = 0x6D;
		keybinder_[Keyboard_::padasterisk] = 0x6A;

		//keybinder_[Keyboard_::padcomma] = 2;
		//keybinder_[Keyboard_::padcomma] = 128;
		//keybinder_[Keyboard_::padperiod] = 2;
		//keybinder_[Keyboard_::padperiod] = 33554432L;
		//keybinder_[Keyboard_::padenter] = 2;
		//keybinder_[Keyboard_::padenter] = 1048576L;

		//keybinder_[Keyboard_::padequal] = 2;
		//keybinder_[Keyboard_::padequal] = 512;
		//keybinder_[Keyboard_::padclear] = 2;
		//keybinder_[Keyboard_::padclear] = 2147483648UL;

		keybinder_[Keyboard_::one] = 0x31;
		keybinder_[Keyboard_::two] = 0x32;
		keybinder_[Keyboard_::three] = 0x33;
		keybinder_[Keyboard_::four] = 0x34;
		keybinder_[Keyboard_::five] = 0x35;
		keybinder_[Keyboard_::six] = 0x36;
		keybinder_[Keyboard_::seven] = 0x37;
		keybinder_[Keyboard_::eight] = 0x38;
		keybinder_[Keyboard_::nine] = 0x39;
		keybinder_[Keyboard_::zero] = 0x30;
		keybinder_[Keyboard_::hyphen] = 0xBD;
		keybinder_[Keyboard_::caret] = 1;
		keybinder_[Keyboard_::backslash] = 0xDC;

		keybinder_[Keyboard_::q] = 0x51;
		keybinder_[Keyboard_::w] = 0x57;
		keybinder_[Keyboard_::e] = 0x45;
		keybinder_[Keyboard_::r] = 0x52;
		keybinder_[Keyboard_::t] = 0x54;
		keybinder_[Keyboard_::y] = 0x59;
		keybinder_[Keyboard_::u] = 0x55;
		keybinder_[Keyboard_::i] = 0x49;
		keybinder_[Keyboard_::o] = 0x4F;
		keybinder_[Keyboard_::p] = 0x50;
		keybinder_[Keyboard_::at] = 33554432L;
		keybinder_[Keyboard_::leftbracket] = 0xDB;

		keybinder_[Keyboard_::a] = 0x41;
		keybinder_[Keyboard_::s] = 0x53;
		keybinder_[Keyboard_::d] = 0x44;
		keybinder_[Keyboard_::f] = 0x46;
		keybinder_[Keyboard_::g] = 0x47;
		keybinder_[Keyboard_::h] = 0x48;
		keybinder_[Keyboard_::j] = 0x4A;
		keybinder_[Keyboard_::k] = 0x4B;
		keybinder_[Keyboard_::l] = 0x4C;
		keybinder_[Keyboard_::semicolon] = 0xBA;
		keybinder_[Keyboard_::colon] = 0xBA;
		keybinder_[Keyboard_::rightbracket] = 0xDD;

		keybinder_[Keyboard_::z] = 0x5A;
		keybinder_[Keyboard_::x] = 0x58;
		keybinder_[Keyboard_::c] = 0x43;
		keybinder_[Keyboard_::v] = 0x56;
		keybinder_[Keyboard_::b] = 0x42;
		keybinder_[Keyboard_::n] = 0x4E;
		keybinder_[Keyboard_::m] = 0x4D;
		keybinder_[Keyboard_::comma] = 0xBC;
		keybinder_[Keyboard_::period] = 0xBE;
		keybinder_[Keyboard_::slash] = 0xBF;
		keybinder_[Keyboard_::underscore] = 64;

		keybinder_[Keyboard_::esc] = 0x1B;
		keybinder_[Keyboard_::rtn] = 0x0D;
		keybinder_[Keyboard_::spc] = 0x20;
		keybinder_[Keyboard_::up] = 0x26;
		keybinder_[Keyboard_::down] = 0x28;
		keybinder_[Keyboard_::left] = 0x25;
		keybinder_[Keyboard_::right] = 0x27;
	}



	bool APIMouseProperties::buttonstate_[4][3];
	POINT APIMouseProperties::position_;


	void APIMouseProperties::getPosition(int &x, int &y) {
//APIInputProperties::listen();
		x = position_.x;
		y = position_.y;
	}
	void APIMouseProperties::setPosition(int x, int y) {
		SetCursorPos(x,y);
	}

	void APIMouseProperties::initialize() {
		refresh();
	}

	void APIMouseProperties::update() {
		GetCursorPos(&position_);
	}
	void APIMouseProperties::refresh() {
		for(int i=0; i<4; i++) for(int j=0; j<3; j++) buttonstate_[i][j] = false;
	}

	bool APIMouseProperties::get(Mouse::Button button, Mouse::ButtonState state) {
APIHIDProperties::listen();
		bool val = buttonstate_[state][button.code];
		switch(state) {
			case Mouse::pushed:
			case Mouse::released:
				buttonstate_[state][button.code] = false;
				break;
		}
		return val;
	}
*/

}	/*	<- namespace Psycholops 	*/
