/***************************************************************************/
/*  Description: InitDI.c, part of CPA INO library                         */                                
/*               Direct Input device low level init implementation         */
/*                                                                         */
/*  Author: F. Jentey                                                      */
/*  Creation date: 21/04/97                                                */
/*  Last Update:   30/07/97  Direct input initialisation                   */
/***************************************************************************/
 
 
/*========  Direct Input header  ========*/
#ifndef INITGUID
#define INITGUID
#include "DInput.h"
#undef  INITGUID
#else
#include "DInput.h"
#endif
 
/*=======================================*/
 
#include "INO\errINO.h"
#include "INOInit.h"
#include "InitDI.h"
#include "INOJoy.h"
#include "INOKeyb.h"
#include "INOMouse.h"
#include "Joystick.h"
 
 
 
#define M_DInput() INO_g_p_stDInputInterface->lpVtbl
 
 
LPDIRECTINPUT  INO_g_p_stDInputInterface = NULL;
HINSTANCE      INO_g_hAppInstance;
HWND           INO_g_hAppMainWindow;
unsigned long  m_ulDITargetVersion = DIRECTINPUT_VERSION;  /* Version of Direct Input the module was build for*/
unsigned long  m_ulDIInstalledVersion;                     /* Version of Direct Input installed on the machine*/
DIDEVCAPS      g_stDIDevCaps;
DIDEVCAPS_DX3  g_stDIDevCaps3;
 
 
 
 
 
INO_tdstDIDevice  INO_g_a_stDIDevice[INO_C_wNbMaxDevice];
short             INO_g_wNbDevices = 0;
 
 
 
 
/*********************************************************************************/
/*              Joystick enumeration callback                                    */
/*     Device instance are stored in INO_g_a_stJoystickInstance                  */
/*********************************************************************************/
BOOL CALLBACK fn_bEnumDeviceCallback(LPCDIDEVICEINSTANCE p_stDeviceInstance, LPVOID p_vRef)
{
    LPDIRECTINPUTDEVICE  p_stDevice;
    LPDIRECTINPUTDEVICE2 p_stDevice2;
    LPCDIDATAFORMAT      p_stDataFormat;
    DWORD                ulCooperativeLevel;
    HRESULT  DInputErr;
 
    /* Create the DirectInput joystick device */
    DInputErr = M_DInput()->CreateDevice(
                                         INO_g_p_stDInputInterface,
                                         &p_stDeviceInstance->guidInstance,
                                         &p_stDevice,
                                         NULL
                                        );
    if (DInputErr != DI_OK) return FALSE;
 
    switch (GET_DIDEVICE_TYPE(p_stDeviceInstance->dwDevType))
    {
    case DIDEVTYPE_MOUSE:
        p_stDataFormat = &c_dfDIMouse;
        /* All these settings have been tried in NT 4 with Service Pack 3 installed*/
        /* This is the option selected...*/
        ulCooperativeLevel = DISCL_NONEXCLUSIVE | DISCL_FOREGROUND;
        /* This one freezes the mouse...*/
        /*ulCooperativeLevel = DISCL_EXCLUSIVE | DISCL_FOREGROUND;*/
        /* Works similarly to DISCL_NONEXCLUSIVE | DISCL_FOREGROUND*/
        /*ulCooperativeLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;*/
        /* This one causes unhandled exceptions...*/
        /*ulCooperativeLevel = DISCL_EXCLUSIVE | DISCL_BACKGROUND;*/
        break;
    case DIDEVTYPE_KEYBOARD:
        p_stDataFormat = &c_dfDIKeyboard;
        ulCooperativeLevel = DISCL_NONEXCLUSIVE | DISCL_FOREGROUND;
        /*ulCooperativeLevel = DISCL_EXCLUSIVE | DISCL_FOREGROUND;*/
        break;
    case DIDEVTYPE_JOYSTICK:
        p_stDataFormat = &c_dfDIJoystick;
        ulCooperativeLevel = DISCL_NONEXCLUSIVE | DISCL_FOREGROUND;
        break;
    default:
        return FALSE;
    }
 
    /* Make sure the device is not acquired before calling setDataFormat */
    /* and SetCooperativeLevel.                                          */
    DInputErr = p_stDevice->lpVtbl->Unacquire(p_stDevice);
    /* Set joystick data format */
    DInputErr = p_stDevice->lpVtbl->SetDataFormat(p_stDevice, p_stDataFormat);
    if (DInputErr != DI_OK)
    {
        p_stDevice->lpVtbl->Release(p_stDevice);
        return FALSE;
    }
 
    /* Set the cooperative level */
    DInputErr = p_stDevice->lpVtbl->SetCooperativeLevel(
                                                        p_stDevice,
                                                        INO_g_hAppMainWindow,
                                                        ulCooperativeLevel
                                                       );
    if (DInputErr != DI_OK)
    {
        p_stDevice->lpVtbl->Release(p_stDevice);
        return FALSE;
    }
 
    /* Everything is OK, keep this joystick */ 
    memcpy(
           &INO_g_a_stDIDevice[INO_g_wNbDevices].m_stDevInstance,
           p_stDeviceInstance,
           sizeof(DIDEVICEINSTANCE)
          );
    INO_g_a_stDIDevice[INO_g_wNbDevices].m_p_stDevInterface = p_stDevice;
 
    /* Find the IDirectInput2 interface, for using Poll method */
    if (GET_DIDEVICE_TYPE(p_stDeviceInstance->dwDevType) == DIDEVTYPE_JOYSTICK)
    {
        DInputErr = p_stDevice->lpVtbl->QueryInterface(
                                                       p_stDevice,
                                                       &IID_IDirectInputDevice2,
                                                       (LPVOID*)&p_stDevice2
                                                      );
        if (DInputErr == S_OK)
        {
            INO_g_a_stDIDevice[INO_g_wNbDevices].m_p_stDevInterface2 = p_stDevice2;
            INO_g_a_stDIDevice[INO_g_wNbDevices].m_wExtendedInterfaceAvailable = 1;
        }
        else
        {
            INO_g_a_stDIDevice[INO_g_wNbDevices].m_p_stDevInterface2 = NULL;
            INO_g_a_stDIDevice[INO_g_wNbDevices].m_wExtendedInterfaceAvailable = 0;
        }
    }
    else
    {
        INO_g_a_stDIDevice[INO_g_wNbDevices].m_p_stDevInterface2 = NULL;
        INO_g_a_stDIDevice[INO_g_wNbDevices].m_wExtendedInterfaceAvailable = 0;
    }
 
    INO_g_wNbDevices++;
    return DIENUM_CONTINUE;
}
 
 
 
/***********************************/
/*       Init Direct Input         */
/*  Return 1 if OK, else 0         */
/***********************************/
short INO_fn_wInitAllDevices(HINSTANCE hInstance, HWND hWindow)
{
    HRESULT  DInputErr;
 
    if (INO_g_p_stDInputInterface) 
        return(1);
 
    INO_g_wNbDevices = 0;
    DInputErr = DirectInputCreate(hInstance,DIRECTINPUT_VERSION,&INO_g_p_stDInputInterface,NULL);
    if (DInputErr==DIERR_OLDDIRECTINPUTVERSION)
    {
        /* If compiled with Direct Input 5, and Direct Input 3 is installed */
        DInputErr = DirectInputCreate(hInstance,DIRECTINPUT_VERSION_3,&INO_g_p_stDInputInterface,NULL);
        if (DInputErr == DI_OK)
            m_ulDIInstalledVersion = DIRECTINPUT_VERSION_3;
    }
    else
        m_ulDIInstalledVersion = DIRECTINPUT_VERSION;
 
    switch (DInputErr)
    {
    case DI_OK:
        INO_g_hAppInstance = hInstance;
        INO_g_hAppMainWindow = hWindow;
        break;
 
    case DIERR_INVALIDPARAM:
    case DIERR_OUTOFMEMORY:
    case DIERR_OLDDIRECTINPUTVERSION:
    case DIERR_BETADIRECTINPUTVERSION:
	default:
        INO_g_p_stDInputInterface = NULL;
        m_ulDIInstalledVersion = 0;
        INO_fn_vUpdateLastError(E_uwINO_FatalDirectInpuNotFound);
        return (0);
    }
 
    memset(INO_g_a_stDIDevice,0,sizeof(INO_tdstDIDevice)*INO_C_wNbMaxDevice);
    
    DInputErr = M_DInput()->EnumDevices(
                                        INO_g_p_stDInputInterface,
                                        0,
                                        fn_bEnumDeviceCallback,
                                        NULL,
                                        DIEDFL_ALLDEVICES
                                       );
    if( DInputErr == DI_OK )
    {
        return ( 1 );
    }
 
    return( 0 );
}
 
 
/***********************************/
/*     Init one device             */
/***********************************/
short INO_fn_wInitDevice(INO_tdhDevice hDev)
{
    HRESULT DInputErr;
    short i, wResult;
 
    for (i=0; i<INO_C_wNbMaxDevice; i++)
    {
        if (!INO_g_a_stDIDevice[i].m_hDevice &&
             (1 << GET_DIDEVICE_TYPE(INO_g_a_stDIDevice[i].m_stDevInstance.dwDevType) == INO_M_uwType(hDev))
           )
        {
            INO_g_a_stDIDevice[i].m_hDevice = hDev;
            switch (INO_M_uwType(hDev))
            {
            case INO_C_uwJoystick:
                hDev->m_pfnRead = INO_fn_wReadJoystick;
                wResult = INO_fn_wInitJoystickCaps(hDev);
                break;
            case INO_C_uwKeyboard:
                hDev->m_pfnRead = INO_fn_wReadKeyboard;
                wResult = INO_fn_wInitKeyboardCaps(hDev);
                break;
            case INO_C_uwMouse:
                /* Initialize the mouse only if the application uses it*/
                hDev->m_pfnRead = INO_fn_wReadMouse;
                wResult = INO_fn_wInitMouseCaps(hDev);
                break;
            }
            if (!wResult)
            {
                INO_fn_vUpdateLastError(E_uwINO_DeviceInitFailed);
                return (-1);
            }
 
            DInputErr = M_DIAcquire(i);
            switch (DInputErr)
            {
                case DI_OK:
                case S_FALSE:
                    hDev->m_p_stState->m_ucStatus |= INO_C_ucAcquired;
                    break;
                    
                case DIERR_INPUTLOST:
                    hDev->m_p_stState->m_ucStatus &= ~INO_C_ucAcquired;
                    break;
                    
                case DIERR_INVALIDPARAM:
                    hDev->m_p_stState->m_ucStatus &= ~INO_C_ucAcquired;
                    INO_fn_vUpdateLastError(E_uwINO_DirectInputError);
                    return (-1);
                    
                default:
                    break;
            }
            return (i);
        }
    }
    return (-1);
}
 
 
/***********************************/
/*     Release one device          */
/***********************************/
short INO_fn_wDestroyDevice(INO_tdhDevice hDev)
{
    long i;
 
    for (i=0; i<INO_C_wNbMaxDevice; i++)
    {
        if (INO_g_a_stDIDevice[i].m_hDevice == hDev)
        {
            M_DIUnacquire(i);
/*            M_DIRelease(i);*/
            memset(&INO_g_a_stDIDevice[i],0,sizeof(INO_tdstDIDevice));
            return (1);
        }
    }
    return (0);
}
 
 
/***********************************/
/*     Release Direct Input        */
/***********************************/
void INO_fn_vReleaseAllDevices()
{
    long i;
 
    if (!INO_g_p_stDInputInterface) return;
 
    for (i=0; i<INO_C_wNbMaxDevice; i++)
        if (INO_g_a_stDIDevice[i].m_hDevice)
        {
            M_DIRelease(i);
            INO_fn_wDestroyDevice(INO_g_a_stDIDevice[i].m_hDevice);
        }
 
    M_DInput()->Release(INO_g_p_stDInputInterface);
    INO_g_p_stDInputInterface = NULL;
}
 
 
/****************************************/
/*  Free All                            */
/****************************************/
short INO_fn_wGetNbDevices(unsigned short uwType)
{
    short i, lNb = 0;
 
    if (!INO_g_p_stDInputInterface) return (0);
 
    for (i=0; i<INO_C_wNbMaxDevice; i++)
     if ((1 << GET_DIDEVICE_TYPE(INO_g_a_stDIDevice[i].m_stDevInstance.dwDevType)) == uwType) lNb++;
        /*if (INO_g_a_stDIDevice[i].m_hDevice)*/
/*            if (GET_DIDEVICE_TYPE(INO_g_a_stDIDevice[i].m_stDevInstance.dwDevType) == uwType) lNb++;*/
    return (lNb);
}
 
 
/******************************************/
/*    init the device capabilities        */
/* and return the DInput sub-type         */
/******************************************/
short INO_fn_wFillDeviceCaps(INO_tdhDevice hDev)
{
    HRESULT  DInputErr;
    short    wDev;
    INO_tdstDevCaps *p_stCaps = (INO_tdstDevCaps*)(hDev->m_p_stCaps);
 
    for (wDev=0; wDev<INO_C_wNbMaxDevice; wDev++)
        if (INO_g_a_stDIDevice[wDev].m_hDevice == hDev) break;
    if (wDev == INO_C_wNbMaxDevice) return (0);
 
    M_DIAcquire(wDev);
 
    if (m_ulDIInstalledVersion == DIRECTINPUT_VERSION_3)
    {
        memset(&g_stDIDevCaps3,0,sizeof(DIDEVCAPS_DX3));
        g_stDIDevCaps3.dwSize = sizeof(DIDEVCAPS_DX3);
        DInputErr = M_DIGetCaps(wDev,(DIDEVCAPS*)&g_stDIDevCaps3);
    }
    else
    {
        memset(&g_stDIDevCaps,0,sizeof(DIDEVCAPS));
        g_stDIDevCaps.dwSize = sizeof(DIDEVCAPS);
        DInputErr = M_DIGetCaps(wDev,&g_stDIDevCaps);
    }
    if (DInputErr == DI_OK)
    {
        if (m_ulDIInstalledVersion == DIRECTINPUT_VERSION_3)
        {
            if (g_stDIDevCaps3.dwFlags & DIDC_ATTACHED)
                hDev->m_p_stState->m_ucStatus |= INO_C_ucConnected;
 
            p_stCaps->m_ucNbButtons = (unsigned char)g_stDIDevCaps3.dwButtons;
            switch (INO_M_uwType(hDev))
            {
            case INO_C_uwJoystick:
                ((INO_tdstJoystickCaps*)p_stCaps)->m_ucNbPOVs = (unsigned char)g_stDIDevCaps3.dwPOVs;
            case INO_C_uwMouse:
                ((INO_tdstJoystickCaps*)p_stCaps)->m_ucNbAxes = (unsigned char)g_stDIDevCaps3.dwAxes;
                break;
            }
            return (GET_DIDEVICE_SUBTYPE(g_stDIDevCaps3.dwDevType));
        }
        else
        {
            if (g_stDIDevCaps.dwFlags & DIDC_ATTACHED)
                hDev->m_p_stState->m_ucStatus |= INO_C_ucConnected;
            p_stCaps->m_ucNbButtons = (unsigned char)g_stDIDevCaps.dwButtons;
            switch (INO_M_uwType(hDev))
            {
            case INO_C_uwJoystick:
                ((INO_tdstJoystickCaps*)p_stCaps)->m_ucNbAxes = (unsigned char)g_stDIDevCaps.dwAxes;
                ((INO_tdstJoystickCaps*)p_stCaps)->m_ucNbPOVs = (unsigned char)g_stDIDevCaps.dwPOVs;
                break;
            }
            return (GET_DIDEVICE_SUBTYPE(g_stDIDevCaps.dwDevType));
        }
    }
    else
    {
        hDev->m_p_stState->m_ucStatus &= ~INO_C_ucConnected;
        hDev->m_p_stState->m_ucStatus &= ~INO_C_ucAcquired;
        INO_fn_vUpdateLastError(E_uwINO_DeviceInitFailed);
        return (0);
    }
}
 
 
 
short INO_fn_wIsConnected(INO_tdhDevice hDev)
{
    HRESULT  DInputErr;
    short wDev;
 
    /* JFP: what is this used for? Is it necessary?*/
    for (wDev=0; wDev<INO_C_wNbMaxDevice; wDev++)
        if (INO_g_a_stDIDevice[wDev].m_hDevice == hDev) 
            break;
    if (wDev == INO_C_wNbMaxDevice) 
        return (0);
 
    /* Use DI 3 structure to go faster */
    memset(&g_stDIDevCaps3,0,sizeof(DIDEVCAPS_DX3));
    g_stDIDevCaps3.dwSize = sizeof(DIDEVCAPS_DX3);
    DInputErr = M_DIGetCaps(wDev,(DIDEVCAPS*)&g_stDIDevCaps3);
 
    if (DInputErr == DI_OK)
    {
        if (g_stDIDevCaps3.dwFlags & DIDC_ATTACHED)
        {
            hDev->m_p_stState->m_ucStatus |= INO_C_ucConnected;
            return (1);
        }
        else
            hDev->m_p_stState->m_ucStatus &= ~INO_C_ucConnected;
    }
 
    return (0);
}
