//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#ifndef INITGUID
#define INITGUID
#endif

#include "math/mMath.h"
#include "platformWin32/winDInputDevice.h"
#include "console/console.h"
#include "platform/gameInterface.h"
#include "core/unicode.h"

// Static class data:
LPDIRECTINPUT8 DInputDevice::smDInputInterface;
U8    DInputDevice::smKeyboardCount;
U8    DInputDevice::smMouseCount;
U8    DInputDevice::smJoystickCount;
U8    DInputDevice::smUnknownCount;
U8    DInputDevice::smModifierKeys;
bool  DInputDevice::smKeyStates[256];
bool  DInputDevice::smInitialized = false;

#ifdef LOG_INPUT
const char* getKeyName( U16 key );
#endif

//------------------------------------------------------------------------------
DInputDevice::DInputDevice( const DIDEVICEINSTANCE* dii )
{
   mDeviceInstance   = *dii;
   mDevice           = NULL;
   mAcquired         = false;
   mNeedSync         = false;
   mObjInstance      = NULL;
   mObjFormat        = NULL;
   mObjInfo          = NULL;
   mObjBuffer1       = NULL;
   mObjBuffer2       = NULL;
   mPrevObjBuffer    = NULL;

   mPrevPOVPos       = 0;

   switch ( GET_DIDEVICE_TYPE( mDeviceInstance.dwDevType ) )
   {
      case DI8DEVTYPE_KEYBOARD:
         mDeviceType = KeyboardDeviceType;
         mDeviceID   = smKeyboardCount++;
         dSprintf( mName, 29, "keyboard%d", mDeviceID );
         break;

      case DI8DEVTYPE_MOUSE:
         mDeviceType = MouseDeviceType;
         mDeviceID   = smMouseCount++;
         dSprintf( mName, 29, "mouse%d", mDeviceID );
         break;

      case DI8DEVTYPE_JOYSTICK:
      case DI8DEVTYPE_GAMEPAD:
         mDeviceType = JoystickDeviceType;
         mDeviceID   = smJoystickCount++;
         dSprintf( mName, 29, "joystick%d", mDeviceID );
         break;

      default:
         mDeviceType = UnknownDeviceType;
         mDeviceID   = smUnknownCount++;
         dSprintf( mName, 29, "unknown%d", mDeviceID );
         break;
   }
}

//------------------------------------------------------------------------------
DInputDevice::~DInputDevice()
{
   destroy();
}

//------------------------------------------------------------------------------
void DInputDevice::init()
{
   if ( !smInitialized )
   {
      dMemset( smKeyStates, 0, sizeof( smKeyStates ) );
      smInitialized = true;
   }

   // Reset all of the static variables:
   smDInputInterface    = NULL;
   smKeyboardCount      = 0;
   smMouseCount         = 0;
   smJoystickCount      = 0;
   smUnknownCount       = 0;
   smModifierKeys       = 0;
}

//------------------------------------------------------------------------------
void DInputDevice::resetModifierKeys()
{
   smModifierKeys = 0;
   setModifierKeys( 0 );
}

//------------------------------------------------------------------------------
bool DInputDevice::create()
{
   HRESULT result;

   if ( smDInputInterface )
   {
      result = smDInputInterface->CreateDevice( mDeviceInstance.guidInstance, &mDevice, NULL );
      if ( result == DI_OK )
      {
		 mDeviceCaps.dwSize = sizeof( DIDEVCAPS );
         if ( FAILED( mDevice->GetCapabilities( &mDeviceCaps ) ) )
         {
            Con::errorf( "  Failed to get the capabilities of the %s input device.", mName );
#ifdef LOG_INPUT
            Input::log( "Failed to get the capabilities of &s!\n", mName );
#endif
            return false;
         }

#ifdef LOG_INPUT
         Input::log( "%s detected, created as %s (%s).\n", mDeviceInstance.tszProductName, mName, ( isPolled() ? "polled" : "asynchronous" ) );
#endif

         if ( enumerateObjects() )
         {
			// Set the device's data format:
            DIDATAFORMAT dataFormat;
            dMemset( &dataFormat, 0, sizeof( DIDATAFORMAT ) );
            dataFormat.dwSize       = sizeof( DIDATAFORMAT );
            dataFormat.dwObjSize    = sizeof( DIOBJECTDATAFORMAT );
			dataFormat.dwFlags      = ( mDeviceType == MouseDeviceType ) ? DIDF_RELAXIS : DIDF_ABSAXIS;
            dataFormat.dwDataSize   = mObjBufferSize;
            dataFormat.dwNumObjs    = mObjCount;
            dataFormat.rgodf        = mObjFormat;		
			
            result = mDevice->SetDataFormat( &dataFormat );
            if ( FAILED( result ) )
            {				
               Con::errorf( "  Failed to set the data format for the %s input device.", mName );
#ifdef LOG_INPUT
               Input::log( "Failed to set the data format for %s!\n", mName );
#endif
			   return false;
            }

            // Set up the data buffer for buffered input:
            DIPROPDWORD prop;
            prop.diph.dwSize        = sizeof( DIPROPDWORD );
            prop.diph.dwHeaderSize  = sizeof( DIPROPHEADER );
            prop.diph.dwObj         = 0;
            prop.diph.dwHow         = DIPH_DEVICE;
            if ( isPolled() )
               prop.dwData = mObjBufferSize;
            else
               prop.dwData = QUEUED_BUFFER_SIZE;

            result = mDevice->SetProperty( DIPROP_BUFFERSIZE, &prop.diph );
            if ( FAILED( result ) )
            {
               Con::errorf( "  Failed to set the buffer size property for the %s input device.", mName );
#ifdef LOG_INPUT
               Input::log( "Failed to set the buffer size property for %s!\n", mName );
#endif
               return false;
            }

            // If this device is a mouse, set it to relative axis mode:
            if ( mDeviceType == MouseDeviceType )
            {
               prop.diph.dwObj   = 0;
               prop.diph.dwHow   = DIPH_DEVICE;
               prop.dwData       = DIPROPAXISMODE_REL;

               result = mDevice->SetProperty( DIPROP_AXISMODE, &prop.diph );
               if ( FAILED( result ) )
               {
                  Con::errorf( "  Failed to set relative axis mode for the %s input device.", mName );
#ifdef LOG_INPUT
                  Input::log( "Failed to set relative axis mode for %s!\n", mName );
#endif
                  return false;
               }
            }
         }
      }
      else
      {
#ifdef LOG_INPUT
         switch ( result )
         {
            case DIERR_DEVICENOTREG:
               Input::log( "CreateDevice failed -- The device or device instance is not registered with DirectInput.\n" );
               break;

            case DIERR_INVALIDPARAM:
               Input::log( "CreateDevice failed -- Invalid parameter.\n" );
               break;

            case DIERR_NOINTERFACE:
               Input::log( "CreateDevice failed -- The specified interface is not supported by the object.\n" );
               break;

            case DIERR_OUTOFMEMORY:
               Input::log( "CreateDevice failed -- Out of memory.\n" );
               break;

            default:
               Input::log( "CreateDevice failed -- Unknown error.\n" );
               break;
         };
#endif // LOG_INPUT
         Con::printf( "  CreateDevice failed for the %s input device!", mName );
         return false;
      }
   }

   Con::printf( "   %s input device created.", mName );
   return true;
}

//------------------------------------------------------------------------------
void DInputDevice::destroy()
{
   if ( mDevice )
   {
      unacquire();

      mDevice->Release();
      mDevice = NULL;

      delete [] mObjInstance;
      delete [] mObjFormat;
      delete [] mObjInfo;
      delete [] mObjBuffer1;
      delete [] mObjBuffer2;

      mObjInstance   = NULL;
      mObjFormat     = NULL;
      mObjInfo       = NULL;
      mObjBuffer1    = NULL;
      mObjBuffer2    = NULL;
      mPrevObjBuffer = NULL;
      mName[0]       = 0;
   }
}

//------------------------------------------------------------------------------
bool DInputDevice::acquire()
{
   if ( mDevice )
   {
      if ( mAcquired )
         return( true );

      bool result = false;
      // Set the cooperative level:
      // (do this here so that we have a valid app window)
      DWORD coopLevel = DISCL_BACKGROUND;
      if ( mDeviceType == JoystickDeviceType
// #ifndef DEBUG
//         || mDeviceType == MouseDeviceType
// #endif
         )
         coopLevel |= DISCL_EXCLUSIVE;
      else
         coopLevel |= DISCL_NONEXCLUSIVE;

      result = mDevice->SetCooperativeLevel( winState.appWindow, coopLevel );
      if ( FAILED( result ) )
      {
         Con::errorf( "Failed to set the cooperative level for the %s input device.", mName );
#ifdef LOG_INPUT
         Input::log( "Failed to set the cooperative level for %s!\n", mName );
#endif
         return false;
      }

      S32 code = mDevice->Acquire();
      result = SUCCEEDED( code );
      if ( result )
      {
         Con::printf( "%s input device acquired.", mName );
#ifdef LOG_INPUT
         Input::log( "%s acquired.\n", mName );
#endif
         mAcquired = true;

         // Update all of the key states:
         if ( !isPolled() )
            mNeedSync = true;
      }
      else
      {
         const char* reason;
         switch ( code )
         {
            case DIERR_INVALIDPARAM:      reason = "Invalid parameter"  ; break;
            case DIERR_NOTINITIALIZED:    reason = "Not initialized"; break;
            case DIERR_OTHERAPPHASPRIO:   reason = "Other app has priority"; break;
            case S_FALSE:                 reason = "Already acquired"; break;
            default:                      reason = "Unknown error"; break;
         }
         Con::warnf( "%s input device NOT acquired: %s", mName, reason );
#ifdef LOG_INPUT
         Input::log( "Failed to acquire %s: %s\n", mName, reason );
#endif
      }

      return( result );
   }

   return( false );
}

//------------------------------------------------------------------------------
bool DInputDevice::unacquire()
{
   if ( mDevice )
   {
      if ( !mAcquired )
         return( true );

      bool result = false;
      result = SUCCEEDED( mDevice->Unacquire() );
      if ( result )
      {
         Con::printf( "%s input device unacquired.", mName );
#ifdef LOG_INPUT
         Input::log( "%s unacquired.\n", mName );
#endif
         mAcquired = false;
      }
      else
      {
         Con::warnf( ConsoleLogEntry::General, "%s input device NOT unacquired.", mName );
#ifdef LOG_INPUT
         Input::log( "Failed to unacquire %s!\n", mName );
#endif
      }

      return( result );
   }

   return( false );
}

//------------------------------------------------------------------------------
BOOL CALLBACK DInputDevice::EnumObjectsProc( const DIDEVICEOBJECTINSTANCE* doi, LPVOID pvRef )
{
   // Don't enumerate unknown types:
   if ( doi->guidType == GUID_Unknown )
      return (DIENUM_CONTINUE);

   // Reduce a couple pointers:
   DInputDevice* diDevice = (DInputDevice*) pvRef;
   DIDEVICEOBJECTINSTANCE* objInstance = &diDevice->mObjInstance[diDevice->mObjEnumCount];
   DIOBJECTDATAFORMAT*     objFormat   = &diDevice->mObjFormat[diDevice->mObjEnumCount];

   // Fill in the object instance structure:
   *objInstance = *doi;

   // DWORD objects must be DWORD aligned:
   if ( !(objInstance->dwType & DIDFT_BUTTON ) )
      diDevice->mObjBufferOfs = ( diDevice->mObjBufferOfs + 3 ) & ~3;

   objInstance->dwOfs = diDevice->mObjBufferOfs;

   // Fill in the object data format structure:
   objFormat->pguid  = &objInstance->guidType;
   objFormat->dwType = objInstance->dwType;
   objFormat->dwFlags= 0;
   objFormat->dwOfs  = diDevice->mObjBufferOfs;

   // Advance the enumeration counters:
   if ( objFormat->dwType & DIDFT_BUTTON )
      diDevice->mObjBufferOfs += SIZEOF_BUTTON;
   else
      diDevice->mObjBufferOfs += SIZEOF_AXIS;
   diDevice->mObjEnumCount++;

   return (DIENUM_CONTINUE);
}

//------------------------------------------------------------------------------
bool DInputDevice::enumerateObjects()
{
   if ( !mDevice )
      return false;

   // Calculate the needed buffer sizes and allocate them:
   mObjCount = ( mDeviceCaps.dwAxes + mDeviceCaps.dwButtons + mDeviceCaps.dwPOVs );
   mObjBufferSize = mObjCount * sizeof( DWORD );

   mObjInstance   = new DIDEVICEOBJECTINSTANCE[mObjCount];
   mObjFormat     = new DIOBJECTDATAFORMAT[mObjCount];
   mObjInfo       = new ObjInfo[mObjCount];

   if ( isPolled() )
   {
      mObjBuffer1 = new U8[mObjBufferSize];
      dMemset( mObjBuffer1, 0, mObjBufferSize );
      mObjBuffer2 = new U8[mObjBufferSize];
      dMemset( mObjBuffer2, 0, mObjBufferSize );
   }
   mObjEnumCount = 0;
   mObjBufferOfs = 0;

   // Enumerate all of the 'objects' detected on the device:
   if ( FAILED( mDevice->EnumObjects( EnumObjectsProc, this, DIDFT_ALL ) ) )
      return false;

   // if we enumerated fewer objects that are supposedly available, reset the
   // object count
   if (mObjEnumCount < mObjCount)
	   mObjCount = mObjEnumCount;

   mObjBufferSize = ( mObjBufferSize + 3 ) & ~3;   // Fill in the actual size to nearest DWORD

   U32 buttonCount   = 0;
   U32 povCount      = 0;
   U32 xAxisCount    = 0;
   U32 yAxisCount    = 0;
   U32 zAxisCount    = 0;
   U32 rAxisCount    = 0;
   U32 uAxisCount    = 0;
   U32 vAxisCount    = 0;
   U32 sliderCount   = 0;
   U32 keyCount      = 0;
   U32 unknownCount  = 0;

   // Fill in the device object's info structure:
   for ( U32 i = 0; i < mObjCount; i++ )
   {
      if ( mObjInstance[i].guidType == GUID_Button )
      {
         mObjInfo[i].mType = SI_BUTTON;
         mObjInfo[i].mInst = KEY_BUTTON0 + buttonCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_POV )
      {
         mObjInfo[i].mType = SI_POV;
         mObjInfo[i].mInst = povCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_XAxis )
      {
         mObjInfo[i].mType = SI_XAXIS;
         mObjInfo[i].mInst = xAxisCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_YAxis )
      {
         mObjInfo[i].mType = SI_YAXIS;
         mObjInfo[i].mInst = yAxisCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_ZAxis )
      {
         mObjInfo[i].mType = SI_ZAXIS;
         mObjInfo[i].mInst = zAxisCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_RxAxis )
      {
         mObjInfo[i].mType = SI_RXAXIS;
         mObjInfo[i].mInst = rAxisCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_RyAxis )
      {
         mObjInfo[i].mType = SI_RYAXIS;
         mObjInfo[i].mInst = uAxisCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_RzAxis )
      {
         mObjInfo[i].mType = SI_RZAXIS;
         mObjInfo[i].mInst = vAxisCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_Slider )
      {
         mObjInfo[i].mType = SI_SLIDER;
         mObjInfo[i].mInst = sliderCount++;
      }
      else if ( mObjInstance[i].guidType == GUID_Key )
      {
         mObjInfo[i].mType = SI_KEY;
         mObjInfo[i].mInst = DIK_to_Key( DIDFT_GETINSTANCE( mObjFormat[i].dwType ) );
         keyCount++;
      }
      else
      {
         mObjInfo[i].mType = SI_UNKNOWN;
         mObjInfo[i].mInst = unknownCount++;
      }

      // Set the device object's min and max values:
      if ( mObjInstance[i].guidType == GUID_Button
        || mObjInstance[i].guidType == GUID_Key
        || mObjInstance[i].guidType == GUID_POV )
      {
         mObjInfo[i].mMin = DIPROPRANGE_NOMIN;
         mObjInfo[i].mMax = DIPROPRANGE_NOMAX;
      }
      else
      {
         // This is an axis or a slider, so find out its range:
         DIPROPRANGE pr;
         pr.diph.dwSize       = sizeof( pr );
         pr.diph.dwHeaderSize = sizeof( pr.diph );
         pr.diph.dwHow        = DIPH_BYID;
         pr.diph.dwObj        = mObjFormat[i].dwType;

         if ( SUCCEEDED( mDevice->GetProperty( DIPROP_RANGE, &pr.diph ) ) )
         {
            mObjInfo[i].mMin = pr.lMin;
            mObjInfo[i].mMax = pr.lMax;
         }
         else
         {
            mObjInfo[i].mMin = DIPROPRANGE_NOMIN;
            mObjInfo[i].mMax = DIPROPRANGE_NOMAX;
         }
      }
   }

#ifdef LOG_INPUT
   Input::log( "  %d total objects detected.\n", mObjCount );
   if ( buttonCount )
      Input::log( "  %d buttons.\n", buttonCount );
   if ( povCount )
      Input::log( "  %d POVs.\n", povCount );
   if ( xAxisCount )
      Input::log( "  %d x-axis.\n", xAxisCount );
   if ( yAxisCount )
      Input::log( "  %d y-axis.\n", yAxisCount );
   if ( zAxisCount )
      Input::log( "  %d z-axis.\n", zAxisCount );
   if ( rAxisCount )
      Input::log( "  %d r-axis.\n", rAxisCount );
   if ( uAxisCount )
      Input::log( "  %d u-axis.\n", uAxisCount );
   if ( vAxisCount )
      Input::log( "  %d v-axis.\n", vAxisCount );
   if ( sliderCount )
      Input::log( "  %d sliders.\n", sliderCount );
   if ( keyCount )
      Input::log( "  %d keys.\n", keyCount );
   if ( unknownCount )
      Input::log( "  %d unknown objects.\n", unknownCount );
   Input::log( "\n" );
#endif

   return true;
}

//------------------------------------------------------------------------------
const char* DInputDevice::getName()
{
#ifdef UNICODE
   static UTF8 buf[512];
   convertUTF16toUTF8(mDeviceInstance.tszInstanceName, buf, sizeof(buf));
   return (const char *)buf;
#else
   return mDeviceInstance.tszInstanceName;
#endif
}

//------------------------------------------------------------------------------
const char* DInputDevice::getProductName()
{
#ifdef UNICODE
   static UTF8 buf[512];
   convertUTF16toUTF8(mDeviceInstance.tszProductName, buf, sizeof(buf));
   return (const char *)buf;
#else
   return mDeviceInstance.tszProductName;
#endif
}

//------------------------------------------------------------------------------
bool DInputDevice::process()
{
   if ( mAcquired )
   {
      if ( isPolled() )
         return processImmediate();
      else
         return processAsync();
   }

   return false;
}

//------------------------------------------------------------------------------
bool DInputDevice::processAsync()
{
   DIDEVICEOBJECTDATA eventBuffer[QUEUED_BUFFER_SIZE];
   DWORD numEvents = QUEUED_BUFFER_SIZE;
   HRESULT result;

   if ( !mDevice )
      return false;

   // Test for the "need sync" flag:
   if ( mNeedSync )
   {
      // For now, only sync the keyboard:
      if ( mDeviceType == KeyboardDeviceType )
         syncKeyboardState();

      mNeedSync = false;
   }

   do
   {
      result = mDevice->GetDeviceData( sizeof( DIDEVICEOBJECTDATA ), eventBuffer, &numEvents, 0 );
	
      if ( !SUCCEEDED( result ) )
      {
         switch ( result )
         {
            case DIERR_INPUTLOST:
               // Data stream was interrupted, so try to reacquire the device:
               mAcquired = false;
               acquire();
               break;

            case DIERR_INVALIDPARAM:
               Con::errorf( "DInputDevice::processAsync -- Invalid parameter passed to GetDeviceData of the %s input device!", mName );
#ifdef LOG_INPUT
               Input::log( "Invalid parameter passed to GetDeviceData for %s!\n", mName );
#endif
               break;

            case DIERR_NOTACQUIRED:
               // We can't get the device, so quit:
               mAcquired = false;
               // Don't error out - this is actually a natural occurrence...
               //Con::errorf( "DInputDevice::processAsync -- GetDeviceData called when %s input device is not acquired!", mName );
#ifdef LOG_INPUT
               Input::log( "GetDeviceData called when %s is not acquired!\n", mName );
#endif
               break;
         }

         return false;
      }

      // We have buffered input, so act on it:
      for ( DWORD i = 0; i < numEvents; i++ )
         buildEvent( findObjInstance( eventBuffer[i].dwOfs ), eventBuffer[i].dwData, eventBuffer[i].dwData );

      // Check for buffer overflow:
      if ( result == DI_BUFFEROVERFLOW )
	  {
         // This is a problem, but we can keep going...
         Con::errorf( "DInputDevice::processAsync -- %s input device's event buffer overflowed!", mName );
#ifdef LOG_INPUT
         Input::log( "%s event buffer overflowed!\n", mName );
#endif
         mNeedSync = true; // Let it know to resync next time through...
      }
   }
   while ( numEvents );

   return true;
}

//------------------------------------------------------------------------------
bool DInputDevice::processImmediate()
{
   if ( !mDevice )
      return false;

   mDevice->Poll();

   U8* buffer = ( mPrevObjBuffer == mObjBuffer1 ) ? mObjBuffer2 : mObjBuffer1;
   HRESULT result = mDevice->GetDeviceState( mObjBufferSize, buffer );
   if ( !SUCCEEDED( result ) )
   {
      switch ( result )
      {
         case DIERR_INPUTLOST:
            // Data stream was interrupted, so try to reacquire the device:
            mAcquired = false;
            acquire();
            break;

         case DIERR_INVALIDPARAM:
            Con::errorf( "DInputDevice::processPolled -- invalid parameter passed to GetDeviceState on %s input device!", mName );
#ifdef LOG_INPUT
            Input::log( "Invalid parameter passed to GetDeviceState on %s.\n", mName );
#endif
            break;

         case DIERR_NOTACQUIRED:
            Con::errorf( "DInputDevice::processPolled -- GetDeviceState called when %s input device is not acquired!", mName );
#ifdef LOG_INPUT
            Input::log( "GetDeviceState called when %s is not acquired!\n", mName );
#endif
            break;

         case E_PENDING:
            Con::warnf( "DInputDevice::processPolled -- Data not yet available for the %s input device!", mName );
#ifdef LOG_INPUT
            Input::log( "Data pending for %s.", mName );
#endif
            break;
      }

      return false;
   }

   // Loop through all of the objects and produce events where
   // the states have changed:
   S32 newData, oldData;
   for ( DWORD i = 0; i < mObjCount; i++ )
   {
      if ( mObjFormat[i].dwType & DIDFT_BUTTON )
      {
         if ( mPrevObjBuffer )
         {
            newData = *( (U8*) ( buffer + mObjFormat[i].dwOfs ) );
            oldData = *( (U8*) ( mPrevObjBuffer + mObjFormat[i].dwOfs ) );
            if ( newData == oldData )
               continue;
         }
         else
            continue;
      }
      else if ( mObjFormat[i].dwType & DIDFT_POV )
      {
         if ( mPrevObjBuffer )
         {
            newData = *( (S32*) ( buffer + mObjFormat[i].dwOfs ) );
            oldData = *( (S32*) ( mPrevObjBuffer + mObjFormat[i].dwOfs ) );
            if ( LOWORD( newData ) == LOWORD( oldData ) )
               continue;
         }
         else
            continue;
      }
      else
      {
         // report normal axes every time through the loop:
         newData = *( (S32*) ( buffer + mObjFormat[i].dwOfs ) );
      }

      // Build an event:
      buildEvent( i, newData, oldData );
   }
   mPrevObjBuffer = buffer;

   return true;
}

//------------------------------------------------------------------------------
bool DInputDevice::processKeyEvent( InputEvent &event )
{
   if ( event.deviceType != KeyboardDeviceType || event.objType != SI_KEY )
      return false;

   bool modKey = false;
   U8 DIKeyCode = Key_to_DIK( event.objInst );

   if ( event.action == SI_MAKE )
   {
      // Maintain the key structure:
      smKeyStates[DIKeyCode] = true;

      switch ( event.objInst )
      {
         case KEY_LSHIFT:
            smModifierKeys |= SI_LSHIFT;
            modKey = true;
            break;

         case KEY_RSHIFT:
            smModifierKeys |= SI_RSHIFT;
            modKey = true;
            break;

         case KEY_LCONTROL:
            smModifierKeys |= SI_LCTRL;
            modKey = true;
            break;

         case KEY_RCONTROL:
            smModifierKeys |= SI_RCTRL;
            modKey = true;
            break;

         case KEY_LALT:
            smModifierKeys |= SI_LALT;
            modKey = true;
            break;

         case KEY_RALT:
            smModifierKeys |= SI_RALT;
            modKey = true;
            break;
      }
   }
   else
   {
      // Maintain the keys structure:
      smKeyStates[DIKeyCode] = false;

      switch ( event.objInst )
      {
         case KEY_LSHIFT:
            smModifierKeys &= ~SI_LSHIFT;
            modKey = true;
            break;

         case KEY_RSHIFT:
            smModifierKeys &= ~SI_RSHIFT;
            modKey = true;
            break;

         case KEY_LCONTROL:
            smModifierKeys &= ~SI_LCTRL;
            modKey = true;
            break;

         case KEY_RCONTROL:
            smModifierKeys &= ~SI_RCTRL;
            modKey = true;
            break;

         case KEY_LALT:
            smModifierKeys &= ~SI_LALT;
            modKey = true;
            break;

         case KEY_RALT:
            smModifierKeys &= ~SI_RALT;
            modKey = true;
            break;
      }
   }

   if ( modKey )
   {
      setModifierKeys( smModifierKeys );
      event.modifier = 0;
   }
   else
      event.modifier = smModifierKeys;

   // TODO: alter this getAscii call
   KEY_STATE state = STATE_LOWER;
   if (event.modifier & (SI_CTRL|SI_ALT) )
   {
      state = STATE_GOOFY;
   }
//   else if ( event.modifier & SI_SHIFT )
   if ( event.modifier & SI_SHIFT )
   {
      state = STATE_UPPER;
   }


   event.ascii = Input::getAscii( event.objInst, state );

   return modKey;
}

//------------------------------------------------------------------------------
void DInputDevice::syncKeyboardState()
{
   AssertFatal( mDeviceType == KeyboardDeviceType, "DInputDevice::syncKeyboardState - device is not a keyboard!" );

#ifdef LOG_INPUT
   Input::log( "Resynching key states for %s!\n", mName );
#endif

   U8* keyBuffer = new U8[mObjBufferSize];
   dMemset( keyBuffer, 0, sizeof( keyBuffer ) );
   HRESULT result = mDevice->GetDeviceState( mObjBufferSize, keyBuffer );
   if ( SUCCEEDED( result ) )
   {
      S32 keyState;
      bool keyIsDown, keyWasDown;
      for ( DWORD i = 1; i < mObjCount; i++ )   // Valid key codes start at 1
      {
         keyState = *( (U8*) ( keyBuffer + mObjFormat[i].dwOfs ) );
         keyWasDown = smKeyStates[i];
         keyIsDown = bool( keyState & 0x80 );
         if ( keyWasDown != keyIsDown )
            buildEvent( i - 1, ( keyState & 0x80 ), ( keyWasDown ? 0x80 : 0 ) );
      }

#ifdef LOG_INPUT
      Input::log( "Resync done.\n" );
#endif
   }
   else
   {
      const char* errorString = NULL;
      switch ( result )
      {
         case DIERR_INPUTLOST:
            errorString = "DIERR_INPUTLOST";
            break;

         case DIERR_INVALIDPARAM:
            errorString = "DIERR_INVALIDPARAM";
            break;

         case DIERR_NOTACQUIRED:
            errorString = "DIERR_NOTACQUIRED";
            break;

         case E_PENDING:
            errorString = "E_PENDING";
            break;

         default:
            errorString = "Unknown Error";
      }

#ifdef LOG_INPUT
      Input::log( "Resync GetDeviceState on %s failed! %s\n", mName, errorString );
#endif
      Con::errorf( "DInputDevice::syncKeyboardState - %s", errorString );
   }

   delete [] keyBuffer;
}

//------------------------------------------------------------------------------
DWORD DInputDevice::findObjInstance( DWORD offset )
{
   DIDEVICEOBJECTINSTANCE *inst = mObjInstance;
   for ( U32 i = 0; i < mObjCount; i++, inst++ )
   {
      if ( inst->dwOfs == offset )
         return i;
   }

   AssertFatal( false, "DInputDevice::findObjInstance -- failed to locate object instance." );
   return 0;
}

//------------------------------------------------------------------------------
bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
{
   DIDEVICEOBJECTINSTANCE &objInstance = mObjInstance[offset];
   ObjInfo &objInfo = mObjInfo[offset];

   if ( objInfo.mType == SI_UNKNOWN )
      return false;

   InputEvent newEvent;
   newEvent.deviceType  = mDeviceType;
   newEvent.deviceInst  = mDeviceID;
   newEvent.objType     = objInfo.mType;
   newEvent.objInst     = objInfo.mInst;
   newEvent.modifier    = smModifierKeys;

   InputEvent newEvent2;
   newEvent2.deviceType  = mDeviceType;
   newEvent2.deviceInst  = mDeviceID;
   newEvent2.objType     = objInfo.mType;
   newEvent2.objInst     = objInfo.mInst;
   newEvent2.modifier    = smModifierKeys;

   switch ( newEvent.objType )
   {
      case SI_XAXIS:
      case SI_YAXIS:
      case SI_ZAXIS:
      case SI_RXAXIS:
      case SI_RYAXIS:
      case SI_RZAXIS:
      case SI_SLIDER:
         newEvent.action = SI_MOVE;
         if ( newEvent.deviceType == MouseDeviceType )
         {
            newEvent.fValue = float( newData );

#ifdef LOG_INPUT
#ifdef LOG_MOUSEMOVE
            if ( newEvent.objType == SI_XAXIS )
               Input::log( "EVENT (DInput): %s move (%.1f, 0.0).\n", mName, newEvent.fValue );
            else if ( newEvent.objType == SI_YAXIS )
               Input::log( "EVENT (DInput): %s move (0.0, %.1f).\n", mName, newEvent.fValue );
            else
#endif
            if ( newEvent.objType == SI_ZAXIS )
               Input::log( "EVENT (DInput): %s wheel move %.1f.\n", mName, newEvent.fValue );
#endif
         }
         else  // Joystick or other device:
         {
            // Scale to the range -1.0 to 1.0:
            if ( objInfo.mMin != DIPROPRANGE_NOMIN && objInfo.mMax != DIPROPRANGE_NOMAX )
            {
               float range = float( objInfo.mMax - objInfo.mMin );
               //newEvent.fValue = float( newData - objInfo.mMin ) / range;
               newEvent.fValue = float( ( 2 * newData ) - objInfo.mMax - objInfo.mMin ) / range;
            }
            else
               newEvent.fValue = newData;

#ifdef LOG_INPUT
            // Keep this commented unless you REALLY want these messages for something--
            // they come once per each iteration of the main game loop.
            //switch ( newEvent.objType )
            //{
               //case SI_XAXIS:
                  //if ( newEvent.fValue < -0.01f || newEvent.fValue > 0.01f )
                     //Input::log( "EVENT (DInput): %s X-axis move %.2f.\n", mName, newEvent.fValue );
                  //break;
               //case SI_YAXIS:
                  //if ( newEvent.fValue < -0.01f || newEvent.fValue > 0.01f )
                     //Input::log( "EVENT (DInput): %s Y-axis move %.2f.\n", mName, newEvent.fValue );
                  //break;
               //case SI_ZAXIS:
                  //Input::log( "EVENT (DInput): %s Z-axis move %.1f.\n", mName, newEvent.fValue );
                  //break;
               //case SI_RXAXIS:
                  //Input::log( "EVENT (DInput): %s R-axis move %.1f.\n", mName, newEvent.fValue );
                  //break;
               //case SI_RYAXIS:
                  //Input::log( "EVENT (DInput): %s U-axis move %.1f.\n", mName, newEvent.fValue );
                  //break;
               //case SI_RZAXIS:
                  //Input::log( "EVENT (DInput): %s V-axis move %.1f.\n", mName, newEvent.fValue );
                  //break;
               //case SI_SLIDER:
                  //Input::log( "EVENT (DInput): %s slider move %.1f.\n", mName, newEvent.fValue );
                  //break;
            //};
#endif
         }

         Game->postEvent( newEvent );
         break;

      case SI_BUTTON:
         newEvent.action   = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK;
         newEvent.fValue   = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f;

#ifdef LOG_INPUT
         if ( newEvent.action == SI_MAKE )
            Input::log( "EVENT (DInput): %s button%d pressed. MODS:%c%c%c\n",
               mName, newEvent.objInst - KEY_BUTTON0, ( smModifierKeys & SI_SHIFT ? 'S' : '.' ), ( smModifierKeys & SI_CTRL ? 'C' : '.' ), ( smModifierKeys & SI_ALT ? 'A' : '.' ) );
         else
            Input::log( "EVENT (DInput): %s button%d released.\n", mName, newEvent.objInst - KEY_BUTTON0 );
#endif

         Game->postEvent( newEvent );
         break;

      case SI_KEY:
         newEvent.action   = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK;
         newEvent.fValue   = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f;
         processKeyEvent( newEvent );

#ifdef LOG_INPUT
         if ( newEvent.action == SI_MAKE )
            Input::log( "EVENT (DInput): %s key pressed. MODS:%c%c%c\n", getKeyName( newEvent.objInst ), ( smModifierKeys & SI_SHIFT ? 'S' : '.' ), ( smModifierKeys & SI_CTRL ? 'C' : '.' ), ( smModifierKeys & SI_ALT ? 'A' : '.' ) );
         else
            Input::log( "EVENT (DInput): %s key released.\n", getKeyName( newEvent.objInst ) );
#endif

         Game->postEvent( newEvent );
         break;

      case SI_POV:
//         newEvent.action   = SI_MOVE;
//          if ( LOWORD( newData ) == 0xffff )
//          {
//             newEvent.objInst  = ( newEvent.objInst == 0 ) ? SI_XPOV : SI_XPOV2;
//             newEvent.fValue   = 0.5f;
//             Game->postEvent( newEvent );
//
//             newEvent.objInst  = ( newEvent.objInst == 0 ) ? SI_YPOV : SI_YPOV2;
//             Game->postEvent( newEvent );
//          }
//          else
//          {
//             // Map 0-35999 data into 0.0-M_2PI fdata:
//             float fdata = float( newData ) * ( 1.0 / 36000.0f ) * M_2PI;
//             float x, y;
//             mSinCos( fdata, x, y );
//             x = ( x + 1.0f ) * 0.5f;
//             y = ( -y + 1.0f ) * 0.5f;
//             newEvent.objInst  = ( newEvent.objInst == 0 ) ? SI_XPOV : SI_XPOV2;
//             newEvent.fValue   = x;
// #ifdef LOG_INPUT
//             Input::log( "EVENT (DInput): POV X-axis %d at %.1f.\n",
//                   ( newEvent.objInst == SI_XPOV ) ? 1 : 2, newEvent.fValue );
// #endif
//             Game->postEvent( newEvent );
//
//             newEvent.objInst  = ( newEvent.objInst == 0 ) ? SI_YPOV : SI_YPOV2;
//             newEvent.fValue   = y;
// #ifdef LOG_INPUT
//             Input::log( "EVENT (DInput): POV Y-axis %d at %.1f.\n",
//                    ( newEvent.objInst == SI_YPOV ) ? 1 : 2, newEvent.fValue );
// #endif
//             Game->postEvent( newEvent );
//          }

         // Handle artificial POV up/down/left/right buttons

         // If we're not a polling device, oldData and newData are the same, so "fake" transitions
         if(!isPolled()) {
            oldData = mPrevPOVPos;
            mPrevPOVPos = newData;
         }

         newData = LOWORD( newData );
         oldData = LOWORD( oldData );
         newData = ( newData == 0xffff ) ? 5 : ( ( ( newData + 31500 ) / 9000 ) -3 ) & 0x03;
         oldData = ( oldData == 0xffff ) ? 5 : ( ( ( oldData + 31500 ) / 9000 ) -3 ) & 0x03;

         if ( newData != oldData )
         {
            if ( oldData != 5 )
            {
               newEvent.action = SI_BREAK;
               newEvent.fValue = 0.0f;
               switch ( oldData )
               {
                  case 0:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_UPOV : SI_UPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Up POV %d released.\n",
                           ( newEvent.objInst == SI_UPOV ) ? 1 : 2 );
#endif
                     break;

                  case 1:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_RPOV : SI_RPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Right POV %d released.\n",
                           ( newEvent.objInst == SI_RPOV ) ? 1 : 2 );
#endif
                     break;

                  case 2:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_DPOV : SI_DPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Down POV %d released.\n",
                           ( newEvent.objInst == SI_DPOV ) ? 1 : 2 );
#endif
                     break;

                  case 3:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_LPOV : SI_LPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Left POV %d released.\n",
                           ( newEvent.objInst == SI_LPOV ) ? 1 : 2 );
#endif
                     break;
               }
               Game->postEvent( newEvent );
            }

            if ( newData != 5 )
            {
               newEvent.action = SI_MAKE;
               newEvent.fValue = 1.0f;
               switch( newData )
               {
                  case 0:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_UPOV : SI_UPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Up POV %d pressed.\n",
                           ( newEvent.objInst == SI_UPOV ) ? 1 : 2 );
#endif
                     break;

                  case 1:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_RPOV : SI_RPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Right POV %d pressed.\n",
                           ( newEvent.objInst == SI_RPOV ) ? 1 : 2 );
#endif
                     break;

                  case 2:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_DPOV : SI_DPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Down POV %d pressed.\n",
                           ( newEvent.objInst == SI_DPOV ) ? 1 : 2 );
#endif
                     break;

                  case 3:
                     newEvent.objInst = ( newEvent.objInst == 0 ) ? SI_LPOV : SI_LPOV2;
#ifdef LOG_INPUT
                     Input::log( "EVENT (DInput): Left POV %d pressed.\n",
                           ( newEvent.objInst == SI_LPOV ) ? 1 : 2 );
#endif
                     break;
               }
               Game->postEvent( newEvent );
            }
         }
         break;
   }

   return true;
}

//------------------------------------------------------------------------------
//
// This function translates the DirectInput scan code to the associated
// internal key code (as defined in event.h).
//
//------------------------------------------------------------------------------
U16 DIK_to_Key( U8 dikCode )
{
   switch ( dikCode )
   {
      case DIK_ESCAPE:        return KEY_ESCAPE;

      case DIK_1:             return KEY_1;
      case DIK_2:             return KEY_2;
      case DIK_3:             return KEY_3;
      case DIK_4:             return KEY_4;
      case DIK_5:             return KEY_5;
      case DIK_6:             return KEY_6;
      case DIK_7:             return KEY_7;
      case DIK_8:             return KEY_8;
      case DIK_9:             return KEY_9;
      case DIK_0:             return KEY_0;

      case DIK_MINUS:         return KEY_MINUS;
      case DIK_EQUALS:        return KEY_EQUALS;
      case DIK_BACK:          return KEY_BACKSPACE;
      case DIK_TAB:           return KEY_TAB;

      case DIK_Q:             return KEY_Q;
      case DIK_W:             return KEY_W;
      case DIK_E:             return KEY_E;
      case DIK_R:             return KEY_R;
      case DIK_T:             return KEY_T;
      case DIK_Y:             return KEY_Y;
      case DIK_U:             return KEY_U;
      case DIK_I:             return KEY_I;
      case DIK_O:             return KEY_O;
      case DIK_P:             return KEY_P;

      case DIK_LBRACKET:      return KEY_LBRACKET;
      case DIK_RBRACKET:      return KEY_RBRACKET;
      case DIK_RETURN:        return KEY_RETURN;
      case DIK_LCONTROL:      return KEY_LCONTROL;

      case DIK_A:             return KEY_A;
      case DIK_S:             return KEY_S;
      case DIK_D:             return KEY_D;
      case DIK_F:             return KEY_F;
      case DIK_G:             return KEY_G;
      case DIK_H:             return KEY_H;
      case DIK_J:             return KEY_J;
      case DIK_K:             return KEY_K;
      case DIK_L:             return KEY_L;

      case DIK_SEMICOLON:     return KEY_SEMICOLON;
      case DIK_APOSTROPHE:    return KEY_APOSTROPHE;
      case DIK_GRAVE:         return KEY_TILDE;
      case DIK_LSHIFT:        return KEY_LSHIFT;
      case DIK_BACKSLASH:     return KEY_BACKSLASH;

      case DIK_Z:             return KEY_Z;
      case DIK_X:             return KEY_X;
      case DIK_C:             return KEY_C;
      case DIK_V:             return KEY_V;
      case DIK_B:             return KEY_B;
      case DIK_N:             return KEY_N;
      case DIK_M:             return KEY_M;

      case DIK_COMMA:         return KEY_COMMA;
      case DIK_PERIOD:        return KEY_PERIOD;
      case DIK_SLASH:         return KEY_SLASH;
      case DIK_RSHIFT:        return KEY_RSHIFT;
      case DIK_MULTIPLY:      return KEY_MULTIPLY;
      case DIK_LALT:          return KEY_LALT;
      case DIK_SPACE:         return KEY_SPACE;
      case DIK_CAPSLOCK:      return KEY_CAPSLOCK;

      case DIK_F1:            return KEY_F1;
      case DIK_F2:            return KEY_F2;
      case DIK_F3:            return KEY_F3;
      case DIK_F4:            return KEY_F4;
      case DIK_F5:            return KEY_F5;
      case DIK_F6:            return KEY_F6;
      case DIK_F7:            return KEY_F7;
      case DIK_F8:            return KEY_F8;
      case DIK_F9:            return KEY_F9;
      case DIK_F10:           return KEY_F10;

      case DIK_NUMLOCK:       return KEY_NUMLOCK;
      case DIK_SCROLL:        return KEY_SCROLLLOCK;

      case DIK_NUMPAD7:       return KEY_NUMPAD7;
      case DIK_NUMPAD8:       return KEY_NUMPAD8;
      case DIK_NUMPAD9:       return KEY_NUMPAD9;
      case DIK_SUBTRACT:      return KEY_SUBTRACT;

      case DIK_NUMPAD4:       return KEY_NUMPAD4;
      case DIK_NUMPAD5:       return KEY_NUMPAD5;
      case DIK_NUMPAD6:       return KEY_NUMPAD6;
      case DIK_ADD:           return KEY_ADD;

      case DIK_NUMPAD1:       return KEY_NUMPAD1;
      case DIK_NUMPAD2:       return KEY_NUMPAD2;
      case DIK_NUMPAD3:       return KEY_NUMPAD3;
      case DIK_NUMPAD0:       return KEY_NUMPAD0;
      case DIK_DECIMAL:       return KEY_DECIMAL;

      case DIK_F11:           return KEY_F11;
      case DIK_F12:           return KEY_F12;
      case DIK_F13:           return KEY_F13;
      case DIK_F14:           return KEY_F14;
      case DIK_F15:           return KEY_F15;

      case DIK_KANA:          return 0;
      case DIK_CONVERT:       return 0;
      case DIK_NOCONVERT:     return 0;
      case DIK_YEN:           return 0;
      case DIK_NUMPADEQUALS:  return 0;
      case DIK_CIRCUMFLEX:    return 0;
      case DIK_AT:            return 0;
      case DIK_COLON:         return 0;
      case DIK_UNDERLINE:     return 0;
      case DIK_KANJI:         return 0;
      case DIK_STOP:          return 0;
      case DIK_AX:            return 0;
      case DIK_UNLABELED:     return 0;

      case DIK_NUMPADENTER:   return KEY_NUMPADENTER;
      case DIK_RCONTROL:      return KEY_RCONTROL;
      case DIK_NUMPADCOMMA:   return KEY_SEPARATOR;
      case DIK_DIVIDE:        return KEY_DIVIDE;
      case DIK_SYSRQ:         return KEY_PRINT;
      case DIK_RALT:          return KEY_RALT;
      case DIK_PAUSE:         return KEY_PAUSE;

      case DIK_HOME:          return KEY_HOME;
      case DIK_UP:            return KEY_UP;
      case DIK_PGUP:          return KEY_PAGE_UP;
      case DIK_LEFT:          return KEY_LEFT;
      case DIK_RIGHT:         return KEY_RIGHT;
      case DIK_END:           return KEY_END;
      case DIK_DOWN:          return KEY_DOWN;
      case DIK_PGDN:          return KEY_PAGE_DOWN;
      case DIK_INSERT:        return KEY_INSERT;
      case DIK_DELETE:        return KEY_DELETE;

      case DIK_LWIN:          return KEY_WIN_LWINDOW;
      case DIK_RWIN:          return KEY_WIN_RWINDOW;
      case DIK_APPS:          return KEY_WIN_APPS;
      case DIK_OEM_102:       return KEY_OEM_102;
   }

   return KEY_NULL;
}

//------------------------------------------------------------------------------
//
// This function translates an internal key code to the associated
// DirectInput scan code
//
//------------------------------------------------------------------------------
U8 Key_to_DIK( U16 keyCode )
{
   switch ( keyCode )
   {
      case KEY_BACKSPACE:     return DIK_BACK;
      case KEY_TAB:           return DIK_TAB;
      case KEY_RETURN:        return DIK_RETURN;
      //KEY_CONTROL:
      //KEY_ALT:
      //KEY_SHIFT:
      case KEY_PAUSE:         return DIK_PAUSE;
      case KEY_CAPSLOCK:      return DIK_CAPSLOCK;
      case KEY_ESCAPE:        return DIK_ESCAPE;

      case KEY_SPACE:         return DIK_SPACE;
      case KEY_PAGE_DOWN:     return DIK_PGDN;
      case KEY_PAGE_UP:       return DIK_PGUP;
      case KEY_END:           return DIK_END;
      case KEY_HOME:          return DIK_HOME;
      case KEY_LEFT:          return DIK_LEFT;
      case KEY_UP:            return DIK_UP;
      case KEY_RIGHT:         return DIK_RIGHT;
      case KEY_DOWN:          return DIK_DOWN;
      case KEY_PRINT:         return DIK_SYSRQ;
      case KEY_INSERT:        return DIK_INSERT;
      case KEY_DELETE:        return DIK_DELETE;
      case KEY_HELP:          return 0;

      case KEY_0:             return DIK_0;
      case KEY_1:             return DIK_1;
      case KEY_2:             return DIK_2;
      case KEY_3:             return DIK_3;
      case KEY_4:             return DIK_4;
      case KEY_5:             return DIK_5;
      case KEY_6:             return DIK_6;
      case KEY_7:             return DIK_7;
      case KEY_8:             return DIK_8;
      case KEY_9:             return DIK_9;

      case KEY_A:             return DIK_A;
      case KEY_B:             return DIK_B;
      case KEY_C:             return DIK_C;
      case KEY_D:             return DIK_D;
      case KEY_E:             return DIK_E;
      case KEY_F:             return DIK_F;
      case KEY_G:             return DIK_G;
      case KEY_H:             return DIK_H;
      case KEY_I:             return DIK_I;
      case KEY_J:             return DIK_J;
      case KEY_K:             return DIK_K;
      case KEY_L:             return DIK_L;
      case KEY_M:             return DIK_M;
      case KEY_N:             return DIK_N;
      case KEY_O:             return DIK_O;
      case KEY_P:             return DIK_P;
      case KEY_Q:             return DIK_Q;
      case KEY_R:             return DIK_R;
      case KEY_S:             return DIK_S;
      case KEY_T:             return DIK_T;
      case KEY_U:             return DIK_U;
      case KEY_V:             return DIK_V;
      case KEY_W:             return DIK_W;
      case KEY_X:             return DIK_X;
      case KEY_Y:             return DIK_Y;
      case KEY_Z:             return DIK_Z;

      case KEY_TILDE:         return DIK_GRAVE;
      case KEY_MINUS:         return DIK_MINUS;
      case KEY_EQUALS:        return DIK_EQUALS;
      case KEY_LBRACKET:      return DIK_LBRACKET;
      case KEY_RBRACKET:      return DIK_RBRACKET;
      case KEY_BACKSLASH:     return DIK_BACKSLASH;
      case KEY_SEMICOLON:     return DIK_SEMICOLON;
      case KEY_APOSTROPHE:    return DIK_APOSTROPHE;
      case KEY_COMMA:         return DIK_COMMA;
      case KEY_PERIOD:        return DIK_PERIOD;
      case KEY_SLASH:         return DIK_SLASH;

      case KEY_NUMPAD0:       return DIK_NUMPAD0;
      case KEY_NUMPAD1:       return DIK_NUMPAD1;
      case KEY_NUMPAD2:       return DIK_NUMPAD2;
      case KEY_NUMPAD3:       return DIK_NUMPAD3;
      case KEY_NUMPAD4:       return DIK_NUMPAD4;
      case KEY_NUMPAD5:       return DIK_NUMPAD5;
      case KEY_NUMPAD6:       return DIK_NUMPAD6;
      case KEY_NUMPAD7:       return DIK_NUMPAD7;
      case KEY_NUMPAD8:       return DIK_NUMPAD8;
      case KEY_NUMPAD9:       return DIK_NUMPAD9;
      case KEY_MULTIPLY:      return DIK_MULTIPLY;
      case KEY_ADD:           return DIK_ADD;
      case KEY_SEPARATOR:     return DIK_NUMPADCOMMA;
      case KEY_SUBTRACT:      return DIK_SUBTRACT;
      case KEY_DECIMAL:       return DIK_DECIMAL;
      case KEY_DIVIDE:        return DIK_DIVIDE;
      case KEY_NUMPADENTER:   return DIK_NUMPADENTER;

      case KEY_F1:            return DIK_F1;
      case KEY_F2:            return DIK_F2;
      case KEY_F3:            return DIK_F3;
      case KEY_F4:            return DIK_F4;
      case KEY_F5:            return DIK_F5;
      case KEY_F6:            return DIK_F6;
      case KEY_F7:            return DIK_F7;
      case KEY_F8:            return DIK_F8;
      case KEY_F9:            return DIK_F9;
      case KEY_F10:           return DIK_F10;
      case KEY_F11:           return DIK_F11;
      case KEY_F12:           return DIK_F12;
      case KEY_F13:           return DIK_F13;
      case KEY_F14:           return DIK_F14;
      case KEY_F15:           return DIK_F15;
      case KEY_F16:
      case KEY_F17:
      case KEY_F18:
      case KEY_F19:
      case KEY_F20:
      case KEY_F21:
      case KEY_F22:
      case KEY_F23:
      case KEY_F24:           return 0;

      case KEY_NUMLOCK:       return DIK_NUMLOCK;
      case KEY_SCROLLLOCK:    return DIK_SCROLL;
      case KEY_LCONTROL:      return DIK_LCONTROL;
      case KEY_RCONTROL:      return DIK_RCONTROL;
      case KEY_LALT:          return DIK_LALT;
      case KEY_RALT:          return DIK_RALT;
      case KEY_LSHIFT:        return DIK_LSHIFT;
      case KEY_RSHIFT:        return DIK_RSHIFT;

      case KEY_WIN_LWINDOW:   return DIK_LWIN;
      case KEY_WIN_RWINDOW:   return DIK_RWIN;
      case KEY_WIN_APPS:      return DIK_APPS;
      case KEY_OEM_102:       return DIK_OEM_102;

   };

   return 0;
}

#ifdef LOG_INPUT
//------------------------------------------------------------------------------
const char* getKeyName( U16 key )
{
   switch ( key )
   {
      case KEY_BACKSPACE:     return "Backspace";
      case KEY_TAB:           return "Tab";
      case KEY_RETURN:        return "Return";
      case KEY_PAUSE:         return "Pause";
      case KEY_CAPSLOCK:      return "CapsLock";
      case KEY_ESCAPE:        return "Esc";

      case KEY_SPACE:         return "SpaceBar";
      case KEY_PAGE_DOWN:     return "PageDown";
      case KEY_PAGE_UP:       return "PageUp";
      case KEY_END:           return "End";
      case KEY_HOME:          return "Home";
      case KEY_LEFT:          return "Left";
      case KEY_UP:            return "Up";
      case KEY_RIGHT:         return "Right";
      case KEY_DOWN:          return "Down";
      case KEY_PRINT:         return "PrintScreen";
      case KEY_INSERT:        return "Insert";
      case KEY_DELETE:        return "Delete";
      case KEY_HELP:          return "Help";

      case KEY_NUMPAD0:       return "Numpad 0";
      case KEY_NUMPAD1:       return "Numpad 1";
      case KEY_NUMPAD2:       return "Numpad 2";
      case KEY_NUMPAD3:       return "Numpad 3";
      case KEY_NUMPAD4:       return "Numpad 4";
      case KEY_NUMPAD5:       return "Numpad 5";
      case KEY_NUMPAD6:       return "Numpad 6";
      case KEY_NUMPAD7:       return "Numpad 7";
      case KEY_NUMPAD8:       return "Numpad 8";
      case KEY_NUMPAD9:       return "Numpad 9";
      case KEY_MULTIPLY:      return "Multiply";
      case KEY_ADD:           return "Add";
      case KEY_SEPARATOR:     return "Separator";
      case KEY_SUBTRACT:      return "Subtract";
      case KEY_DECIMAL:       return "Decimal";
      case KEY_DIVIDE:        return "Divide";
      case KEY_NUMPADENTER:   return "Numpad Enter";

      case KEY_F1:            return "F1";
      case KEY_F2:            return "F2";
      case KEY_F3:            return "F3";
      case KEY_F4:            return "F4";
      case KEY_F5:            return "F5";
      case KEY_F6:            return "F6";
      case KEY_F7:            return "F7";
      case KEY_F8:            return "F8";
      case KEY_F9:            return "F9";
      case KEY_F10:           return "F10";
      case KEY_F11:           return "F11";
      case KEY_F12:           return "F12";
      case KEY_F13:           return "F13";
      case KEY_F14:           return "F14";
      case KEY_F15:           return "F15";
      case KEY_F16:           return "F16";
      case KEY_F17:           return "F17";
      case KEY_F18:           return "F18";
      case KEY_F19:           return "F19";
      case KEY_F20:           return "F20";
      case KEY_F21:           return "F21";
      case KEY_F22:           return "F22";
      case KEY_F23:           return "F23";
      case KEY_F24:           return "F24";

      case KEY_NUMLOCK:       return "NumLock";
      case KEY_SCROLLLOCK:    return "ScrollLock";
      case KEY_LCONTROL:      return "LCtrl";
      case KEY_RCONTROL:      return "RCtrl";
      case KEY_LALT:          return "LAlt";
      case KEY_RALT:          return "RAlt";
      case KEY_LSHIFT:        return "LShift";
      case KEY_RSHIFT:        return "RShift";

      case KEY_WIN_LWINDOW:   return "LWin";
      case KEY_WIN_RWINDOW:   return "RWin";
      case KEY_WIN_APPS:      return "Apps";
   }

   static char returnString[5];
   dSprintf( returnString, sizeof( returnString ), "%c", Input::getAscii( key, STATE_UPPER ) );
   return returnString;
}
#endif // LOG_INPUT

//------------------------------------------------------------------------------
const char* DInputDevice::getJoystickAxesString()
{
   if ( mDeviceType != JoystickDeviceType )
      return( "" );

   U32 axisCount = mDeviceCaps.dwAxes;
   char buf[64];
   dSprintf( buf, sizeof( buf ), "%d", axisCount );
   for ( U32 i = 0; i < mObjCount; i++ )
   {
      switch ( mObjInfo[i].mType )
      {
         case SI_XAXIS:
            dStrcat( buf, "\tX" );
            break;
         case SI_YAXIS:
            dStrcat( buf, "\tY" );
            break;
         case SI_ZAXIS:
            dStrcat( buf, "\tZ" );
            break;
         case SI_RXAXIS:
            dStrcat( buf, "\tR" );
            break;
         case SI_RYAXIS:
            dStrcat( buf, "\tU" );
            break;
         case SI_RZAXIS:
            dStrcat( buf, "\tV" );
            break;
         case SI_SLIDER:
            dStrcat( buf, "\tS" );
            break;
      }
   }

   char* returnString = Con::getReturnBuffer( dStrlen( buf ) + 1 );
   dStrcpy( returnString, buf );
   return( returnString );
}

//------------------------------------------------------------------------------
bool DInputDevice::joystickDetected()
{
   return( smJoystickCount > 0  );
}



