/*****************************************************************************
 * $LastChangedDate: 2011-04-09 21:58:06 -0400 (Sat, 09 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Base low-level JoystickDevice class (system-neutral).
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define INPUT_DEVICE_JOYSTICK_CC 1
#include "base/module.hh"
#include "base/busy.hh"
using namespace base;
#include "glue/module.hh"
#include "glue/conf.hh"
using namespace glue;
#include "input/module.hh"
#include "input/conf.hh"
#include "input/device_joystick.hh"

namespace input {

// Internal definitions:
namespace defs { const uint JOYSTICK_EVENT_ENQUEUE_LIMIT = 20; }  // limit the enqueueing of events per tick

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////  JoystickDevice  /////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

DEFINE_SINGLETON_CLASS_VARS( JoystickDevice )  // will be a derivative

/*****************************************************************************
 * ctor/dtor.
 * JoystickDevice is initially disabled to give chance for program
 * to become ready to respond to joystick events.
 *****************************************************************************/
JoystickDevice::JoystickDevice( void )
:   mTickFunctor(new TickFunctor)
{
//ASSERT_RUN_ONCE;  // in case joystick drivers are switched at run-time (historical)
ASSERT( MAX_AXIS_VALUE == 0x7fff );  // don't change

    // Register timer-tick to poll and enqueue joystick events.
    GET_TIMER().Register( mTickFunctor, INPUT_CONF.mInputQueueTickFreq );
}

JoystickDevice::~JoystickDevice()
{
    // Unregister.
    GET_TIMER().Unregister( mTickFunctor );
}

/*****************************************************************************
 * This is a special kind of singleton that instantiates a system-specific derivative of itself.
 *****************************************************************************/
JoystickDevice&
JoystickDevice::GetInstance( void )
{
    if ( UX( msInstance == NULL ) )
    {
        // Construct a system-specific derivative of this class.

        // Prefer SDL.
        // SDL can be disabled by passing -no-sdl
        // or overridden by -jlinux/-jwindows.
    #if COMPILE_SDL
        if ( GLUE_CONF.mEnableSDL
         and (not INPUT_CONF.mEnableLinuxJoystick)
         and (not INPUT_CONF.mEnableWindowsJoystick) )
        {
            msInstance = new JoystickDeviceSDL;
            return *msInstance;  // use SDL, skip the following
        }
    #endif

    // Mutually-exclusive.
    #if COMPILE_LINUX_JOYSTICK  // FreeBSD supports Linux joystick driver
        msInstance = new JoystickDeviceLinux;
    #elif OS_WINDOWS
        msInstance = new JoystickDeviceWindows;
    #else
        msInstance = new JoystickDeviceNop;  // default
    #endif

    }

    ASSERT( msInstance != NULL );
    return *msInstance;
}

/*****************************************************************************
 * For joysticks, a timer-tick is used to poll events from the joystick device
 * in order to enqueue events.
 *****************************************************************************/
void
JoystickDevice::Tick( const Milliseconds milliseconds )
{
BUSY_BEGIN {

    // Template Method: PollJoystickDevice()
    // This is the system-neutral JoystickDevice class.
    // Poll joystick device using the device-specific derived method PollJoystickDevice() = 0.
    // Enqueue joystick events.

    if ( IfEnabled() )
    {
        for ( uint i = 0; i < defs::JOYSTICK_EVENT_ENQUEUE_LIMIT; ++i )
        {
            JoystickEvent event;
            if ( PollJoystickDevice( event ) )  // virtual PollJoystickDevice() = 0
            {
                ASSERT( event.IfValid() );
                Enqueue( event );
            }
            else
            {
                // No more events.
                break;
            }
        }
    }

} BUSY_END
}

void
JoystickDevice::TickFunctor::Tick( const Milliseconds milliseconds )
{
    GET_JOYSTICK_DEVICE().Tick( milliseconds );  // reflect
}

} // namespace input
