/*****************************************************************************
 * $Id: joystick_win32.cc,v 1.37 2006/08/01 23:14:02 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Joystick functions (Win32).  This is actually a header file.
 * @remarks - This code is obsolete.  It can only be compiled with Visual C++
 *            but VC++ makefiles were discarded.
 *          - Documented at joystick.hh
 *			- Typically, the range of a joystick axis on Win32 is 0..65535.
 *			  To be similar to the Linux joystick code, 32768 is subtracted.
 *          - I'm no good at mickeysoft programming (and prefer to stay that way).
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *			THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#include "joystick.hh"

namespace gfxInput {

const int JOY_THRESHOLD_W32 = 500;	///< affects sensitivity of joystick movement
static bool sJoystickOpen = false;

/*****************************************************************************
 * Open joystick device.
 *****************************************************************************/
bool
Joystick::Open( void )
{
	// For speed, only test the joystick once, and remember the result.
	static int once = 0;
	if ( once++ ) return sJoystickOpen;

	// Messy.
	JOYINFO	joyinfo; 
	UINT	wNumDevs, wDeviceID; 
	BOOL	bDev1Attached, bDev2Attached; 
	if ( (wNumDevs = joyGetNumDevs() ) == 0 ) return (sJoystickOpen=false);
	bDev1Attached = joyGetPos( JOYSTICKID1, &joyinfo ) != JOYERR_UNPLUGGED;
	bDev2Attached = wNumDevs == 2 && joyGetPos( JOYSTICKID2, &joyinfo ) != JOYERR_UNPLUGGED;
	if ( bDev1Attached || bDev2Attached ) // decide which joystick to use
	{
		wDeviceID = bDev1Attached ? JOYSTICKID1 : JOYSTICKID2;
		return (sJoystickOpen=true);
	}
	else 
	{
		return (sJoystickOpen=false);
	}
}
void
Joystick::Close( void )
{
	// NOP
}

/*****************************************************************************
 * Poll the next joystick event (non-blocking).
 * @return True if an event(s) occurred.
 * Updates the .event member struct.
 *****************************************************************************/
bool
Joystick::PollEvent( void )
{
	// Is joystick enabled?
	if ( ! mEnabled || ! sJoystickOpen )
	   return false;

	// Forget previous event.
	ClearEvent();

	// For code commonality, simulate polling a joystick event "the Linux way" on Win32.
	int			i;
	JOYINFO		joyinfo;
	int			x, y, z;
	int			x_delta, y_delta, z_delta;	// abs
	static int	x_prev = 0, y_prev = 0, z_prev = 0;
	int			button_delta;
	static int	button_prev = 0;
    // Naming this a "queue" is a bit of a stretch: it's really two static vars
    // that provide minimal queueing.
#define EVENT_QUEUE_SIZE 2
	static		JoyEvent eventQueue[EVENT_QUEUE_SIZE]; // init'ed by its constructor

	// Any queued event?
	for ( i = 0; i < EVENT_QUEUE_SIZE; ++i )
	{
		if (    eventQueue[i].mAxis   != eJoyAxis_NONE
             || eventQueue[i].mButton != eJoyButton_NONE )
		{
			mEvent = eventQueue[i]; // memberwise copy
			eventQueue[i].Clear();
			return true;
		}
	}

	// Poll the joystick driver.
	if ( joyGetPos( 0, &joyinfo ) != JOYERR_NOERROR )
	   return false;

	// Axis and button events can occur simultaneously.
	// Either or both types of events will be reported
	// in the .event member struct.

	// Button state.
	button_delta = joyinfo.wButtons ^ button_prev;
	button_delta &= ((1<<eJoyButton_LAST)-1); // ignore other bits
	if ( button_delta )
	{
		// Buttons are indicated by bits.
		// Correlate bit to its decimal number.
		int button;
		for ( button = 0; button < eJoyButton_LAST; ++button )
		{
			if ( button_delta & (1<<button) ) break;
		}
ASSERT_OR_RETURN_VAL( button < eJoyButton_LAST, false );
		mEvent.mButton    = (eJoyButton) (button+1);
		mEvent.mButtonVal = joyinfo.wButtons & (1<<button);

		// Since only one button is reported at a time,
		// button_prev must record only the reported button,
		// in order to possibly report changes to other buttons.
		button_prev &= (~(1<<button)); // merge reported bit, keep other bits
		button_prev |= (joyinfo.wButtons & (1<<button));
	}
	else
	{
		button_prev = joyinfo.wButtons;
	}

	// Axis state.
	// Typically, the range of a joystick axis on Win32 is 0..65535.
	// Subtract JOY_HI for a negative/positive range (eg -32767..+32767).
ASSERT( JOY_HI == 32767 ); // joystick_win32.cc depends on JOY_HI == 32767
	x		= joyinfo.wXpos - JOY_HI; // JOY_HI=32767 is assumed!
	y		= joyinfo.wYpos - JOY_HI;
	z		= joyinfo.wZpos - JOY_HI;
	x_delta	= ABS(x - x_prev);
	y_delta = ABS(y - y_prev);
	z_delta = ABS(z - z_prev);
	x_prev	= x;
	y_prev	= y;
	z_prev	= z;
	// If more than one axis was moved, then report one and queue the other(s).
	if ( x_delta > JOY_THRESHOLD_W32 )
	{
		mEvent.mAxis    = eJoyAxis_LEFTRIGHT;
		mEvent.mAxisVal = x * mAxisDir[0]; // if axis reversed
	}
	if ( y_delta > JOY_THRESHOLD_W32 )
	{
		JoyEvent& e	= (mEvent.mAxis != eJoyAxis_NONE) ? mEvent : eventQueue[0];
		e.mAxis		= eJoyAxis_UPDOWN;
		e.mAxisVal	= y * mAxisDir[1]; // if axis reversed
	}
	if ( z_delta > JOY_THRESHOLD_W32 )
	{
		JoyEvent& e	= (mEvent.mAxis != eJoyAxis_NONE && eventQueue[0].mAxis != eJoyAxis_NONE) ? mEvent : eventQueue[1];
		e.mAxis		= eJoyAxis_THIRD;
		e.mAxisVal	= z * mAxisDir[2]; // if axis reversed
	}

	// Return true if any event occured.
	return    mEvent.mAxis   != eJoyAxis_NONE
           || mEvent.mButton != eJoyButton_NONE;
}

} // namespace gfxInput
