/*
AppleWin : An Apple //e emulator for Windows

Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski

AppleWin 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.

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

/* Description: Keyboard emulation
 *
 * Author: Various
 */

/* Adaptation for SDL and POSIX (l) by beom beotiger, Nov-Dec 2007 */


#include "stdafx.h"
//#pragma  hdrstop

static bool g_bKeybBufferEnable = false;

#define KEY_OLD

/*static BYTE asciicode[2][10] = {
	{0x08,0x0D,0x15,0x2F,0x00,0x00,0x00,0x00,0x00,0x00},
	{0x08,0x0B,0x15,0x0A,0x00,0x00,0x00,0x00,0x00,0x7F}
};	// Convert PC arrow keys to Apple keycodes*/

/*static*/ bool  g_bShiftKey = false;
/*static*/ bool  g_bCtrlKey  = false;
/*static*/ bool  g_bAltKey   = false;
static bool  g_bCapsLock = true;
static int   lastvirtkey     = 0;	// Current PC keycode
static BYTE  keycode         = 0;	// Current Apple keycode
static DWORD keyboardqueries = 0;

#ifdef KEY_OLD
// Original
static BOOL  keywaiting      = 0;
#else
// Buffered key input:
// - Needed on faster PCs where aliasing occurs during short/fast bursts of 6502 code.
// - Keyboard only sampled during 6502 execution, so if it's run too fast then key presses will be missed.
const int KEY_BUFFER_MIN_SIZE = 1;
const int KEY_BUFFER_MAX_SIZE = 2;
static int g_nKeyBufferSize = KEY_BUFFER_MAX_SIZE;	// Circ key buffer size
static int g_nNextInIdx = 0;
static int g_nNextOutIdx = 0;
static int g_nKeyBufferCnt = 0;

static struct
{
	int nVirtKey;
	BYTE nAppleKey;
} g_nKeyBuffer[KEY_BUFFER_MAX_SIZE];
#endif

static BYTE g_nLastKey = 0x00;

//
// ----- ALL GLOBALLY ACCESSIBLE FUNCTIONS ARE BELOW THIS LINE -----
//

//===========================================================================

void KeybReset()
{
#ifdef KEY_OLD
	keywaiting = 0;
#else
	g_nNextInIdx = 0;
	g_nNextOutIdx = 0;
	g_nKeyBufferCnt = 0;
	g_nLastKey = 0x00;

	g_nKeyBufferSize = g_bKeybBufferEnable ? KEY_BUFFER_MAX_SIZE : KEY_BUFFER_MIN_SIZE;
#endif
}

//===========================================================================

//void KeybSetBufferMode(bool bNewKeybBufferEnable)
//{
//	if(g_bKeybBufferEnable == bNewKeybBufferEnable)
//		return;
//
//	g_bKeybBufferEnable = bNewKeybBufferEnable;
//	KeybReset();
//}
//
//bool KeybGetBufferMode()
//{
//	return g_bKeybBufferEnable;
//}

//===========================================================================
bool KeybGetAltStatus ()
{
	return g_bAltKey;
}

//===========================================================================
bool KeybGetCapsStatus ()
{
	return g_bCapsLock;
}

//===========================================================================
bool KeybGetCtrlStatus ()
{
	return g_bCtrlKey;
}

//===========================================================================
bool KeybGetShiftStatus ()
{
	return g_bShiftKey;
}

//===========================================================================
void KeybUpdateCtrlShiftStatus()
{
//	g_bShiftKey = (GetKeyState( VK_SHIFT  ) & KF_UP) ? true : false; // 0x8000 KF_UP
//	g_bCtrlKey  = (GetKeyState( VK_CONTROL) & KF_UP) ? true : false;
//	g_bAltKey   = (GetKeyState( VK_MENU   ) & KF_UP) ? true : false;
	Uint8 *keys;
	keys = SDL_GetKeyState(NULL);

	g_bShiftKey = (keys[SDLK_LSHIFT] | keys[SDLK_RSHIFT]); // 0x8000 KF_UP   SHIFT
	g_bCtrlKey  = (keys[SDLK_LCTRL]  | keys[SDLK_RCTRL]);	// CTRL
	g_bAltKey   = (keys[SDLK_LALT]   | keys[SDLK_RALT]);	// ALT
}

//===========================================================================
BYTE KeybGetKeycode()		// Used by MemCheckPaging() & VideoCheckMode()
{
	return keycode;
}

//===========================================================================
DWORD KeybGetNumQueries ()	// Used in determining 'idleness' of Apple system
{
	DWORD result = keyboardqueries;
	keyboardqueries = 0;
	return result;
}

//===========================================================================
void KeybQueueKeypress (int key, BOOL bASCII)
{
//	static bool bFreshReset; - do not use

	if (bASCII == ASCII)
	{
/*		if (bFreshReset && key == 0x03)
		{
			bFreshReset = 0;
			return; // Swallow spurious CTRL-C caused by CTRL-BREAK
		}
		bFreshReset = 0;*/
		if (key > 0x7F) return;
// Conver SHIFTed keys to their secondary values
// may be this is straitfoward method, but it seems to be working. What else we need?? --bb
		KeybUpdateCtrlShiftStatus();
		if(g_bShiftKey) 		// SHIFT is pressed
			switch(key) {
				case '1': key = '!'; break;
				case '2': key = '@'; break;
				case '3': key = '#'; break;
				case '4': key = '$'; break;
				case '5': key = '%'; break;
				case '6': key = '^'; break;
				case '7': key = '&'; break;
				case '8': key = '*'; break;
				case '9': key = '('; break;
				case '0': key = ')'; break;
				case '`': key = '~'; break;
				case '-': key = '_'; break;
				case '=': key = '+'; break;
				case '\\': key = '|'; break;
				case '[': key = '{'; break;
				case ']': key = '}'; break;
				case ';': key = ':'; break;
				case '\'': key = '"'; break;
				case ',': key = '<'; break;
				case '.': key = '>'; break;
				case '/': key = '?'; break;
				default: 	     break;
			}
		else if (g_bCtrlKey) {
			if(key >= SDLK_a && key <= SDLK_z) key = key - SDLK_a + 1;
			else switch(key) {
				case '\\': key = 28; break;
				case '[' : key = 27; break;
				case ']' : key = 29; break;
				case SDLK_RETURN: key = 10; break;

				default: break;
			}
		}


		if (!IS_APPLE2)
		{
			if (g_bCapsLock && (key >= 'a') && (key <='z'))
				keycode = key - 32;
			else
				keycode = key;
		}
		else
		{
			if (key >= '`')
				keycode = key - 32;
			else
				keycode = key;
		}
		lastvirtkey = key;
	}
	else
	{
/*		if ((key == VK_CANCEL) && (GetKeyState(VK_CONTROL) < 0)) - implement in Frame.cpp
		{
			// Ctrl+Reset
			if (!IS_APPLE2)
				MemResetPaging();

			DiskReset();
			KeybReset();
			if (!IS_APPLE2)
				VideoResetState();	// Switch Alternate char set off
			MB_Reset();

#ifndef KEY_OLD
			g_nNextInIdx = g_nNextOutIdx = g_nKeyBufferCnt = 0;
#endif

			CpuReset();
			bFreshReset = 1;
			return;
		}
*/
/* 	No pasting??? Ye-e-e-e-et! */
// 		if ((key == VK_INSERT) && (GetKeyState(VK_SHIFT) < 0))
// 		{
// 			// Shift+Insert
// 			ClipboardInitiatePaste();
// 			return;
// 		}

// 		if (!((key >= VK_LEFT) && (key <= VK_DELETE) && asciicode[IS_APPLE2 ? 0 : 1][key - VK_LEFT]))
// 			return;
// 		keycode = asciicode[IS_APPLE2 ? 0 : 1][key - VK_LEFT];		// Convert to Apple arrow keycode
// 		lastvirtkey = key;
// 		{0x08,0x0D,0x15,0x2F,0x00,0x00,0x00,0x00,0x00,0x00}, - good old APPLE2
// 		{0x08,0x0B,0x15,0x0A,0x00,0x00,0x00,0x00,0x00,0x7F}

		if(IS_APPLE2)
			switch(key) {
				case SDLK_LEFT: keycode = 0x08; break;
				case SDLK_UP:	keycode = 0x0D; break;
				case SDLK_RIGHT:keycode = 0x15; break;
				case SDLK_DOWN: keycode = 0x2F; break;
				case SDLK_DELETE:keycode = 0x00;break;
				default: return;
			}
		else
			switch(key) {
				case SDLK_LEFT: keycode = 0x08; break;
				case SDLK_UP:	keycode = 0x0B; break;
				case SDLK_RIGHT:keycode = 0x15; break;
				case SDLK_DOWN: keycode = 0x0A; break;
				case SDLK_DELETE:keycode = 0x7F;break;
				default: return;
			}
		lastvirtkey = key;
	}


#ifdef KEY_OLD
	keywaiting = 1;
#else
	bool bOverflow = false;

	if(g_nKeyBufferCnt < g_nKeyBufferSize)
		g_nKeyBufferCnt++;
	else
		bOverflow = true;

	g_nKeyBuffer[g_nNextInIdx].nVirtKey = lastvirtkey;
	g_nKeyBuffer[g_nNextInIdx].nAppleKey = keycode;
	g_nNextInIdx = (g_nNextInIdx + 1) % g_nKeyBufferSize;

	if(bOverflow)
		g_nNextOutIdx = (g_nNextOutIdx + 1) % g_nKeyBufferSize;
#endif
}

//===========================================================================

/*static HGLOBAL hglb = NULL;
static LPTSTR lptstr = NULL;
static bool g_bPasteFromClipboard = false;
static bool g_bClipboardActive = false;*/
/*
void ClipboardInitiatePaste()
{
	if (g_bClipboardActive)
		return;

	g_bPasteFromClipboard = true;
}

static void ClipboardDone()
{
	if (g_bClipboardActive)
	{
		g_bClipboardActive = false;
		GlobalUnlock(hglb);
		CloseClipboard();
	}
}

static void ClipboardInit()
{
	ClipboardDone();

	if (!IsClipboardFormatAvailable(CF_TEXT))
		return;

	if (!OpenClipboard(g_hFrameWindow))
		return;

	hglb = GetClipboardData(CF_TEXT);
	if (hglb == NULL)
	{
		CloseClipboard();
		return;
	}

	lptstr = (char*) GlobalLock(hglb);
	if (lptstr == NULL)
	{
		CloseClipboard();
		return;
	}

	g_bPasteFromClipboard = false;
	g_bClipboardActive = true;
}

static char ClipboardCurrChar(bool bIncPtr)
{
	char nKey;
	int nInc = 1;

	if((lptstr[0] == 0x0D) && (lptstr[1] == 0x0A))
	{
		nKey = 0x0D;
		nInc = 2;
	}
	else
	{
		nKey = lptstr[0];
	}

	if(bIncPtr)
		lptstr += nInc;

	return nKey;
}*/

//===========================================================================

BYTE /*__stdcall */KeybReadData (WORD, WORD, BYTE, BYTE, ULONG)
{
	keyboardqueries++;

// 	if(g_bPasteFromClipboard)
// 		ClipboardInit();
//
// 	if(g_bClipboardActive)
// 	{
// 		if(*lptstr == 0)
// 			ClipboardDone();
// 		else
// 			return 0x80 | ClipboardCurrChar(false);
// 	}

	//

#ifdef KEY_OLD
	return keycode | (keywaiting ? 0x80 : 0);
#else
	BYTE nKey = g_nKeyBufferCnt ? 0x80 : 0;
	if(g_nKeyBufferCnt)
	{
		nKey |= g_nKeyBuffer[g_nNextOutIdx].nAppleKey;
		g_nLastKey = g_nKeyBuffer[g_nNextOutIdx].nAppleKey;
	}
	else
	{
		nKey |= g_nLastKey;
	}
	return nKey;
#endif
}

//===========================================================================

BYTE /*__stdcall */KeybReadFlag (WORD, WORD, BYTE, BYTE, ULONG)
{
	keyboardqueries++;

	//

// 	if(g_bPasteFromClipboard)
// 		ClipboardInit();
//
// 	if(g_bClipboardActive)
// 	{
// 		if(*lptstr == 0)
// 			ClipboardDone();
// 		else
// 			return 0x80 | ClipboardCurrChar(true);
// 	}

	//

	Uint8 *keys;
	keys = SDL_GetKeyState(NULL); // get current key state - thanx to SDL developers! ^_^ beom beotiger
#ifdef KEY_OLD
	keywaiting = 0;
	return keycode | (keys[lastvirtkey] ? 0x80 : 0);
#else
	BYTE nKey = (keys[g_nKeyBuffer[g_nNextOutIdx].nVirtKey]) ? 0x80 : 0;
	nKey |= g_nKeyBuffer[g_nNextOutIdx].nAppleKey;
	if(g_nKeyBufferCnt)
	{
		g_nKeyBufferCnt--;
		g_nNextOutIdx = (g_nNextOutIdx + 1) % g_nKeyBufferSize;
	}
	return nKey;
#endif
}

//===========================================================================
void KeybToggleCapsLock ()
{
	if (!IS_APPLE2)
	{
		g_bCapsLock = !g_bCapsLock;// never mind real CapsLock status, heh???(GetKeyState(VK_CAPITAL) & 1);
//		printf("g_bCapsLock=%d\n", g_bCapsLock);
//		FrameRefreshStatus(DRAW_LEDS);
	}
}

//===========================================================================

DWORD KeybGetSnapshot(SS_IO_Keyboard* pSS)
{
	pSS->keyboardqueries	= keyboardqueries;
	pSS->nLastKey			= g_nLastKey;
	return 0;
}

DWORD KeybSetSnapshot(SS_IO_Keyboard* pSS)
{
	keyboardqueries	= pSS->keyboardqueries;
	g_nLastKey		= pSS->nLastKey;
	return 0;
}
