//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_USBKeyboard.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Asura_PS3_USBKeyboard.h"

// Stdlib platform-specific.
#include <cell/keyboard.h>

// Engine independent.
#include "Core/Asura_UserInput.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Logfile.h"

//TEMP - remove when checks on CELL_SDK_VERSION below go
#include <sdk_version.h>

// C O N S T A N T S /////////////////////////////////////////////////////////

#if CELL_SDK_VERSION < 0x090002
static const u_int uKB_ARRANGEMENT     = CELL_KB_MAPPING_101;
static const u_int uKB_REPEAT_DELAY    = 30;
static const u_int uKB_REPEAT_INTERVAL = 2;
#endif

static const u_int uKB_INPUT_BFR_SIZE  = 32;

// G L O B A L S /////////////////////////////////////////////////////////////

static Asura_UnicodeChar g_wszKBInputBuffer[uKB_INPUT_BFR_SIZE];
static u_int g_uKBInputBufferLen = 0;

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

void Asura_PS3_USBKeyboard::Initialise( u_int uID )
{
    m_uID = uID;

    // Set our status to DISCONNECTED initially. We'll pick up the keyboard
    // in the first update, if it's already there.
    m_eStatus = STATUS_DISCONNECTED;
}

void Asura_PS3_USBKeyboard::Reset( void )
{
    m_eStatus = STATUS_DISCONNECTED;
}

void Asura_PS3_USBKeyboard::Update( const CellKbInfo& xCurKBInfo, u_char* pucKeyOutput )
{
    bool bCurrentlyConnected = ( CELL_KB_STATUS_CONNECTED == xCurKBInfo.status[m_uID] );
    bool bWasConnected = ( STATUS_DISCONNECTED != m_eStatus );

    // Deal with the keyboard having been connected / disconnected.
    if( bCurrentlyConnected != bWasConnected )
    {
        if( bCurrentlyConnected )
        {
            Asura_OutputToDebugger(( "USB keyboard: Keyboard %u connected.\n", m_uID ));
            HandleConnection();
        }
        else
        {
            Asura_OutputToDebugger(( "USB keyboard: Keyboard %u disconnected.\n", m_uID ));
            m_eStatus = STATUS_DISCONNECTED;
        }

        // Process input next frame.
        return;
    }

    // Check if we have anything to do.
    if( STATUS_CONNECTED != m_eStatus )
    {
        return;
    }

    // Process latest input.
    CellKbData xData;
	// JAT 30/08/07 - you seem to need to do this otherwise you get spurious key presses
	Asura_Mem::Set( &xData, 0, sizeof( CellKbData ) );

    if( cellKbRead(m_uID, &xData) == CELL_KB_OK )
    {
        // Run through the individual key codes.
        TranslateKeyPresses( xData, pucKeyOutput );

        // We also need to process the current state of CTRL, etc.
        TranslateModifierKeys( xData, pucKeyOutput );
    }
}

void Asura_PS3_USBKeyboard::TranslateKeyPresses( const CellKbData& xData, u_char* pucKeyOutput )
{
    for( int i = 0; i < xData.len; ++i )
    {
        const u_short usKeyCode = xData.keycode[i];

        if( !usKeyCode ) { continue; }

#if CELL_SDK_VERSION < 0x090002
        u_short usTranslatedKeyCode = cellKbCnvRawCode( uKB_ARRANGEMENT,
                                                        xData.mkey,
                                                        xData.led,
                                                        usKeyCode );
#else
        u_short usTranslatedKeyCode = cellKbCnvRawCode( m_uArrangement,
                                                        xData.mkey,
                                                        xData.led,
                                                        usKeyCode );
#endif

        u_int uAsuraKeyID = ASURA_KEYID_VOID;

        if( ASURA_KEYID_SPACE <= usTranslatedKeyCode && usTranslatedKeyCode < ASURA_NUMBER_OF_ASCII_KEYS )
        {
            uAsuraKeyID = usTranslatedKeyCode;
        }
        else
        {
            switch( usKeyCode )
            {
                case CELL_KEYC_ENTER:         uAsuraKeyID = ASURA_KEYID_ENTER;          break;
                case CELL_KEYC_ESCAPE:        uAsuraKeyID = ASURA_KEYID_ESCAPE;         break;
                case CELL_KEYC_BS:            uAsuraKeyID = ASURA_KEYID_BACKSPACE;      break;
                case CELL_KEYC_TAB:           uAsuraKeyID = ASURA_KEYID_TAB;            break;
                case CELL_KEYC_CAPS_LOCK:     uAsuraKeyID = ASURA_KEYID_CAPSLOCK;       break;
                case CELL_KEYC_F1:            uAsuraKeyID = ASURA_KEYID_F1;             break;
                case CELL_KEYC_F2:            uAsuraKeyID = ASURA_KEYID_F2;             break;
                case CELL_KEYC_F3:            uAsuraKeyID = ASURA_KEYID_F3;             break;
                case CELL_KEYC_F4:            uAsuraKeyID = ASURA_KEYID_F4;             break;
                case CELL_KEYC_F5:            uAsuraKeyID = ASURA_KEYID_F5;             break;
                case CELL_KEYC_F6:            uAsuraKeyID = ASURA_KEYID_F6;             break;
                case CELL_KEYC_F7:            uAsuraKeyID = ASURA_KEYID_F7;             break;
                case CELL_KEYC_F8:            uAsuraKeyID = ASURA_KEYID_F8;             break;
                case CELL_KEYC_F9:            uAsuraKeyID = ASURA_KEYID_F9;             break;
                case CELL_KEYC_F10:           uAsuraKeyID = ASURA_KEYID_F10;            break;
                case CELL_KEYC_F11:           uAsuraKeyID = ASURA_KEYID_F11;            break;
                case CELL_KEYC_F12:           uAsuraKeyID = ASURA_KEYID_F12;            break;
				case CELL_KEYC_SCROLL_LOCK:   uAsuraKeyID = ASURA_KEYID_SCROLLOK;       break;
				case CELL_KEYC_PAUSE:		  uAsuraKeyID = ASURA_KEYID_PAUSE;			break;
                case CELL_KEYC_INSERT:        uAsuraKeyID = ASURA_KEYID_INS;            break;
                case CELL_KEYC_HOME:          uAsuraKeyID = ASURA_KEYID_HOME;           break;
                case CELL_KEYC_PAGE_UP:       uAsuraKeyID = ASURA_KEYID_PAGEUP;         break;
                case CELL_KEYC_DELETE:        uAsuraKeyID = ASURA_KEYID_DEL;            break;
                case CELL_KEYC_END:           uAsuraKeyID = ASURA_KEYID_END;            break;
                case CELL_KEYC_PAGE_DOWN:     uAsuraKeyID = ASURA_KEYID_PAGEDOWN;       break;
                case CELL_KEYC_RIGHT_ARROW:   uAsuraKeyID = ASURA_KEYID_RIGHT;          break;
                case CELL_KEYC_LEFT_ARROW:    uAsuraKeyID = ASURA_KEYID_LEFT;           break;
                case CELL_KEYC_DOWN_ARROW:    uAsuraKeyID = ASURA_KEYID_DOWN;           break;
                case CELL_KEYC_UP_ARROW:      uAsuraKeyID = ASURA_KEYID_UP;             break;
                case CELL_KEYC_NUM_LOCK:      uAsuraKeyID = ASURA_KEYID_NUMLOCK;        break;
                case CELL_KEYC_KPAD_SLASH:    uAsuraKeyID = ASURA_KEYID_NUMPADDIVIDE;   break;
                case CELL_KEYC_KPAD_ASTERISK: uAsuraKeyID = ASURA_KEYID_NUMPADMULTIPLY; break;
                case CELL_KEYC_KPAD_MINUS:    uAsuraKeyID = ASURA_KEYID_NUMPADSUB;      break;
                case CELL_KEYC_KPAD_PLUS:     uAsuraKeyID = ASURA_KEYID_NUMPADADD;      break;
                case CELL_KEYC_KPAD_ENTER:    uAsuraKeyID = ASURA_KEYID_NUMPADENTER;    break;
                case CELL_KEYC_KPAD_1:        uAsuraKeyID = ASURA_KEYID_NUMPAD1;        break;
                case CELL_KEYC_KPAD_2:        uAsuraKeyID = ASURA_KEYID_NUMPAD2;        break;
                case CELL_KEYC_KPAD_3:        uAsuraKeyID = ASURA_KEYID_NUMPAD3;        break;
                case CELL_KEYC_KPAD_4:        uAsuraKeyID = ASURA_KEYID_NUMPAD4;        break;
                case CELL_KEYC_KPAD_5:        uAsuraKeyID = ASURA_KEYID_NUMPAD5;        break;
                case CELL_KEYC_KPAD_6:        uAsuraKeyID = ASURA_KEYID_NUMPAD6;        break;
                case CELL_KEYC_KPAD_7:        uAsuraKeyID = ASURA_KEYID_NUMPAD7;        break;
                case CELL_KEYC_KPAD_8:        uAsuraKeyID = ASURA_KEYID_NUMPAD8;        break;
                case CELL_KEYC_KPAD_9:        uAsuraKeyID = ASURA_KEYID_NUMPAD9;        break;
                case CELL_KEYC_KPAD_0:        uAsuraKeyID = ASURA_KEYID_NUMPAD0;        break;
                case CELL_KEYC_KPAD_PERIOD:   uAsuraKeyID = ASURA_KEYID_NUMPADDEL;      break;
                case CELL_KEYC_APPLICATION:   uAsuraKeyID = ASURA_KEYID_APPS;           break;
            }
        }

        if( ASURA_KEYID_VOID != uAsuraKeyID )
        {
            pucKeyOutput[uAsuraKeyID] = 0xFFu;

            if( g_uKBInputBufferLen < uKB_INPUT_BFR_SIZE )
            {
                g_wszKBInputBuffer[g_uKBInputBufferLen++] = static_cast<Asura_UnicodeChar>( uAsuraKeyID );
            }
        }
    }
}

void Asura_PS3_USBKeyboard::TranslateModifierKeys( const CellKbData& xData, u_char* pucKeyOutput )
{
    const u_int uModifierState = xData.mkey;

    if( uModifierState & CELL_KB_MKEY_L_CTRL )
    {
        pucKeyOutput[ASURA_KEYID_CTRL]     = 0xFF;
        pucKeyOutput[ASURA_KEYID_LEFTCTRL] = 0xFF;
    }

    if( uModifierState & CELL_KB_MKEY_L_SHIFT )
    {
        pucKeyOutput[ASURA_KEYID_SHIFT]     = 0xFF;
        pucKeyOutput[ASURA_KEYID_LEFTSHIFT] = 0xFF;
    }

    if( uModifierState & CELL_KB_MKEY_L_ALT )
    {
        pucKeyOutput[ASURA_KEYID_ALT]     = 0xFF;
        pucKeyOutput[ASURA_KEYID_LEFTALT] = 0xFF;
    }

    if( uModifierState & CELL_KB_MKEY_L_WIN )
    {
        pucKeyOutput[ASURA_KEYID_LWIN] = 0xFF;
    }

    if( uModifierState & CELL_KB_MKEY_R_CTRL )
    {
        pucKeyOutput[ASURA_KEYID_CTRL]      = 0xFF;
        pucKeyOutput[ASURA_KEYID_RIGHTCTRL] = 0xFF;
    }

    if( uModifierState & CELL_KB_MKEY_R_SHIFT )
    {
        pucKeyOutput[ASURA_KEYID_SHIFT]      = 0xFF;
        pucKeyOutput[ASURA_KEYID_RIGHTSHIFT] = 0xFF;
    }

    if( uModifierState & CELL_KB_MKEY_R_ALT )
    {
        pucKeyOutput[ASURA_KEYID_ALT]      = 0xFF;
        pucKeyOutput[ASURA_KEYID_RIGHTALT] = 0xFF;
    }

    if( uModifierState & CELL_KB_MKEY_R_WIN )
    {
        pucKeyOutput[ASURA_KEYID_RWIN] = 0xFF;
    }
}

void Asura_PS3_USBKeyboard::HandleConnection( void )
{
    int iReturn;

    do 
    {
#if CELL_SDK_VERSION < 0x090002
        // (MP - 07.02.2006) This is taken pretty much directly from one of
        // the samples and probably needs some tweaking. For example, the
        // arrangement, if we're supporting this properly, is something that
        // the user should be able to set (since apparently there's no way
        // to determine it automatically.)

        iReturn = cellKbSetArrangement( m_uID, uKB_ARRANGEMENT );
        if( CELL_KB_OK != iReturn ) { break; }

        iReturn = cellKbSetRepeat( m_uID, uKB_REPEAT_DELAY, uKB_REPEAT_INTERVAL );
        if( CELL_KB_OK != iReturn ) { break; }

        iReturn = cellKbSetReadMode( m_uID, CELL_KB_RMODE_INPUTCHAR );
        if( CELL_KB_OK != iReturn ) { break; }

        iReturn = cellKbSetCodeType( m_uID, CELL_KB_CODETYPE_RAW );
        if( CELL_KB_OK != iReturn ) { break; }
#else
        iReturn = cellKbSetReadMode( m_uID, CELL_KB_RMODE_INPUTCHAR );
        if( CELL_KB_OK != iReturn ) { break; }

        iReturn = cellKbSetCodeType( m_uID, CELL_KB_CODETYPE_RAW );
        if( CELL_KB_OK != iReturn ) { break; }

        CellKbConfig xConfig;
        iReturn = cellKbGetConfiguration( m_uID, &xConfig );
        if( CELL_KB_OK != iReturn ) { break; }

        m_uArrangement = xConfig.arrange;
#endif
    }
    while( false );

    if( CELL_KB_OK == iReturn )
    {
        m_eStatus = STATUS_CONNECTED;
    }
    else
    {
        Asura_OutputToDebugger(( "USB keyboard: ERROR! Failed to initialise keyboard %u.\n", m_uID ));
        m_eStatus = STATUS_ERROR;
    }
}

void Asura_PS3_USBKeyboard::ResetInputBfr( void )
{
    g_uKBInputBufferLen = 0;
}

void Asura_PS3_USBKeyboard::GetInputBfr( const Asura_UnicodeChar** pwszInputBfr, u_int* puLen )
{
    *pwszInputBfr = g_wszKBInputBuffer;
    *puLen = g_uKBInputBufferLen;
}
