/***************************************************************************/
/*  Description: KeybDI.c, part of CPA INO library                         */                                
/*               Keyboard specific implementation                          */
/*                                                                         */
/*  Author: F. Jentey                                                      */
/*  Last Update: 05/03/97                                                  */
/*               30/07/97 Direct Input                                     */
/***************************************************************************/
 
 
#include <locale.h>
#include "INO\errINO.h"
#include "InitDI.h"
#include "INOInit.h"
#include "Keyboard.h"
 
 
 
 
/**************************************************************************
	Globale vars
***************************************************************************/
 
/* Keyboard config variables : get the country keyboard config */
char                    INO_LayoutName[KL_NAMELENGTH];
HKL                     INO_LayoutHandle;
 
/* Table for virtual key code use for ASCII conversion  */
/* INO_acDikToVirtual is used for the numeric pad, coz  */
/* MapVirtualKeyEx() doesn't Verr Num state.            */
BYTE                    INO_acVirtualKeyCodeTab[256];
BYTE                    INO_acDikToVirtual[256];
 
/* Store the key use in the game */
unsigned short          INO_ActiveKey[128];
unsigned short          INO_ActiveKeyNumber;
 
 
 
 
/**********************************************************************************/
/*  This fonction returns the ASCII code corresponding to a key code if this key  */
/*  is printable.                                                                 */
/**********************************************************************************/
short INO_fn_wDikToAscii(short wKeyCode, INO_tdhDevice hDev)
{
    unsigned long   VirtualKeyCode;
    long            iResult;
    unsigned short  a_uwASCIICode[2];
 
    /* Get the VirtualKey code of the key               */
    /* We suppose all DIK code correspond to scan code. */
    /* For numeric pad key, use the dik to virtual key  */
    /* code translation table.                          */
    if ( (((wKeyCode>0x46) && (wKeyCode<0x54)) || (wKeyCode==0xb5)) &&
         (GetKeyState(VK_NUMLOCK) & INO_C_ucKeyToggled) )
        VirtualKeyCode = INO_acDikToVirtual[wKeyCode];
    else
        VirtualKeyCode = MapVirtualKeyEx(wKeyCode, 1, INO_LayoutHandle);
 
    /* La fameuse modif qui tue - Elie */
	if ( (VirtualKeyCode == 0) ||
         (VirtualKeyCode == VK_CAPITAL) ||
		 (VirtualKeyCode == VK_SCROLL) ||
         (VirtualKeyCode == VK_NUMLOCK) ||
		 (VirtualKeyCode == VK_SHIFT) ||
         (VirtualKeyCode == VK_CONTROL) ||
		 (VirtualKeyCode == VK_MENU) ||
		 ((VirtualKeyCode >= VK_F1) && (VirtualKeyCode<=VK_F24))
		) 
		return (0);
 
    /* Fill the INO_acVirtualKeyCodeTab array with SHIFT, ALT and CTRL state */
    INO_acVirtualKeyCodeTab[VK_SHIFT] = INO_C_ucKeyDown &
        ( M_KeyState(hDev,C_ucKey_LSHIFT) |
          M_KeyState(hDev,C_ucKey_RSHIFT) );
    INO_acVirtualKeyCodeTab[VK_CONTROL] = INO_C_ucKeyDown &
        ( M_KeyState(hDev,C_ucKey_LCTRL) |
          M_KeyState(hDev,C_ucKey_RCTRL) );
    INO_acVirtualKeyCodeTab[VK_MENU] = INO_C_ucKeyDown &
        ( M_KeyState(hDev,C_ucKey_LALT) |
          M_KeyState(hDev,C_ucKey_RALT) );
    INO_acVirtualKeyCodeTab[VK_CAPITAL] = GetKeyState(VK_CAPITAL) & INO_C_ucKeyToggled;
 
	/* Deuxieme modif qui tue - Elie */
/*	INO_acVirtualKeyCodeTab[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK) & INO_C_ucKeyToggled;*/
 
    iResult = ToAsciiEx(VirtualKeyCode, wKeyCode, INO_acVirtualKeyCodeTab, a_uwASCIICode,0 , INO_LayoutHandle);
    switch (iResult)
    {
    case 0:
        return (0);
    case 1:
        iResult = (int)(a_uwASCIICode[0] & 0xFF);
        /*if (!isprint(iResult) /*&& iswprint(iResult)  ) */
        /*    return (0);*/
        break;
    case 2:
        return (0);
    }
    return ((short)iResult);
}
 
 
/*  Remove an active key. An error occurs when the specified key isn't   */
/*  active.By default, all keys are active, but you can remove some keys */
/*  if they aren't used to speed up the fn_wKbRead function.             */
short INO_fn_wRemoveActiveKey(short wKeyCode)
{
    short i,j;
 
    for (i=0 ;i<INO_ActiveKeyNumber; i++)
    {
        if (INO_ActiveKey[i] == wKeyCode)
        {
            for (j=i+1; j<INO_ActiveKeyNumber; j++)
                INO_ActiveKey[j-1] = INO_ActiveKey[j];
            INO_ActiveKeyNumber--;
            return (1);
        }
    }
    return (0);
}
 
 
/*  Add a key to be managed. There is an error if the specified key is  */
/*  already active.                                                     */
short INO_fn_wAddActiveKey(short wKeyCode)
{
    short i;
 
    if (INO_ActiveKeyNumber > 127)
    {
        /* TO DO: error message */
        return (0);
    }
    /* Test if key is already active */
    for (i=0 ;i<INO_ActiveKeyNumber; i++)
    {
        if (INO_ActiveKey[i] == wKeyCode)
            return (0);
    }
    INO_ActiveKey[INO_ActiveKeyNumber] = wKeyCode;
    INO_ActiveKeyNumber++;
    return (0);
}
 
 
/*********************************************************************
                    INITIALISATION
*********************************************************************/
 
/*  Init keyboard capabilities. */
short INO_fn_wInitKeyboardCaps(INO_tdhDevice hDev)
{
    short    wASCIIForKeyA, i;
    short    wDev;
    unsigned long ulSubType;
 
    for (i=0; i<INO_C_wNbMaxDevice; i++)
        if (INO_g_a_stDIDevice[i].m_hDevice == hDev) break;
    if (i == INO_C_wNbMaxDevice) return (0);
    wDev = i;
 
    ulSubType = INO_fn_wFillDeviceCaps(hDev);
    if (!ulSubType) 
        return(0);
    memset(INO_ActiveKey,0,sizeof(unsigned short)*128);
    INO_ActiveKeyNumber = 0;
 
    /* For ASCII conversion */
    GetKeyboardLayoutName(INO_LayoutName);
    INO_LayoutHandle = LoadKeyboardLayout(INO_LayoutName,KLF_ACTIVATE);
 
    /* Set the default active Key (All keys) */
    for ( i=0x01 ; i<0x58 ; i++ ) 
        INO_fn_wAddActiveKey(i);
    INO_fn_wAddActiveKey(0x69);  /* Pause */
    INO_fn_wAddActiveKey(0x9c);
    INO_fn_wAddActiveKey(0x9d);
    INO_fn_wAddActiveKey(0xb5);
    INO_fn_wAddActiveKey(0xb7);
    INO_fn_wAddActiveKey(0xb8);
    INO_fn_wAddActiveKey(0xc7);
    INO_fn_wAddActiveKey(0xc5);  /* Num Lock */
    INO_fn_wAddActiveKey(0xc8);
    INO_fn_wAddActiveKey(0xc9);
    INO_fn_wAddActiveKey(0xcb);
    INO_fn_wAddActiveKey(0xcd);
    INO_fn_wAddActiveKey(0xcf);
    INO_fn_wAddActiveKey(0xd0);
    INO_fn_wAddActiveKey(0xd1);
    INO_fn_wAddActiveKey(0xd2);
    INO_fn_wAddActiveKey(0xd3);
    INO_fn_wAddActiveKey(0xdb);
    INO_fn_wAddActiveKey(0xdc);
    INO_fn_wAddActiveKey(0xdd);
 
    /* Fill the Dik to Virtual key code translation table */
    /* for numeric pad (Vk when Verr Num is ON)           */
    INO_acDikToVirtual[C_ucKey_PAD_0]     = VK_NUMPAD0;
    INO_acDikToVirtual[C_ucKey_PAD_1]     = VK_NUMPAD1;
    INO_acDikToVirtual[C_ucKey_PAD_2]     = VK_NUMPAD2;
    INO_acDikToVirtual[C_ucKey_PAD_3]     = VK_NUMPAD3;
    INO_acDikToVirtual[C_ucKey_PAD_4]     = VK_NUMPAD4;
    INO_acDikToVirtual[C_ucKey_PAD_5]     = VK_NUMPAD5;
    INO_acDikToVirtual[C_ucKey_PAD_6]     = VK_NUMPAD6;
    INO_acDikToVirtual[C_ucKey_PAD_7]     = VK_NUMPAD7;
    INO_acDikToVirtual[C_ucKey_PAD_8]     = VK_NUMPAD8;
    INO_acDikToVirtual[C_ucKey_PAD_9]     = VK_NUMPAD9;
    INO_acDikToVirtual[C_ucKey_PAD_DIV]   = VK_DIVIDE;
    INO_acDikToVirtual[C_ucKey_PAD_MUL]   = VK_MULTIPLY;
    INO_acDikToVirtual[C_ucKey_PAD_SUB]   = VK_SUBTRACT;
    INO_acDikToVirtual[C_ucKey_PAD_ADD]   = VK_ADD; 
    INO_acDikToVirtual[C_ucKey_PAD_POINT] = VK_DECIMAL;
 
    /* Find keyboard type (Azerty / Qwerty) */
    wASCIIForKeyA = INO_fn_wDikToAscii(C_ucKey_A,hDev) & 0xFF;
    if ((wASCIIForKeyA == 'a') || (wASCIIForKeyA == 'A'))
        ((INO_tdstKeyboardCaps*)(hDev->m_p_stCaps))->m_wIsAzerty = 1;
    else
        ((INO_tdstKeyboardCaps*)(hDev->m_p_stCaps))->m_wIsAzerty = 0;
 
    return (1);
}
 
 
/*  This function must be called at regular interval, for example each VBL.  */
/*  It Update the informations about the keyboard.                           */
short INO_fn_wReadKeyboard(INO_tdhDevice hDev)
{
    HRESULT  DInputErr;
    unsigned short  i, j;
    short    DownKey[INO_C_wMaxKeyDown];
    short    DownKeyNumber;
    short    LastKeyHasChanged, StateHasChanged;
    short    wDev;
    INO_tdstKeyboardState *p_stState = M_KState(hDev);
    INO_tdstKeyboardRecord *p_stH = (INO_tdstKeyboardRecord*)hDev->m_pvHistoric;
    unsigned char *p_ucCurrentTab, *p_ucPrevTab;
 
    if (!INO_M_bIsConnected(hDev)) 
        return (0);
 
    for (wDev=0; wDev<INO_C_wNbMaxDevice; wDev++)
        if (INO_g_a_stDIDevice[wDev].m_hDevice == hDev) break;
    if (wDev == INO_C_wNbMaxDevice)
    {
        INO_fn_vUpdateLastError(E_uwINO_WarningInvalidDevice);
        return (0);
    }
 
    /* Increase the internal engine loop counter */
    hDev->m_ulLastTimeCount++;
 
    /* Flip the keyboard state buffer */
    p_stState->m_wCurrentTab = 1 - p_stState->m_wCurrentTab;
    p_ucCurrentTab = p_stState->m_a_ucTabKey[p_stState->m_wCurrentTab];
    p_ucPrevTab = p_stState->m_a_ucTabKey[1-p_stState->m_wCurrentTab];
 
    /* Try to acquire the keyboard if not done */
    if (!(p_stState->m_ucStatus & INO_C_ucAcquired) ||
         (p_stState->m_ucStatus & INO_C_ucAccessError) )
    {
        DInputErr = M_DIAcquire(wDev);
        switch (DInputErr)
        {
        case DI_OK:
		case S_FALSE:
            hDev->m_p_stState->m_ucStatus |= INO_C_ucAcquired;
            hDev->m_p_stState->m_ucStatus &= ~INO_C_ucAccessError;
            break;
 
        case DIERR_INPUTLOST:
        case DIERR_NOTACQUIRED:
        case DIERR_OTHERAPPHASPRIO:
            return (0);
 
		default:
            hDev->m_p_stState->m_ucStatus &= ~INO_C_ucAcquired;
            hDev->m_p_stState->m_ucStatus |= INO_C_ucAccessError;
            INO_fn_vUpdateLastError(E_uwINO_DirectInputError);
            return (0);
        }
    }
 
    /* Read all key */
	DInputErr = M_DIGetState(wDev, sizeof(BYTE)*256, p_ucCurrentTab);
	switch (DInputErr)
	{
	case DI_OK:
    case E_PENDING:
        break;
		
    case DIERR_INPUTLOST:
    case DIERR_NOTACQUIRED:
        hDev->m_p_stState->m_ucStatus &= ~INO_C_ucAcquired;
        return (0);
 
    default:
        INO_fn_vUpdateLastError(E_uwINO_DirectInputError);
        hDev->m_p_stState->m_ucStatus |= INO_C_ucAccessError;
        hDev->m_p_stState->m_ucStatus &= ~INO_C_ucAcquired;
        return (0);
	}
 
    /* Check if the keyboard state has changed by comparing the current buffer with     */
    /* the previous one. Store the key code of the down key in the local tab            */
    /* ChangedKey.                                                                      */
    StateHasChanged = 0;
    LastKeyHasChanged = 0;
    DownKeyNumber = 0;
    for (i=0 ; i<INO_ActiveKeyNumber ; i++)
    {
        j = INO_ActiveKey[i];
        if (p_ucCurrentTab[j] != p_ucPrevTab[j])
        {
            StateHasChanged = 1;
            if (p_ucCurrentTab[j])
            {
                LastKeyHasChanged = 1;
                p_stState->m_wLastKey = j;
            }
        }
        if ((DownKeyNumber < INO_C_wMaxKeyDown) && p_ucCurrentTab[j])
            DownKey[DownKeyNumber++] = j;
    }
 
    if (LastKeyHasChanged)
    {
        /* Update last ASCII code */
        p_stState->m_wLastASCII = INO_fn_wDikToAscii(p_stState->m_wLastKey,hDev);
 
        /* Update the current string */
        if (p_stState->m_wLastASCII)
        {
            if (p_stState->m_wCurrentStringLength == INO_C_wStringMaxLength)
            {
                for (i=0; i<INO_C_wStringMaxLength-1; i++)
                    p_stState->m_szCurrentString[i] = p_stState->m_szCurrentString[i+1];
                p_stState->m_szCurrentString[INO_C_wStringMaxLength-1] = (char)p_stState->m_wLastASCII;
            }
            else
            {
                p_stState->m_szCurrentString[p_stState->m_wCurrentStringLength] = (char)p_stState->m_wLastASCII;
                p_stState->m_wCurrentStringLength++;
            }
        }
    }
    else
        p_stState->m_wLastASCII = 0;
 
    /* Update historic.                                         */
    /* Test if it's the first call of the fn_wJKbReadKeyboard   */
    if (hDev->m_wRecordNumber == 0)
    {
        p_stH[0].m_ulDate = hDev->m_ulLastTimeCount;
        p_stH[0].m_wNbKeyDown = 0;
        hDev->m_wRecordNumber = 1;
        StateHasChanged = 0;
    }
    /* If not, update the historic if keyboard state has changed */
    else
    {
        if (StateHasChanged)
        {
            hDev->m_wHistoricHead++;
            if (hDev->m_wHistoricHead == hDev->m_wHistoricSize) hDev->m_wHistoricHead = 0;
            if (hDev->m_wRecordNumber != hDev->m_wHistoricSize) hDev->m_wRecordNumber++;
            
            p_stH[hDev->m_wHistoricHead].m_ulDate = hDev->m_ulLastTimeCount;
            p_stH[hDev->m_wHistoricHead].m_wNbKeyDown = 0;
            for (i=0; i<DownKeyNumber; i++)
                p_stH[hDev->m_wHistoricHead].m_wKeyDown[i] = DownKey[i];
            p_stH[hDev->m_wHistoricHead].m_wNbKeyDown = DownKeyNumber;
        }
    }
    p_stState->m_wStateHasChanged = StateHasChanged;        
 
    return (1);
}
 
/*
  This function return C_wKbTrue if the selected key was down in the
  specified previous state.
*/
short INO_fn_bKeyWasDown(INO_tdhDevice hDev, short wKeyCode, short wHistoricIndex)
{
    long index, i;
    INO_tdstKeyboardRecord *p_stState;
 
    if (wHistoricIndex > hDev->m_wHistoricSize-1)
        return (0);
    else
    {
        index = (hDev->m_wHistoricHead - wHistoricIndex + hDev->m_wHistoricSize) % hDev->m_wHistoricSize;
        p_stState = &((INO_tdstKeyboardRecord*)(hDev->m_pvHistoric))[index];
        for (i=0 ; i<p_stState->m_wNbKeyDown; i++ )
            if (p_stState->m_wKeyDown[i] == wKeyCode ) return (1);
    }
    return (0);
}
 
 
short INO_fn_wCheckCheatCode(INO_tdhDevice hDev,char *p_szTestString)
{
    short l;
    char *p_szCurrentS;
    INO_tdstKeyboardState *p_stState = (INO_tdstKeyboardState*)(hDev->m_p_stState);
 
    l = strlen(p_szTestString);
    if (l > INO_C_wStringMaxLength) return (0);
    if (l > p_stState->m_wCurrentStringLength) return (0);
 
    p_szCurrentS = &p_stState->m_szCurrentString[p_stState->m_wCurrentStringLength-l];
 
    return (strcmp(p_szCurrentS,p_szTestString) == 0);
}
