//========================================================================
// GLFW - An OpenGL framework
// File:        amigaos_joystick.c
// Platforms:   AmigaOS, MorphOS
// API version: 2.6
// WWW:         http://glfw.sourceforge.net
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Camilla Berglund
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would
//    be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
//    be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
//    distribution.
//
//========================================================================

#include "internal.h"


//************************************************************************
//****                  GLFW internal functions                       ****
//************************************************************************

//========================================================================
// _glfwSendJoyReadRequest() - Send joystick read request
//========================================================================

static void _glfwSendJoyReadRequest( void )
{
    // Send read request
    _glfwJoy.GameIO->io_Command = GPD_READEVENT;
    _glfwJoy.GameIO->io_Flags   = 0;
    _glfwJoy.GameIO->io_Data    = (APTR) &_glfwJoy.GameEvent;
    _glfwJoy.GameIO->io_Length  = sizeof(struct InputEvent);
    SendIO( (struct IORequest *) _glfwJoy.GameIO );
}


//========================================================================
// _glfwInitJoysticks() - Initialize joystick interface
//========================================================================

void _glfwInitJoysticks( void )
{
    unsigned char controller_type = 0;
    struct GamePortTrigger game_trigger;

    // Start by clearing all handles
    _glfwJoy.GameMP         = NULL;
    _glfwJoy.GameIO         = NULL;
    _glfwJoy.Present        = 0;
    _glfwJoy.GameDeviceOpen = 0;

    // Create gameport.device message port
    if( !(_glfwJoy.GameMP = CreatePort( NULL, 0 )) )
    {
        return;
    }

    // Create gameport.device I/O request
    if( !( _glfwJoy.GameIO = (struct IOStdReq *)
           CreateExtIO(_glfwJoy.GameMP, sizeof(struct IOStdReq)) ) )
    {
        _glfwTerminateJoysticks();
        return;
    }

    // Open gameport.device (unit 1 = port 2)
    _glfwJoy.GameIO->io_Message.mn_Node.ln_Type = NT_UNKNOWN;
    if( OpenDevice( "gameport.device", 1,
                    (struct IORequest *)_glfwJoy.GameIO, 0 ) )
    {
        _glfwTerminateJoysticks();
        return;
    }
    _glfwJoy.GameDeviceOpen = 1;

    // Start critical section
    Forbid();

    // Find out if anyone else is using the stick
    _glfwJoy.GameIO->io_Command = GPD_ASKCTYPE;
    _glfwJoy.GameIO->io_Flags   = IOF_QUICK;
    _glfwJoy.GameIO->io_Data    = (APTR) &controller_type;
    _glfwJoy.GameIO->io_Length  = 1;
    DoIO( (struct IORequest *) _glfwJoy.GameIO );

    // Was it already allocated?
    if( controller_type != GPCT_NOCONTROLLER )
    {
        Permit();
        _glfwTerminateJoysticks();
        return;
    }

    // Allocate joystick
    controller_type = GPCT_ABSJOYSTICK;
    _glfwJoy.GameIO->io_Command = GPD_SETCTYPE;
    _glfwJoy.GameIO->io_Flags   = IOF_QUICK;
    _glfwJoy.GameIO->io_Data    = (APTR) &controller_type;
    _glfwJoy.GameIO->io_Length  = 1;
    DoIO( (struct IORequest *) _glfwJoy.GameIO );
    _glfwJoy.Present = 1;

    // End critical section
    Permit();

    // Set trigger conditions
    game_trigger.gpt_Keys       = GPTF_UPKEYS | GPTF_DOWNKEYS;
    game_trigger.gpt_XDelta     = 1;
    game_trigger.gpt_YDelta     = 1;
    game_trigger.gpt_Timeout    = (UWORD) 0xFFFF; // ~20 minutes
    _glfwJoy.GameIO->io_Command = GPD_SETTRIGGER;
    _glfwJoy.GameIO->io_Flags   = IOF_QUICK;
    _glfwJoy.GameIO->io_Data    = (APTR) &game_trigger;
    _glfwJoy.GameIO->io_Length  = (LONG) sizeof(struct GamePortTrigger);
    DoIO( (struct IORequest *) _glfwJoy.GameIO );

    // Flush buffer
    _glfwJoy.GameIO->io_Command = CMD_CLEAR;
    _glfwJoy.GameIO->io_Flags   = IOF_QUICK;
    _glfwJoy.GameIO->io_Data    = NULL;
    _glfwJoy.GameIO->io_Length  = 0;
    DoIO( (struct IORequest *) _glfwJoy.GameIO );

    // Send joystick read request (asynchronous)
    _glfwSendJoyReadRequest();
}


//========================================================================
// _glfwTerminateJoysticks() - Close all opened joystick handles
//========================================================================

void _glfwTerminateJoysticks( void )
{
    unsigned char controller_type;

    // Remove any remaining asynchronous messages
    if( _glfwJoy.GameIO )
    {
        if( !CheckIO( (struct IORequest *)_glfwJoy.GameIO ) )
        {
            AbortIO( (struct IORequest *)_glfwJoy.GameIO );
            WaitIO( (struct IORequest *)_glfwJoy.GameIO );
        }
    }

    // Deallocate joystick
    if( _glfwJoy.Present )
    {
        controller_type = GPCT_NOCONTROLLER;
        _glfwJoy.GameIO->io_Command = GPD_SETCTYPE;
        _glfwJoy.GameIO->io_Flags   = IOF_QUICK;
        _glfwJoy.GameIO->io_Data    = (APTR) &controller_type;
        _glfwJoy.GameIO->io_Length  = 1;
        DoIO( (struct IORequest *) _glfwJoy.GameIO );
        _glfwJoy.Present = 0;
    }

    // Close gameport.device
    if( _glfwJoy.GameDeviceOpen )
    {
        CloseDevice( (struct IORequest *) _glfwJoy.GameIO );
        _glfwJoy.GameDeviceOpen = 0;
    }

    // Delete I/O request
    if( _glfwJoy.GameIO )
    {
        DeleteExtIO( (struct IORequest *) _glfwJoy.GameIO );
        _glfwJoy.GameIO = NULL;
    }

    // Delete message port
    if( _glfwJoy.GameMP )
    {
        DeletePort( _glfwJoy.GameMP );
        _glfwJoy.GameMP = NULL;
    }
}


//========================================================================
// _glfwPollJoystickEvents() - Empty joystick event queue
//========================================================================

static void _glfwPollJoystickEvents( void )
{
    int got_event = 0;

    // Do we have a stick?
    if( !_glfwJoy.Present )
    {
        return;
    }

    // Empty the message queue
    while( GetMsg( _glfwJoy.GameMP ) != NULL )
    {
        // Flag: we got an event
        got_event = 1;

        switch( _glfwJoy.GameEvent.ie_Code )
        {
            // Left button pressed
            case IECODE_LBUTTON:
                _glfwJoy.Button[ 0 ] = 1;
                break;

            // Left button released
            case (IECODE_LBUTTON | IECODE_UP_PREFIX):
                _glfwJoy.Button[ 0 ] = 0;
                break;

            // Right button pressed
            case IECODE_RBUTTON:
                _glfwJoy.Button[ 1 ] = 1;
                break;

            // Right button released
            case (IECODE_RBUTTON | IECODE_UP_PREFIX):
                _glfwJoy.Button[ 1 ] = 0;
                break;

            // Axis event
            case IECODE_NOBUTTON:
                _glfwJoy.Axis[ 0 ] = (float) _glfwJoy.GameEvent.ie_X;
                _glfwJoy.Axis[ 1 ] = (float) -_glfwJoy.GameEvent.ie_Y;
                break;

            default:
                break;
        }
    }

    // Did we get any events?
    if( got_event )
    {
        // Send joystick read request (asynchronous)
        _glfwSendJoyReadRequest();
    }
}



//************************************************************************
//****               Platform implementation functions                ****
//************************************************************************

//========================================================================
// _glfwPlatformGetJoystickParam() - Determine joystick capabilities
//========================================================================

int _glfwPlatformGetJoystickParam( int joy, int param )
{
    // Is joystick present?
    if( joy != GLFW_JOYSTICK_1 || !_glfwJoy.Present )
    {
        return 0;
    }

    // We assume that the joystick is connected, and has two axes and two
    // buttons (since there is no way of retrieving this information from
    // AmigaOS)
    switch( param )
    {
    case GLFW_PRESENT:
        return GL_TRUE;

    case GLFW_AXES:
        return 2;

    case GLFW_BUTTONS:
        return 2;

    default:
        break;
    }

    return 0;
}


//========================================================================
// _glfwPlatformGetJoystickPos() - Get joystick axis positions
//========================================================================

int _glfwPlatformGetJoystickPos( int joy, float *pos, int numaxes )
{
    int k;

    // Is joystick present?
    if( joy != GLFW_JOYSTICK_1 || !_glfwJoy.Present )
    {
        return 0;
    }

    // Update joystick state
    _glfwPollJoystickEvents();

    // Copy axis position information to output vector
    if( numaxes > 2 )
    {
        numaxes = 2;
    }
    for( k = 0; k < numaxes; ++ k )
    {
        pos[ k ] = _glfwJoy.Axis[ k ];
    }

    return numaxes;
}


//========================================================================
// _glfwPlatformGetJoystickButtons() - Get joystick button states
//========================================================================

int _glfwPlatformGetJoystickButtons( int joy, unsigned char *buttons,
    int numbuttons )
{
    int k;

    // Is joystick present?
    if( joy != GLFW_JOYSTICK_1 || !_glfwJoy.Present )
    {
        return 0;
    }

    // Update joystick state
    _glfwPollJoystickEvents();

    // Copy button information to output vector
    if( numbuttons > 2 )
    {
        numbuttons = 2;
    }
    for( k = 0; k < numbuttons; ++ k )
    {
        buttons[ k ] = _glfwJoy.Button[ k ];
    }

    return numbuttons;
}