//-----------------------------------------------------------------------------------
//
//   Torque Network Library - ZAP example multiplayer vector graphics space game
//   Copyright (C) 2004 GarageGames.com, Inc.
//   For more information see http://www.opentnl.org
//
//   This program 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.
//
//   For use in products that are not compatible with the terms of the GNU 
//   General Public License, alternative licensing options are available 
//   from GarageGames.com.
//
//   This program 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 this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//------------------------------------------------------------------------------------

#define STRICT
#define DIRECTINPUT_VERSION 0x0800
#include <windows.h>
#include <dinput.h>

#include "gameConnection.h"
#include "point.h"
#include "UI.h"
#include "input.h"

namespace Zap
{

extern const char *gWindowTitle;

void getModifierState( bool &shiftDown, bool &controlDown, bool &altDown )
{
	shiftDown = ( GetKeyState( VK_SHIFT ) & 0xFF00 ) != 0;
	controlDown = ( GetKeyState( VK_CONTROL ) & 0xFF00 ) != 0;
	altDown = ( GetKeyState( VK_MENU ) & 0xFF00 ) != 0;
}

void checkMousePos(S32 maxdx, S32 maxdy)
{
   char windowName[256];

   HWND theWindow = GetForegroundWindow();
   GetWindowText(theWindow, windowName, sizeof(windowName));

   if(strcmp(windowName, gWindowTitle))
      return;

   RECT r;

   GetWindowRect(theWindow, &r);
   POINT cp;
   GetCursorPos(&cp);

   // Check our position in the window; if we're close to the top (within 32px) then ignore
   // same if we're within 5px of the edges. That way we can avoid going crazy when
   // people try to resize/drag the window.
   if(cp.x - r.top < 32)   return;
   if(cp.y - r.left < 5)   return;
   if(r.right - cp.y < 5)  return;
   if(r.bottom - cp.x < 5) return;

   S32 centerX = (r.right + r.left) >> 1;
   S32 centerY = (r.bottom + r.top) >> 1;

   cp.x -= centerX;
   cp.y -= centerY;

   if(cp.x > maxdx)
      cp.x = maxdx;
   if(cp.y > maxdy)
      cp.y = maxdy;
   if(cp.x < -maxdx)
      cp.x = -maxdx;
   if(cp.y < -maxdy)
      cp.y = -maxdy;

   SetCursorPos(int(centerX + cp.x), int(centerY + cp.y));
}

BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext );

LPDIRECTINPUT8 gDirectInput = NULL;
LPDIRECTINPUTDEVICE8 gJoystick = NULL;
char gJoystickName[MAX_PATH] = "";

void InitJoystick()
{
   if(FAILED(DirectInput8Create ( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
         IID_IDirectInput8, (VOID**)&gDirectInput, NULL ) ) )
      return;

   if(FAILED(gDirectInput->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback,
         NULL, DIEDFL_ATTACHEDONLY ) ) )
      return;

   if(!gJoystick)
      return;

   if( FAILED(gJoystick->SetDataFormat( &c_dfDIJoystick2 ) ) )
      return;
}

const char *GetJoystickName()
{
   return gJoystickName;
}

BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
                                     VOID* pContext )
{
   // Obtain an interface to the enumerated joystick.
   if(FAILED(gDirectInput->CreateDevice( pdidInstance->guidInstance, &gJoystick, NULL )))
      return DIENUM_CONTINUE;
   strcpy(gJoystickName, pdidInstance->tszProductName);
   logprintf("Joystick found: %s", gJoystickName);
   return DIENUM_STOP;
}


bool ReadJoystick(F32 axes[MaxJoystickAxes], U32 &buttonMask, U32 &hatMask)
{
   // mark: it's ok
   // mark: it's called "winJoystick"
   // mark: it's supposed to be gross.

   DIJOYSTATE2 js;       // DInput joystick state 

   if(!gJoystick)
      return false;

   if(FAILED(gJoystick->Poll() ) )
   {
      HRESULT hr;
      hr = gJoystick->Acquire();
      
      while( hr == DIERR_INPUTLOST ) 
         hr = gJoystick->Acquire();
      return false; 
   }

   // Get the input's device state
   if(FAILED(gJoystick->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) )
      return false; // The device should have been acquired during the Poll()

   F32 scale = 1 / 32768.0f;
   axes[0] = (F32(js.lX) - 32768.0f) * scale;
   axes[1] = (F32(js.lY) - 32768.0f) * scale;
   axes[2] = (F32(js.lZ) - 32768.0f) * scale;
   axes[3] = (F32(js.lRx) - 32768.0f) * scale;
   axes[4] = (F32(js.lRy) - 32768.0f) * scale;
   axes[5] = (F32(js.lRz) - 32768.0f) * scale;
   axes[6] = (F32(js.rglSlider[0]) - 32768.0f) * scale;
   axes[7] = (F32(js.rglSlider[1]) - 32768.0f) * scale;
   axes[8] = 0;
   axes[9] = 0;
   axes[10] = 0;
   axes[11] = 0;

   // check the state of the buttons:
   buttonMask = 0;
   U32 pov = js.rgdwPOV[0];

   for( U32 i = 0; i < 12; i++ )
      if((js.rgbButtons[i] & 0x80) != 0)
         buttonMask |= BIT(i);

   switch(pov)
   {
   case 0:
      hatMask |= ControllerButtonDPadUp;
      break;
   case 4500:
      hatMask |= ControllerButtonDPadUp | ControllerButtonDPadRight;
      break;
   case 9000:
      hatMask |= ControllerButtonDPadRight;
      break; 
   case 13500:
      hatMask |= ControllerButtonDPadRight | ControllerButtonDPadDown;
      break;
   case 18000:
      hatMask |= ControllerButtonDPadDown;
      break;
   case 22500:
      hatMask |= ControllerButtonDPadDown | ControllerButtonDPadLeft;
      break;
   case 27000:
      hatMask |= ControllerButtonDPadLeft;
      break;
   case 31500:
      hatMask |= ControllerButtonDPadLeft | ControllerButtonDPadUp;
      break;
   }
   return true;
}

void ShutdownJoystick()
{
    // Unacquire the device one last time just in case 
    // the app tried to exit while the device is still acquired.
    if( gJoystick ) 
        gJoystick->Unacquire();
    
    // Release any DirectInput objects.
    if(gJoystick)
      gJoystick->Release();
    if(gDirectInput)
      gDirectInput->Release();
}

};
