/***************************************************************************/
/*  Description: JoyDI.c, part of CPA INO library                          */                                
/*               Joystick specific implementation                          */
/*                                                                         */
/*  Author: F. Jentey                                                      */
/*  Last Update: 05/03/97                                                  */
/***************************************************************************/
 
 
#include "INO\errINO.h"
#include "InitDI.h"
#include "INOInit.h"
#include "Joystick.h"
 
 
 
/**********************************************************************************/
/*  Fill the p_stJoyCaps with the capabilities of the specified joystick          */
/*  Return an error if unable to get these information.                           */
/*  It usually happens when the joystick isn't configurated in the control panel, */
/*  but could also appear with some joystick if the joystick isn't connected.     */
/**********************************************************************************/
short INO_fn_wInitJoystickCaps(INO_tdhDevice hDev)
{
    HRESULT  DInputErr;
    DIPROPRANGE stAxisRange;
    DWORD a_ulAxis[INO_C_wNbMaxAxis] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ };
    short    i, wDev;
    INO_tdstJoystickCaps *p_stJoyCaps = (INO_tdstJoystickCaps*)(hDev->m_p_stCaps);
    unsigned long ulSubType;
 
    for (i=0; i<INO_C_wNbMaxDevice; i++)
        if (INO_g_a_stDIDevice[i].m_hDevice == hDev) break;
    if (i == INO_C_wNbMaxDevice) return (0);
    wDev = i;
 
    ulSubType = INO_fn_wFillDeviceCaps(hDev);
    if (!ulSubType) return(0);
 
    /* Joystick sub-type */
    switch (ulSubType)
    {
    case DIDEVTYPEJOYSTICK_TRADITIONAL:
    case DIDEVTYPEJOYSTICK_GAMEPAD:
        hDev->m_uwType |= INO_C_uwSubTypeJoypad;
        break;
 
    case DIDEVTYPEJOYSTICK_FLIGHTSTICK:
    case DIDEVTYPEJOYSTICK_RUDDER:
        hDev->m_uwType |= INO_C_uwSubTypeJoystick;
        break;
 
    case DIDEVTYPEJOYSTICK_WHEEL:
        hDev->m_uwType |= INO_C_uwSubTypeSteeringWheel;
        break;
    
    case DIDEVTYPEJOYSTICK_HEADTRACKER:
    default:
        hDev->m_uwType |= INO_C_uwUnknown;
    }
 
    /* Get Axis capabilities */
    for (i=0; i<INO_C_wNbMaxAxis; i++)
    {
        stAxisRange.diph.dwSize       = sizeof(DIPROPRANGE);
        stAxisRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
        stAxisRange.diph.dwHow        = DIPH_BYOFFSET;
        stAxisRange.diph.dwObj        = a_ulAxis[i];
        DInputErr = M_DIGetProp(wDev,DIPROP_RANGE,&stAxisRange.diph);
        switch (DInputErr)
        {
        case DI_OK:
            p_stJoyCaps->m_a_stAxisCaps[i].m_lCenter    = ((stAxisRange.lMax + stAxisRange.lMin) >> 1);
            p_stJoyCaps->m_a_stAxisCaps[i].m_lRange     = ((stAxisRange.lMax - stAxisRange.lMin) >> 1);
            p_stJoyCaps->m_a_stAxisCaps[i].m_lThreshold = p_stJoyCaps->m_a_stAxisCaps[i].m_lRange >> 1;
            p_stJoyCaps->m_ulCapsFlags |= 1 << i;
            break;
 
        case DIERR_OBJECTNOTFOUND:
            break;
 
        case DIERR_UNSUPPORTED:
        case DIERR_INVALIDPARAM:
        case DIERR_NOTINITIALIZED:
        default:
            INO_fn_vUpdateLastError(E_uwINO_DirectInputError);
            return (0);
        }
    }
 
    return (1);
}
 
 
/**********************************************************************************/
/*                 GET JOYSTICK STATE USING DURECT INPUT 5                        */
/*  Fill the p_stJoyState with the state of the specified joystick                */
/*  Return an error if unable to get these information.                           */
/*  It usually happens when the joystick isn't configurated in the control panel, */
/*  but could also appear with some joystick if the joystick isn't connected.     */
/**********************************************************************************/
short INO_fn_wReadJoystick(INO_tdhDevice hDev)
{
    HRESULT     DInputErr;
    DIJOYSTATE  stJoyState;
    short       wDev, i;
    INO_tdstJoystickState *p_stJoyState = (INO_tdstJoystickState*)(hDev->m_p_stState);
    INO_tdstAxisCaps *p_stAxes = (INO_tdstAxisCaps*)&(((INO_tdstJoystickCaps*)(hDev->m_p_stCaps))->m_a_stAxisCaps);
    INO_tdstJoystickRecord *p_stH = (INO_tdstJoystickRecord*)hDev->m_pvHistoric;
 
    if (!INO_M_bIsConnected(hDev)) 
        return (0);
 
    for (i=0; i<INO_C_wNbMaxDevice; i++)
        if (INO_g_a_stDIDevice[i].m_hDevice == hDev) break;
    if (i == INO_C_wNbMaxDevice)
    {
        INO_fn_vUpdateLastError(E_uwINO_WarningInvalidDevice);
        return (0);
    }
    wDev = i;
 
    /* First, reacquired the joystick if necessary */
    if (!(p_stJoyState->m_ucStatus & INO_C_ucAcquired) ||
         (p_stJoyState->m_ucStatus & INO_C_ucAccessError) )
    {
        DInputErr = M_DIAcquire(wDev);
        switch (DInputErr)
        {
        case DI_OK:
		case S_FALSE:
            p_stJoyState->m_ucStatus |= INO_C_ucAcquired;
            p_stJoyState->m_ucStatus &= ~INO_C_ucAccessError;
            break;
 
        case DIERR_INPUTLOST:
        case DIERR_NOTACQUIRED:
        case DIERR_OTHERAPPHASPRIO:
            return (0);
 
        default:
            INO_fn_vUpdateLastError(E_uwINO_DirectInputError);
            p_stJoyState->m_ucStatus &= ~INO_C_ucAcquired;
            p_stJoyState->m_ucStatus |= INO_C_ucAccessError;
            return (0);
        }
    }
 
    /* If the direct input extended interface is available, call Poll method */
    /* before getting joystick state                                         */
    if (M_DIDevEx(wDev))
    {
        DInputErr = M_DIDev2(wDev)->lpVtbl->Poll(M_DIDev2(wDev));
        switch (DInputErr)
        {
        case DI_OK:
		case S_FALSE:
            break;
 
        case DIERR_INPUTLOST:
        case DIERR_NOTACQUIRED:
            p_stJoyState->m_ucStatus &= ~INO_C_ucAcquired;
            return (0);
 
        default:
            INO_fn_vUpdateLastError(E_uwINO_DirectInputError);
            p_stJoyState->m_ucStatus &= ~INO_C_ucAcquired;
            p_stJoyState->m_ucStatus |= INO_C_ucAccessError;
            return (0);
        }
    }
 
    /* Get joystick state */
    DInputErr = M_DIGetState(wDev,sizeof(DIJOYSTATE),&stJoyState);
	switch (DInputErr)
	{
	case DI_OK:
    case E_PENDING:
        break;
		
    case DIERR_INPUTLOST:
    case DIERR_NOTACQUIRED:
        p_stJoyState->m_ucStatus &= ~INO_C_ucAcquired;
        return (0);
 
    default:
        p_stJoyState->m_ucStatus |= INO_C_ucAccessError;
        p_stJoyState->m_ucStatus &= ~INO_C_ucAcquired;
        INO_fn_vUpdateLastError(E_uwINO_DirectInputError);
        return (0);
	}
 
    /* Read axes informations */
    p_stJoyState->m_a_lAxisValue[0] = stJoyState.lX - p_stAxes[0].m_lCenter;
    p_stJoyState->m_a_lAxisValue[1] = stJoyState.lY - p_stAxes[1].m_lCenter;
    p_stJoyState->m_a_lAxisValue[2] = stJoyState.lZ - p_stAxes[2].m_lCenter;
    p_stJoyState->m_a_lAxisValue[3] = stJoyState.lRx - p_stAxes[3].m_lCenter;
    p_stJoyState->m_a_lAxisValue[4] = stJoyState.lRy - p_stAxes[4].m_lCenter;
    p_stJoyState->m_a_lAxisValue[5] = stJoyState.lRz - p_stAxes[5].m_lCenter;
 
    p_stJoyState->m_uwDirections = 0;    
    /* Update X direction */
    if (p_stJoyState->m_a_lAxisValue[0] < -p_stAxes[0].m_lThreshold)
        p_stJoyState->m_uwDirections |= INO_C_uwLeft;
    else
        if (p_stJoyState->m_a_lAxisValue[0] > p_stAxes[0].m_lThreshold)
            p_stJoyState->m_uwDirections |= INO_C_uwRight;
    
    /* Update Y direction */
    if (p_stJoyState->m_a_lAxisValue[1] > p_stAxes[1].m_lThreshold)
        p_stJoyState->m_uwDirections |= INO_C_uwDown;
    else
        if (p_stJoyState->m_a_lAxisValue[1] < -p_stAxes[1].m_lThreshold)
            p_stJoyState->m_uwDirections |= INO_C_uwUp;
    
    /* Update Z direction */
    if (p_stJoyState->m_a_lAxisValue[2] < -p_stAxes[2].m_lThreshold)
        p_stJoyState->m_uwDirections |= INO_C_uwZDown;
    else
        if (p_stJoyState->m_a_lAxisValue[2] > p_stAxes[2].m_lThreshold)
            p_stJoyState->m_uwDirections |= INO_C_uwZUp;
 
    /* Copy buttons info */
    p_stJoyState->m_ulButtons = 0;
    for (i=0; i<hDev->m_p_stCaps->m_ucNbButtons; i++)
        if (stJoyState.rgbButtons[i] & INO_C_ucButtonDown)
            p_stJoyState->m_ulButtons |= 1 << i;
 
    /* Update historic.   */
    if (hDev->m_wRecordNumber == 0)
    {
        p_stH[0].m_ulDate = hDev->m_ulLastTimeCount;
        p_stH[0].m_ulButtons = p_stJoyState->m_ulButtons;
        p_stH[0].m_uwDirections = p_stJoyState->m_uwDirections;
        hDev->m_wRecordNumber = 1;
    }
    else
    {
        hDev->m_wHistoricHead++;
        if (hDev->m_wHistoricHead == hDev->m_wHistoricSize) hDev->m_wHistoricHead = 0;
        if (hDev->m_wRecordNumber != hDev->m_wHistoricSize) hDev->m_wRecordNumber++;
        p_stH[hDev->m_wHistoricHead].m_ulDate = hDev->m_ulLastTimeCount;
        p_stH[hDev->m_wHistoricHead].m_ulButtons = p_stJoyState->m_ulButtons;
        p_stH[hDev->m_wHistoricHead].m_uwDirections = p_stJoyState->m_uwDirections;
    }
 
    return (1);
}
