/*****************************************************************************
 * $Id: joystick.cc,v 1.42 2006/08/01 23:14:02 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Conditionally compile OS-specific joystick code.
 * @remarks	Hardcoded for 3-axis joysticks but supports 2-axis joysticks.
 *          Documented at joystick.hh
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

/// @namespace gfxInput
/// @brief Input library namespace.

#define GFXINPUT_JOYSTICK_CC 1
#include "joystick.hh"
#include "gfx.hh"
#include "gfx_gfxsys.hh"
using namespace gfx;

//----------------------------------------------------------------------------
// Linux
#if defined(linux)
#include "joystick_linux.cc"
// Win32
#elif defined(_WIN32)
#include "joystick_win32.cc"
// Unsupported operating systems (dummy functions)
#else
namespace gfxInput {
bool	Joystick::PollEvent( void ) { return false; }
bool	Joystick::Open( void ) { return false; }
void	Joystick::Close( void ) { }
} // namespace gfxInput
#endif
//----------------------------------------------------------------------------

namespace gfxInput {

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
Joystick::Joystick( void )
{
ASSERT( JOY_HI == 32767 ); // joystick_win32.cc depends on JOY_HI == 32767
	mEnabled = false;
	ClearEvent();
	CalibrateDefault();
}

/*****************************************************************************
 * Clear event.
 *****************************************************************************/
void
Joystick::ClearEvent( void )
{
	mEvent.Clear();
}

/*****************************************************************************
 * Enable/disable joystick.
 * @return True if joystick device is enabled and present, else false.
 *****************************************************************************/
bool
Joystick::Enable( bool flag )
{
	if ( flag )
	{
		mEnabled = Open();
	}
	else
	{
		mEnabled = false;
		Close();
	}
	return mEnabled;
}

/*****************************************************************************
 * Default joystick calibration.
 *****************************************************************************/
bool
Joystick::CalibrateDefault( void )
{
	static int dir[3] = { 1, 1, 1 };
	return    CalibrateDirection( dir )
           && CalibrateMin( JOY_LO, JOY_LO, JOY_LO, JOY_LO, JOY_LO, JOY_LO )
           && CalibrateMax( JOY_HI, JOY_HI, JOY_HI, JOY_HI, JOY_HI, JOY_HI );
}

/*****************************************************************************
 * Check/correct joystick value.
 * SIDE-EFFECT: May return a value with a different sign.
 *****************************************************************************/
void
Joystick::CheckJoyvalMin( int* val )
{
	*val = ABS(*val);
	if ( *val == 0 )     { *val = JOY_LO; return; } // default if eg atoi(argv[]) returns 0
	if ( *val > JOY_HI ) { *val = JOY_LO; return; } // if way off
}
void
Joystick::CheckJoyvalMax( int* val )
{
	*val = ABS(*val);
	if ( *val == 0 )     { *val = JOY_HI; return; } // default if eg atoi(argv[]) returns 0
	if ( *val > JOY_HI ) { *val = JOY_HI; return; } // if way off
}

/*****************************************************************************
 * Set joystick calibration (range of axises).
 * Can tolerate bad args (from cmd-line).
 * The sign of the args don't matter.
 *****************************************************************************/
bool
Joystick::CalibrateMin( int left, int right,
                        int up,   int down,
                        int slow, int fast ) // throttle analogy
{
	// Check/fix joystick value.
	// CheckJoyval*() may return value with different sign.
	CheckJoyvalMin(&left);
	CheckJoyvalMin(&right);
	CheckJoyvalMin(&up);
	CheckJoyvalMin(&down);
	CheckJoyvalMin(&slow);
	CheckJoyvalMin(&fast);

	// Assign appropriate signs independent of passed/corrected values.
	mCalibration.mLeft.mLo  = -(ABS(left));
	mCalibration.mRight.mLo =  (ABS(right));
	mCalibration.mUp.mLo    = -(ABS(up));
	mCalibration.mDown.mLo	=  (ABS(down));
	mCalibration.mSlow.mLo  = -(ABS(slow));
	mCalibration.mFast.mLo  =  (ABS(fast));
	return true;
}
bool
Joystick::CalibrateMax( int left, int right,
                        int up,   int down,
                        int slow, int fast ) // throttle analogy
{
	// Check/fix joystick value.
	// CheckJoyval*() may return value with different sign.
	CheckJoyvalMax(&left);
	CheckJoyvalMax(&right);
	CheckJoyvalMax(&up);
	CheckJoyvalMax(&down);
	CheckJoyvalMax(&slow);
	CheckJoyvalMax(&fast);

	// Assign appropriate signs independent of passed/corrected values.
	mCalibration.mLeft.mHi  = -(ABS(left));
	mCalibration.mRight.mHi =  (ABS(right));
	mCalibration.mUp.mHi    = -(ABS(up));
	mCalibration.mDown.mHi	=  (ABS(down));
	mCalibration.mSlow.mHi  = -(ABS(slow));
	mCalibration.mFast.mHi  =  (ABS(fast));
	return true;
}

/*****************************************************************************
 * Set joystick center/deadzone.
 * Can tolerate bad args (from cmd-line).
 * Not as accurate since same deadzone value is reused for both directions
 * but is convenient.
 *****************************************************************************/
bool
Joystick::CalibrateDeadzone( const int dz[3] )
{
	// CalibrateMin() can tolerate bad args that may come from the cmd-line.
	return CalibrateMin( dz[0], dz[0],
                         dz[1], dz[1],
                         dz[2], dz[2] );
}

/*****************************************************************************
 * Set joystick axis directions.
 * Can tolerate bad args (from cmd-line).
 *****************************************************************************/
bool
Joystick::CalibrateDirection( const int d[3] )
{
	for ( int i = 0; i < 3; ++i )
		mAxisDir[i] = (d[i]==1||d[i]==-1) ? d[i] : 1; // ensure +-1
	return true;
}

} // namespace gfxInput
