/*
     PLIB - A Suite of Portable Game Libraries
     Copyright (C) 1998,2002  Steve Baker

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
     License as published by the Free Software Foundation; either
     version 2 of the License, or (at your option) any later version.

     This library 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
     Library General Public License for more details.

     You should have received a copy of the GNU Library General Public
     License along with this library; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

     For further information visit http://plib.sourceforge.net

     $Id: jsLinux.cxx,v 1.1 2007/01/02 21:48:42 jsd Exp jsd $
*/

#include "js.h"

#if defined (UL_LINUX)

#include <linux/joystick.h>

#if defined(JS_VERSION) && JS_VERSION >= 0x010000

#include <sys/param.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#	include <iostream>

struct os_specific_s {
  js_event     js          ;
  v_bool       tmp_buttons ;
  v_float      tmp_axes  ;
  char         fname [ 128 ] ;
  int          fd ;
};

void jsInit () {}

void jsJoystick::open ()
{
  name [0] = '\0' ;

  os->fd = ::open ( os->fname, O_RDONLY ) ;

  error = ( os->fd < 0 ) ;

  if ( error )
    return ;

  /*
    Set the correct number of axes for the linux driver
  */

  /* Melchior Franz's fixes for big-endian Linuxes since writing 
   *  to the upper byte of an uninitialized word doesn't work. 
   *  9 April 2003 
   */
  unsigned char u ;
  ioctl ( os->fd, JSIOCGAXES   , &u ) ; 
  num_axes = u ;
  ioctl ( os->fd, JSIOCGBUTTONS, &u ) ;
  num_buttons = u + JS_SHIFT;
  ioctl ( os->fd, JSIOCGNAME ( sizeof(name) ), name ) ;
  fcntl ( os->fd, F_SETFL      , O_NONBLOCK   ) ;

  if ( num_axes > _JS_MAX_AXES )
    num_axes = _JS_MAX_AXES ;

  // Remove any deadband value already done in the kernel.
  // Since we have our own deadband management this is safe to do.
  struct js_corr corr [ _JS_MAX_AXES ] ;
  ioctl ( os->fd, JSIOCGCORR, corr );
  for ( int i = 0; i < num_axes ; ++i ) {
    if ( corr[ i ] . type == JS_CORR_BROKEN ) {
      int nodeadband = ( corr[ i ] . coef[ 0 ] + corr[ i ] . coef[ 1 ] ) / 2 ;
      corr[ i ] . coef[ 0 ] = nodeadband ;
      corr[ i ] . coef[ 1 ] = nodeadband ;
    }
  }
  ioctl ( os->fd, JSIOCSCORR, corr );
  
  dead_band.resize(num_axes);
  saturate.resize(num_axes);
  center.resize(num_axes);
  max.resize(num_axes);
  min.resize(num_axes);

  for ( int i = 0 ; i < num_axes ; i++ )
  {
    max       [ i ] = 32767.0f ;
    center    [ i ] = 0.0f ;
    min       [ i ] = -32767.0f ;
    dead_band [ i ] = 0.0f ;
    saturate  [ i ] = 1.0f ;
  }

}



void jsJoystick::close ()
{
  if ( ! error )
    ::close ( os->fd ) ;
  delete os;
}


jsJoystick::jsJoystick ( int ident )
{
  id = ident ;
  os = new struct os_specific_s;

  sprintf ( os->fname, "/dev/input/js%d", ident ) ;

  if ( access ( os->fname, F_OK ) != 0 )
    sprintf ( os->fname, "/dev/js%d", ident ) ;

  open () ;
}


void jsJoystick::rawRead ( v_bool& buttons, v_float& axes )
{

  if ( error )
  {
    buttons.clear();
    axes.clear();
    return ;
  }


  while (1)
  {
    int status = ::read ( os->fd, &(os->js), sizeof(js_event) ) ;

    if ( status != sizeof(js_event) )
    {
      /* use the old values */

      buttons = os->tmp_buttons ;
      axes    = os->tmp_axes;

      if ( errno == EAGAIN )
        return ;

      perror( os->fname ) ;
      setError () ;
      return ;
    }

    os->tmp_buttons.resize(num_buttons, 0);
    os->tmp_axes.resize(num_axes, 0);

    unsigned int chan = os->js.number;
    if (chan != 0) {		// never mess with button channel zero
      if (os->tmp_buttons[0]) chan += JS_SHIFT;
    }
    switch ( os->js.type & ~JS_EVENT_INIT )
    {
      case JS_EVENT_BUTTON :
        os->tmp_buttons[chan] = os->js.value;
        break ;

      case JS_EVENT_AXIS:
        os->tmp_axes [ chan ] = (float) os->js.value ;
        break ;

      default:
        ulSetError ( UL_WARNING, "PLIB_JS: Unrecognised /dev/js return!?!" ) ;
        /* use the old values */
	break;
    }

    buttons = os->tmp_buttons ;
    axes    = os->tmp_axes;
    return ;
  }
}

// For compatibility with old callers.
// Can handle at most 32 buttons (the number of bits in an int).
void jsJoystick::rawRead ( int *buttons, float *axes )
{
  if ( error )
  {
    if ( buttons )
      *buttons = 0 ;

    if ( axes )
      for ( int i = 0 ; i < num_axes ; i++ )
        axes[i] = 1500.0f ;

    return ;
  }

  v_bool mybuttons;
  v_float myaxes;
  rawRead(mybuttons, myaxes);

  if (buttons)
  {
    *buttons = 0;
    unsigned int bbb(1);	// the walking bit
    unsigned int lim(8*sizeof(*buttons));
    if (mybuttons.size() < lim) lim = mybuttons.size();
    for (unsigned int ii = 0; ii < lim; ii++)
    {
      if (mybuttons[ii]) *buttons |= bbb;
      bbb <<= 1;
    }
  }

  if (axes)
    for (int ii = 0; ii < num_axes; ii++)
    {
      axes[ii] = myaxes[ii];
    }
  // that's all
}

#endif
#endif
