/////////////////////////////////////////////////////////////////////////////
// Name:        joystick.cpp
// Purpose:     wxJoystick class
// Author:      Ported to Linux by Guilhem Lavaux
// Modified by:
// Created:     05/23/98
// RCS-ID:      $Id: joystick.cpp,v 1.10 2002/08/20 17:16:05 JS Exp $
// Copyright:   (c) Guilhem Lavaux
// Licence:   	wxWindows license
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
#pragma implementation "joystick.h"
#endif

#include "wx/defs.h"

#if wxUSE_JOYSTICK

#include "wx/joystick.h"

#include <linux/joystick.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

#include "wx/event.h"
#include "wx/window.h"

#define JOYSTICK_AXE_MAX 32767
#define JOYSTICK_AXE_MIN -32767

IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)

wxJoystick::wxJoystick(int joystick)
{
  wxString dev_name;
  // Assume it's the same device name on all Linux systems ...
  dev_name.Printf( wxT("/dev/js%d"), (joystick == wxJOYSTICK1) ? 0 : 1);  // FIXME Unicode?

  m_joystick = open(dev_name.fn_str(), O_RDWR);
  m_lastposition = wxPoint(-1, -1);
  for (int i=0;i<15;i++)
    m_axe[i] = 0;
  if (m_joystick != -1)
    Create();
}

////////////////////////////////////////////////////////////////////////////
// Background thread
////////////////////////////////////////////////////////////////////////////
void *wxJoystick::Entry(void)
{
  struct js_event j_evt;
  wxJoystickEvent jwx_event;
  fd_set read_fds;
  struct timeval time_out = {0, 0};

  FD_ZERO(&read_fds);
  while (1) {
    TestDestroy();

    if (m_polling) {
      FD_SET(m_joystick, &read_fds);
      select(m_joystick+1, &read_fds, NULL, NULL, &time_out);
      if (FD_ISSET(m_joystick, &read_fds))
        read(m_joystick, &j_evt, sizeof(j_evt));
      else
        j_evt.type = 0;
    } else {
      read(m_joystick, &j_evt, sizeof(j_evt));
    }

    if ((j_evt.type & JS_EVENT_AXIS) == JS_EVENT_AXIS) {
      switch (j_evt.number) {
      case 1:
        m_lastposition.x = j_evt.value;
        jwx_event.SetEventType(wxEVT_JOY_MOVE);
        break;
      case 2:
        m_lastposition.y = j_evt.value;
        jwx_event.SetEventType(wxEVT_JOY_MOVE);
        break;
      case 3:
        m_axe[3] = j_evt.value;
        jwx_event.SetEventType(wxEVT_JOY_ZMOVE);
        break;
      default:
        m_axe[j_evt.number] = j_evt.value;
        jwx_event.SetEventType(wxEVT_JOY_MOVE);
        break;
      }
      jwx_event.SetPosition(m_lastposition);
      jwx_event.SetZPosition(m_axe[3]);
    }
    if ((j_evt.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) {
      register int mask = 1 << j_evt.number;
      char button = m_buttons & mask;

      m_buttons &= ~mask;
      if (button) {
        jwx_event.SetEventType(wxEVT_JOY_BUTTON_UP);
      } else {
        jwx_event.SetEventType(wxEVT_JOY_BUTTON_DOWN);
        m_buttons |= mask;
      }

      jwx_event.SetButtonState(m_buttons);
      jwx_event.SetButtonChange(j_evt.number);
    }
  }
  if (m_catchwin)
    m_catchwin->ProcessEvent(jwx_event);
  if (m_polling)
    usleep(m_polling*1000);
}

////////////////////////////////////////////////////////////////////////////
// State
////////////////////////////////////////////////////////////////////////////

wxPoint wxJoystick::GetPosition(void) const
{
  return m_lastposition;
}

int wxJoystick::GetZPosition(void) const
{
  return m_axe[3];
}

int wxJoystick::GetButtonState(void) const
{
  return m_buttons;
}

int wxJoystick::GetPOVPosition(void) const
{
  return -1;
}

int wxJoystick::GetPOVCTSPosition(void) const
{
  return -1;
}

int wxJoystick::GetRudderPosition(void) const
{
  return m_axe[4];
}

int wxJoystick::GetUPosition(void) const
{
  return m_axe[5];
}

int wxJoystick::GetVPosition(void) const
{
  return m_axe[6];
}

int wxJoystick::GetMovementThreshold(void) const
{
  return 0;
}

void wxJoystick::SetMovementThreshold(int threshold)
{
}

////////////////////////////////////////////////////////////////////////////
// Capabilities
////////////////////////////////////////////////////////////////////////////

bool wxJoystick::IsOk(void) const
{
  return (m_joystick != -1);
}

int wxJoystick::GetNumberJoysticks(void) const
{
  wxString dev_name;
  int fd, j;

  for (j=0;j<2;j++) {
    dev_name.Printf(wxT("/dev/js%d"), j);
    fd = open(dev_name.fn_str(), O_RDONLY);
    if (fd == -1)
      return j;
    close(fd);
  }
  return j;
}

int wxJoystick::GetManufacturerId(void) const
{
  return 0;
}

int wxJoystick::GetProductId(void) const
{
  return 0;
}

wxString wxJoystick::GetProductName(void) const
{
  wxString dev_name;
  // 2002-08-20 johan@linkdata.se
  // Return the device name in lieu of a better one
  dev_name.Printf( wxT("/dev/js%d"), (m_joystick == wxJOYSTICK1) ? 0 : 1);  // FIXME Unicode?
  return dev_name;
}

int wxJoystick::GetXMin(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetYMin(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetZMin(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetXMax(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetYMax(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetZMax(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetNumberButtons(void) const
{
  int nb;

  ioctl(m_joystick, JSIOCGBUTTONS, &nb);

  return nb;
}

int wxJoystick::GetNumberAxes(void) const
{
  int nb;

  ioctl(m_joystick, JSIOCGAXES, &nb);

  return nb;
}

int wxJoystick::GetMaxButtons(void) const
{
  return 15; // internal
}

int wxJoystick::GetMaxAxes(void) const
{
  return 15; // internal
}

int wxJoystick::GetPollingMin(void) const
{
  return -1;
}

int wxJoystick::GetPollingMax(void) const
{
  return -1;
}

int wxJoystick::GetRudderMin(void) const
{
  return JOYSTICK_AXE_MIN;
}

int wxJoystick::GetRudderMax(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetUMin(void) const
{
  return JOYSTICK_AXE_MIN;
}

int wxJoystick::GetUMax(void) const
{
  return JOYSTICK_AXE_MAX;
}

int wxJoystick::GetVMin(void) const
{
  return JOYSTICK_AXE_MIN;
}

int wxJoystick::GetVMax(void) const
{
  return JOYSTICK_AXE_MAX;
}

bool wxJoystick::HasRudder(void) const
{
  return GetNumberAxes() >= 4;
}

bool wxJoystick::HasZ(void) const
{
  return GetNumberAxes() >= 3;
}

bool wxJoystick::HasU(void) const
{
  return GetNumberAxes() >= 5;
}

bool wxJoystick::HasV(void) const
{
  return GetNumberAxes() >= 6;
}

bool wxJoystick::HasPOV(void) const
{
  return FALSE;
}

bool wxJoystick::HasPOV4Dir(void) const
{
  return FALSE;
}

bool wxJoystick::HasPOVCTS(void) const
{
  return FALSE;
}

////////////////////////////////////////////////////////////////////////////
// Operations
////////////////////////////////////////////////////////////////////////////

bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
{
  m_catchwin = win;
  m_polling = pollingFreq;
  return TRUE;
}

bool wxJoystick::ReleaseCapture(void)
{
  m_catchwin = NULL;
  m_polling = 0;
  return TRUE;
}
#endif  // wxUSE_JOYSTICK

