#include "pch.hpp"
//
//============================================================
//
// Contents:  Keyboard handling routines
//
// Owner   :  AndyG
//
// All Rights reserved worldwide (c) Microsoft - AAS Games                           
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               
//============================================================
//
#include "ControlManager.hpp"
#include "DirectX.hpp"
#include "Windows.hpp"
#include "ErrorHandler.hpp"
#include "Debugger.hpp"
#include "Language.h"

extern DWORD gLanguageDLL;

DWORD KeyBoardBuffer[256];
DWORD KeyCurrent;				// Where the next key to be read is
DWORD KeyPressed;				// Where the next key pressed will go
//
// Same as above, but for 'inside debugger'
//
DWORD dbKeyBoardBuffer[256];
DWORD dbKeyCurrent;				// Where the next key to be read is
DWORD dbKeyPressed;				// Where the next key pressed will go

//
// States of the keyboard keys (stored as bits)
//
BYTE KeyStates[32];
BYTE OldKeyStates[32];

DWORD gNumLockMode = 2;
DWORD gCapLockMode = 2;
DWORD gScrollLockMode = 2;

DWORD LastWMDown=0;				// Last WM_KEYDOWN message scan code

bool (__stdcall *g_pfnCTCL_HandleKey)(DWORD wParam, bool bDown) = NULL;


void AddKeyEvent(DWORD KeyEvent)
{
	if( ((KeyPressed+1)&255)!=KeyCurrent )
	{
		KeyBoardBuffer[KeyPressed] = KeyEvent;
		KeyPressed=(KeyPressed+1)&255;
	}
	// else shouldn't we assert or something?
}


//
// Called from WinProc
//
void DealWithKey( DWORD wParam, DWORD lParam )
{
    if (g_pfnCTCL_HandleKey) { // jcem
        if (!g_pfnCTCL_HandleKey(wParam, true))
            return;
    }
//
// Update keyboard state map
//
	DWORD Offset=wParam>>3;
	DWORD Bit=1<<(wParam&7);
	KeyStates[Offset]|=Bit;
//
// Do not put mouse buttons in keyboard buffer
//
	if( wParam<6 && lParam==0 )
		return;
//
// Update keyboard buffer
//
	if( LastWMDown )
	{
		if( DebuggerActive )
		{
			if( ((dbKeyPressed+1)&255)!=dbKeyCurrent )
			{
				dbKeyBoardBuffer[dbKeyPressed] = LastWMDown;
				dbKeyPressed=(dbKeyPressed+1)&255;
			}
		}
		if( !DebuggerActive || (DebuggerActive && gControlsActive) )
		{
			AddKeyEvent(LastWMDown);
		}
	}

	if(7==GetKeyboardType(0) && 0xd==(GetKeyboardType(1)>>8))
		LastWMDown=(wParam&255)<<8;
	else
		LastWMDown=((wParam&255)+((lParam&0x1000000)>>16))<<8;
}

//
// Called from WinProc
//
void DoKeyReleased( DWORD KeyCode )
{
    if (g_pfnCTCL_HandleKey) { // jcem
        if (!g_pfnCTCL_HandleKey(KeyCode, false))
            return;
    }
	DWORD Offset=KeyCode>>3;
	DWORD Bit=1<<(KeyCode&7);
	KeyStates[Offset]&=~Bit;
	if( 0==(OldKeyStates[Offset]&Bit) )		// If released in a single frame, make sure 'RELEASED' is returned
		OldKeyStates[Offset]|=Bit;
}


//
// Called before windows messages read
//
void SaveOldKeyState()
{
	memcpy( OldKeyStates, KeyStates, sizeof(OldKeyStates) );
}



//
// Clear keyboard buffers
//
void CMCreateKeyboard()
{
	memset( KeyStates, 0, sizeof(KeyStates) );
	memset( OldKeyStates, 0, sizeof(OldKeyStates) );
} 



//
// Examine this frame and last frame, return FREE, PRESSED, HELD or RELEASED
//
gosEnum_KeyStatus GetStatus( gosEnum_KeyIndex index )
{
//	if( !gGotFocus || !gActive )
//		return KEY_FREE;

	DWORD Offset=index>>3;
	DWORD Bit=1<<(index&7);

	if( (KeyStates[Offset]&Bit) )
	{
		if( (OldKeyStates[Offset]&Bit) )
			return KEY_HELD;
		return KEY_PRESSED;
	}

	if( (OldKeyStates[Offset]&Bit) )
		return KEY_RELEASED;

	return KEY_FREE;
}



//
// Describe the key code passed as an ASCII string
//
char* __stdcall gos_DescribeKey( DWORD Key )
{
	static char KeyName[64];

	if( (Key>>8)==1 )
		return gos_GetResourceString(gLanguageDLL, GOS_CONTROLS_LEFTMOUSE);
	if( (Key>>8)==2 )
		return gos_GetResourceString(gLanguageDLL, GOS_CONTROLS_RIGHTMOUSE);
	if( (Key>>8)==4 )
		return gos_GetResourceString(gLanguageDLL, GOS_CONTROLS_MIDDLEMOUSE);
	
	if( 7==GetKeyboardType(0) && 0xd==(GetKeyboardType(1)>>8) )
	{
		GetKeyNameText((MapVirtualKey((Key>>8)&0xff,0)<<16)+1, KeyName, 64);
	}
	else
	{
		GetKeyNameText(((MapVirtualKey((Key>>8)&0xff,0) + ((Key>>8)&0x100))<<16)+1, KeyName, 64);
	}

	return KeyName;
}





// CLIENT VISIBLE
// Returns TRUE if the key is currently held down, FALSE otherwise.
gosEnum_KeyStatus __stdcall gos_GetKeyStatus(gosEnum_KeyIndex index)
{
	if( 1==LogMode )
	{
//
// Playing back a log file
//
		if( InDebugger )
		{
			return GetStatus( index );
		}
		return (gosEnum_KeyStatus)ReadBitStream(2,Log_Keypress);
	}
	else
//
// Recording a log file
//
	{
		if( InDebugger )
		{
			return GetStatus( index );
		}

		gosEnum_KeyStatus tmp=GetStatus( index );

		if( DebuggerActive && !InDebugger && !gControlsActive )
		{
			tmp=KEY_FREE;
		}

		if( !InDebugger )
		{
			SPEW(( "GameOS_LogFile", "gos_GetKeyStatus(0x%x)",index ));
			WriteBitStream( tmp, 2, Log_Keypress );
		}

		return tmp;
	}
}



//////////////////////////////////////////////////////////////////////////////////
// Get extended keys from keyboard.
//
// NULL until a key is pressed
// Low byte is ASCII key values - or 0 when high byte is an extended keycode that is not ASCII
// High byte is a 'gosEnum_KeyIndex' code such as KEY_LEFT )
//
// For example cursor left is		KEY_LEFT<<8
// For example the enter key is		KEY_ENTER<<8 + 13
// For example the 'a' key is		KEY_A<<8 + 'a'
//
// There is an extra bit that may be added on some keyboards to signify an 'extended key'. This bit
// is require to display the correct key name. Applications should only use this bit for the
// gos_DescribeKey function. This extra bit is bit 16, i.e. 0x10000 - This bit SHOULD be masked off
// unless you are going to use the value for gos_DescribeKey.
//
// If Environment.ButtonsAsKeys is set, then gos_GetKey will also get button presses.  The high byte
// of the dword will indicate the type of device (KEYDEV_KEYBOARD or KEYDEV_JOYSTICK); the second
// byte will indicate the ID of the device (always 0 [or 1 for extended keys] for keyboard), ie
// joystick #1 or #2, etc., and the third byte will indicate the button ID on the device.  The low
// byte will always be zero (since these buttons never map to ASCII).  For example, a return value of
// 0x01020300 is button 3 on joystick #2.
//
DWORD __stdcall gos_GetKey()
{
	DWORD tmp=0;

	if( 1==LogMode )
	{
//
// Playing back a log file
//
		if( InDebugger )
		{
			if( dbKeyCurrent!=dbKeyPressed )
			{
				tmp=dbKeyBoardBuffer[dbKeyCurrent];
				dbKeyCurrent=(dbKeyCurrent+1)&255;
			}
			return tmp;
		}
		tmp=ReadBitStream(32,Log_GetKey);
		return tmp;
	}
	else
	{
//
// Recording a log file
//
		if( InDebugger )
		{
			if( dbKeyCurrent!=dbKeyPressed )
			{
				tmp=dbKeyBoardBuffer[dbKeyCurrent];
				dbKeyCurrent=(dbKeyCurrent+1)&255;
			}
			return tmp;
		}
		
		if( DebuggerActive && !InDebugger && !gControlsActive || KeyCurrent==KeyPressed )
		{
			tmp=0;
		}
		else
		{
			tmp=KeyBoardBuffer[KeyCurrent];
			KeyCurrent=(KeyCurrent+1)&255;
		}

		if( !InDebugger )
		{
			SPEW(( "GameOS_LogFile", "gos_GetKey()" ));
			WriteBitStream( tmp, 32, Log_GetKey );
		}

		return tmp;
	}
}




// CLIENT VISIBLE
// Flush the keyboard buffer (zero it out)
void __stdcall gos_KeyboardFlush()
{
	if( InDebugger )
		dbKeyPressed=dbKeyCurrent=0;
	else
		KeyPressed=KeyCurrent=0;
}


void SetExtended( BYTE vk, BOOL bState )
{
	gosASSERT(vk == VK_NUMLOCK || vk == VK_CAPITAL || vk == VK_SCROLL);
	//
	// This code seems to be causing problems and doesn't always work reliably.
	//
	return;

	OSVERSIONINFO osinfo;
	osinfo.dwOSVersionInfoSize=sizeof(osinfo);
	GetVersionEx( &osinfo );


	if (osinfo.dwPlatformId != VER_PLATFORM_WIN32_NT)
	{
		// win95/98 handle it differently
		BYTE states[256];
		GetKeyboardState(states);
		states[vk] = (BYTE) bState;
		SetKeyboardState(states);
	}
	else
	{
		int keyState = GetKeyState(vk);
		keyState &= 1;
		if( (bState && !keyState) ||
			(!bState && keyState) )
		{
			// Simulate a key release for NT
			keybd_event( vk, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0 );
			keybd_event( vk, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
		}
	}
}








