/*
	Twilight Prophecy 3D/Multimedia SDK
	A multi-platform development system for virtual reality and multimedia.

	Copyright (C) 1997-2001 by Twilight 3D Finland Oy Ltd.

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program 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 this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	Please read the file LICENSE.TXT for additional details.


	source:
		win32 keyboard

	revision history:
		???/??/1997 - Thomas Mannfred Carlsson - initial revision
		Feb/06/2001 - Jukka Liimatta - renaissance build
*/
#include <prcore/prcore.hpp>
using namespace prcore;



//////////////////////////////////////////////////////
// statics                                         //
////////////////////////////////////////////////////

static char kbuffer[256];
static BYTE pbuffer[256];
static const int KCHARMAX = 256;
static char kcharbuf[KCHARMAX];
static int kcharsize = 0;

LRESULT CALLBACK kbStaticHook(int ncode, WPARAM wparam, LPARAM lparam);
LRESULT CALLBACK sysStaticHook(int ncode, WPARAM wparam, LPARAM lparam);

static HHOOK kbStaticHookId = 0;
static HHOOK sysStaticHookId = 0;
static long kbStaticInitCount = 0;
static uint32 pkbfunc = NULL;

static char remap[] =
{
	0x00,	//
	0x01,	// KEYCODE_ESC
	0x02,	// KEYCODE_1
	0x03,	// KEYCODE_2
	0x04,	// KEYCODE_3
	0x05,	// KEYCODE_4
	0x06,	// KEYCODE_5
	0x07,	// KEYCODE_6
	0x08,	// KEYCODE_7
	0x09,	// KEYCODE_8
	0x0a,	// KEYCODE_9
	0x0b,	// KEYCODE_0
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x1e,	// KEYCODE_A
	0x30,	// KEYCODE_B
	0x2e,	// KEYCODE_C
	0x20,	// KEYCODE_D
	0x12,	// KEYCODE_E
	0x21,	// KEYCODE_F
	0x22,	// KEYCODE_G
	0x23,	// KEYCODE_H
	0x17,	// KEYCODE_I
	0x24,	// KEYCODE_J
	0x25,	// KEYCODE_K
	0x26,	// KEYCODE_L
	0x32,	// KEYCODE_M
	0x31,	// KEYCODE_N
	0x18,	// KEYCODE_O
	0x19,	// KEYCODE_P
	0x10,	// KEYCODE_Q
	0x13,	// KEYCODE_R
	0x1f,	// KEYCODE_S
	0x14,	// KEYCODE_T
	0x16,	// KEYCODE_U
	0x2f,	// KEYCODE_V
	0x11,	// KEYCODE_W
	0x2d,	// KEYCODE_X
	0x15,	// KEYCODE_Y
	0x2c,	// KEYCODE_Z
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x3b,	// KEYCODE_F1
	0x3c,	// KEYCODE_F2
	0x3d,	// KEYCODE_F3
	0x3e,	// KEYCODE_F4
	0x3f,	// KEYCODE_F5
	0x40,	// KEYCODE_F6
	0x41,	// KEYCODE_F7
	0x42,	// KEYCODE_F8
	0x43,	// KEYCODE_F9
	0x44,	// KEYCODE_F10
	0x57,	// KEYCODE_F11
	0x58,	// KEYCODE_F12
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x52,	// KEYCODE_NUMPAD0
	0x4f,	// KEYCODE_NUMPAD1
	0x50,	// KEYCODE_NUMPAD2
	0x51,	// KEYCODE_NUMPAD3
	0x4b,	// KEYCODE_NUMPAD4
	0x4c,	// KEYCODE_NUMPAD5
	0x4d,	// KEYCODE_NUMPAD6
	0x47,	// KEYCODE_NUMPAD7
	0x48,	// KEYCODE_NUMPAD8
	0x49,	// KEYCODE_NUMPAD9
	0x45,	// KEYCODE_NUMLOCK
	0x35,	// KEYCODE_DIVIDE
	0x37,	// KEYCODE_MULTIPLY
	0x4a,	// KEYCODE_SUBTRACT
	0x4e,	// KEYCODE_ADDITION
	0x1c,	// KEYCODE_ENTER
	0x53,	// KEYCODE_DECIMAL
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x0e,	// KEYCODE_BACKSPACE
	0x0f,	// KEYCODE_TAB
	0x1c,	// KEYCODE_RETURN
	0x1d,	// KEYCODE_L_CTRL
	0x5a,	// KEYCODE_R_CTRL
	0x2a,	// KEYCODE_L_SHIFT
	0x36,	// KEYCODE_R_SHIFT
	0x38,	// KEYCODE_L_ALT
	0x5c,	// KEYCODE_R_ALT
	0x39,	// KEYCODE_SPACE
	0x3a,	// KEYCODE_CAPSLOCK
	0x47,	// KEYCODE_PRNT_SCRN
	0x57,	// KEYCODE_SCROLL_LOCK
	0x49,	// KEYCODE_PGUP
	0x51,	// KEYCODE_PGDN
	0x52,	// KEYCODE_INS
	0x53,	// KEYCODE_DEL
	0x47,	// KEYCODE_HOME
	0x4f,	// KEYCODE_END
	0x4b,	// KEYCODE_LEFT
	0x4d,	// KEYCODE_RIGHT
	0x48,	// KEYCODE_UP
	0x50,	// KEYCODE_DOWN
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
	0x00,	//
};


//////////////////////////////////////////////////////
// Keyboard                                        //
////////////////////////////////////////////////////

Keyboard::Keyboard()
{
	kbStaticInitCount++;
	if ( ( !kbStaticHookId ) && ( kbStaticInitCount <= 1 ) )
	{
		pkbfunc = 0;

		memset( kbuffer, 0, sizeof( kbuffer ) );
		if ( !GetKeyboardState( (PBYTE)pbuffer ) ) memset( pbuffer, 0, sizeof( pbuffer ) );

		kbStaticHookId = SetWindowsHookEx( WH_KEYBOARD,
			(HOOKPROC) kbStaticHook, 0, GetCurrentThreadId() );

		if ( !kbStaticHookId ) 
		{ 
			return; 
		}

		sysStaticHookId = SetWindowsHookEx( WH_CBT,
			(HOOKPROC) sysStaticHook, 0, GetCurrentThreadId() );

		if ( !sysStaticHookId ) 
		{ 
			return; 
		}
	}
}


Keyboard::~Keyboard()
{
	if ( kbStaticInitCount > 0 )
	{
		kbStaticInitCount--;
	}

	if ( ( kbStaticHookId ) && ( kbStaticInitCount == 0 ) )
	{
		UnhookWindowsHookEx( kbStaticHookId );
		kbStaticHookId = 0;

		if ( sysStaticHookId )
		{
			UnhookWindowsHookEx( sysStaticHookId );
			sysStaticHookId = 0;
		}

		pkbfunc = 0;
	}
}


int Keyboard::GetButtonCount() const
{
	return 128;
}


bool Keyboard::GetButton(int code)
{
	return kbuffer[ remap[code] ] != 0;
}


static uint16 tkbuf[4] = {0, 0, 0, 0};
LRESULT CALLBACK kbStaticHook(int ncode, WPARAM wparam, LPARAM lparam)
{

	if ( ncode < 0 )
	{
		return (int)CallNextHookEx( kbStaticHookId, ncode, wparam, lparam );
	}

	if ( ( lparam & 0x80000000 ) )
	{
		kbuffer[(char) ( ( lparam >> 16 ) & 127 )] = 0;
		pbuffer[wparam & 0x0ff] &= ~0x080;

		if ( pkbfunc )
		{
			int ascres = ToAscii( wparam, ( lparam >> 16 ) & 0x07f, (PBYTE)pbuffer, (LPWORD)tkbuf, 0 );
			if ( ascres > 0 )
			{
				uint16 ascii;
				if ( ascres == 2 ) 	ascii = tkbuf[1];
				else				ascii = tkbuf[0];
			}
			else
			{
			}
		}

		return( 1 );
	}
	else
	{
		pbuffer[wparam & 0x0ff] ^= 0x01;
		pbuffer[wparam & 0x0ff] |= 0x080;
		kbuffer[(char) ( ( lparam >> 16 ) & 127 )] = 1;

		if ( pkbfunc )
		{
			int ascres = ToAscii( wparam, ( lparam >> 16 ) & 0x07f, (PBYTE)pbuffer, (LPWORD)tkbuf, 0 );
			if ( ascres > 0 )
			{
				uint16 ascii;
				if ( ascres == 2 ) 	ascii = tkbuf[1];
				else				ascii = tkbuf[0];
			}
			else
			{
			}
		}
		return( 1 );
	}

	// if we passed over control, these should be filtered out..
	if ( ( WORD(lparam >> 16 ) & KF_ALTDOWN ) )
	{
		return( 1 );
	}

	if ( ( GetAsyncKeyState( VK_CONTROL ) & 0x80000000 ) )
	{
		return( 1 );
	}
}


LRESULT CALLBACK sysStaticHook(int ncode, WPARAM wparam, LPARAM lparam)
{
	return (int)CallNextHookEx( sysStaticHookId, ncode, wparam, lparam );
}
