/*		DirectInput
 *
 * Copyright 1998 Marcus Meissner
 * Copyright 1998,1999 Lionel Ulmer
 * Copyright 2000-2002 TransGaming Technologies Inc.
 *
 */
/* Status:
 *
 * - Tomb Raider 2 Demo:
 *   Playable using keyboard only.
 * - WingCommander Prophecy Demo:
 *   Doesn't get Input Focus.
 *
 * - Fallout : works great in X and DGA mode
 */

#include "config.h"
#include <assert.h>
#include <string.h>

#include "wine/debug.h"
#include "winbase.h"
#include "winuser.h"
#include "winerror.h"
#include "windef.h"
#include "dinput_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(dinput);

extern void WINAPI __wine_get_hal(LPDDRAWI_DIRECTDRAW_GBL *gbl, LPDDHAL_GETDRIVERINFO *ddgdi);

LPDDRAWI_DIRECTDRAW_GBL dinput_dd_gbl;

static ICOM_VTABLE(IDirectInput7A) ddi7avt;
static ICOM_VTABLE(IDirectInput7W) ddi7wvt;
static ICOM_VTABLE(IDirectInput8A) ddi8avt;
static ICOM_VTABLE(IDirectInput8W) ddi8wvt;

/* This array will be filled a dinput.so loading */
#define MAX_WINE_DINPUT_DEVICES 8
static dinput_device * dinput_devices[MAX_WINE_DINPUT_DEVICES];
static int nrof_dinput_devices = 0;

BOOL WINAPI Init( HINSTANCE inst, DWORD reason, LPVOID reserv)
{
    switch(reason)
    {
      case DLL_PROCESS_ATTACH:
        __wine_get_hal(&dinput_dd_gbl, NULL);
        init_keyboard();
        init_joystick();
        init_mouse();
        break;
      case DLL_PROCESS_DETACH:
        deinit_mouse();
        deinit_joystick();
        deinit_keyboard();
        dinput_dd_gbl = NULL;
        break;
    }
    return TRUE;
}


/* register a direct draw driver. */
void dinput_register_device(dinput_device *device) {
    int	i;

    /* insert according to priority */
    for (i=0;i<nrof_dinput_devices;i++) {
	if (dinput_devices[i]->pref <= device->pref) {
	    RtlMoveMemory(dinput_devices+i+1,dinput_devices+i,sizeof(dinput_devices[0])*(nrof_dinput_devices-i));
	    dinput_devices[i] = device;
	    break;
	}
    }
    if (i==nrof_dinput_devices)	/* not found, or too low priority */
	dinput_devices[nrof_dinput_devices] = device;

    nrof_dinput_devices++;

    /* increase MAX_DDRAW_DRIVERS if the line below triggers */
    assert(nrof_dinput_devices <= MAX_WINE_DINPUT_DEVICES);
}

/******************************************************************************
 *	DirectInputCreateEx (DINPUT.@)
 */
HRESULT WINAPI DirectInputCreateEx(
	HINSTANCE hinst, DWORD dwVersion, REFIID riid, LPVOID *ppDI,
	LPUNKNOWN punkOuter
) {
	IDirectInputImpl* This;

	TRACE("(0x%08lx,%04lx,%s,%p,%p)\n",
		(DWORD)hinst,dwVersion,debugstr_guid(riid),ppDI,punkOuter
	);
	if (IsEqualGUID(&IID_IDirectInputA,riid) ||
	    IsEqualGUID(&IID_IDirectInput2A,riid) ||
	    IsEqualGUID(&IID_IDirectInput7A,riid)) {
	  This = (IDirectInputImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
	  This->lpVtbl = &ddi7avt;
	  This->ref = 1;
	  This->ver = dwVersion;
	  *ppDI = This;

	  return DI_OK;
	}

	if (IsEqualGUID(&IID_IDirectInputW,riid) ||
	    IsEqualGUID(&IID_IDirectInput2W,riid) ||
	    IsEqualGUID(&IID_IDirectInput7W,riid)) {
	  This = (IDirectInputImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
	  This->lpVtbl = &ddi7wvt;
	  This->ref = 1;
	  This->ver = dwVersion;
	  *ppDI = This;

	  return DI_OK;
	}

	if (IsEqualGUID(&IID_IDirectInput8A,riid)) {
	  This = (IDirectInputImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
	  This->lpVtbl = &ddi8avt;
	  This->ref = 1;
	  This->ver = dwVersion;
	  *ppDI = This;

	  return DI_OK;
	}

	if (IsEqualGUID(&IID_IDirectInput8W,riid)) {
	  This = (IDirectInputImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
	  This->lpVtbl = &ddi8wvt;
	  This->ref = 1;
	  This->ver = dwVersion;
	  *ppDI = This;

	  return DI_OK;
	}

	return DIERR_OLDDIRECTINPUTVERSION;
}

/******************************************************************************
 *	DirectInputCreateA (DINPUT.@)
 */
HRESULT WINAPI DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter)
{
	IDirectInputImpl* This;
	TRACE("(0x%08lx,%04lx,%p,%p)\n",
		(DWORD)hinst,dwVersion,ppDI,punkOuter
	);
	This = (IDirectInputImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
	This->lpVtbl = &ddi7avt;
	This->ref = 1;
	This->ver = dwVersion;
	*ppDI=(IDirectInputA*)This;
	return 0;

}

/******************************************************************************
 *	DirectInputCreateW (DINPUT.@)
 */
HRESULT WINAPI DirectInputCreateW(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTW *ppDI, LPUNKNOWN punkOuter)
{
	IDirectInputImpl* This;
	TRACE("(0x%08lx,%04lx,%p,%p)\n",
		(DWORD)hinst,dwVersion,ppDI,punkOuter
	);
	This = (IDirectInputImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
	This->lpVtbl = &ddi7wvt;
	This->ref = 1;
	This->ver = 0;
	*ppDI=(IDirectInputW*)This;
	return 0;

}

/******************************************************************************
 *	IDirectInputA_EnumDevices
 */
static HRESULT WINAPI IDirectInputAImpl_EnumDevices(
	LPDIRECTINPUT7A iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback,
	LPVOID pvRef, DWORD dwFlags
)
{
	ICOM_THIS(IDirectInputImpl,iface);
	DIDEVICEINSTANCEA devInstance;
	int i, j, r;

	TRACE("(this=%p,0x%04lx,%p,%p,%04lx)\n", This, dwDevType, lpCallback, pvRef, dwFlags);

	for (i = 0; i < nrof_dinput_devices; i++) {
	  for (j = 0, r = -1; r != 0; j++) {
	    devInstance.dwSize = sizeof(devInstance);
	    r = DeviceThunkA_enum_device(dinput_devices[i], This, dwDevType, 
					 dwFlags, &devInstance, j);
	    if (r > 0 && lpCallback(&devInstance,pvRef) == DIENUM_STOP)
	      return 0;
	  }
	}

	return 0;
}

/******************************************************************************
 *	IDirectInputW_EnumDevices
 */
static HRESULT WINAPI IDirectInputWImpl_EnumDevices(
	LPDIRECTINPUT7W iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback,
	LPVOID pvRef, DWORD dwFlags
)
{
	ICOM_THIS(IDirectInputImpl,iface);
	DIDEVICEINSTANCEW devInstance;
	int i, j, r;

	TRACE("(this=%p,0x%04lx,%p,%p,%04lx)\n", This, dwDevType, lpCallback, pvRef, dwFlags);

	for (i = 0; i < nrof_dinput_devices; i++) {
	  for (j = 0, r = -1; r != 0; j++) {
	    devInstance.dwSize = sizeof(devInstance);
	    r = DeviceThunkW_enum_device(dinput_devices[i], This, dwDevType, 
					 dwFlags, &devInstance, j);
	    if (r > 0 && lpCallback(&devInstance,pvRef) == DIENUM_STOP)
	      return 0;
	  }
	}

	return 0;
}

static HRESULT WINAPI IDirectInputAImpl_QueryInterface(
	LPDIRECTINPUT7A iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IDirectInputImpl,iface);

	TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	if (IsEqualGUID(&IID_IUnknown,riid) ||
	    IsEqualGUID(&IID_IDirectInputA,riid) ||
	    IsEqualGUID(&IID_IDirectInput2A,riid) ||
	    IsEqualGUID(&IID_IDirectInput7A,riid)) {
		IDirectInput_AddRef(iface);
		*ppobj = This;
		return 0;
	}
	TRACE("Unsupported interface !\n");
	return E_FAIL;
}

static HRESULT WINAPI IDirectInputWImpl_QueryInterface(
	LPDIRECTINPUT7W iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IDirectInputImpl,iface);

	TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	if (IsEqualGUID(&IID_IUnknown,riid) ||
	    IsEqualGUID(&IID_IDirectInputW,riid) ||
	    IsEqualGUID(&IID_IDirectInput2W,riid) ||
	    IsEqualGUID(&IID_IDirectInput7W,riid)) {
		IDirectInput_AddRef(iface);
		*ppobj = This;
		return 0;
	}
	TRACE("Unsupported interface !\n");
	return E_FAIL;
}

static ULONG WINAPI IDirectInputAImpl_AddRef(LPDIRECTINPUT7A iface)
{
	ICOM_THIS(IDirectInputImpl,iface);
	return ++(This->ref);
}

static ULONG WINAPI IDirectInputWImpl_AddRef(LPDIRECTINPUT7W iface)
{
	ICOM_THIS(IDirectInputImpl,iface);
	return ++(This->ref);
}

static ULONG WINAPI IDirectInputAImpl_Release(LPDIRECTINPUT7A iface)
{
	ICOM_THIS(IDirectInputImpl,iface);
	if (!(--This->ref)) {
		HeapFree(GetProcessHeap(),0,This);
		return 0;
	}
	return This->ref;
}

static ULONG WINAPI IDirectInputWImpl_Release(LPDIRECTINPUT7W iface)
{
	ICOM_THIS(IDirectInputImpl,iface);
	if (!(--This->ref)) {
		HeapFree(GetProcessHeap(),0,This);
		return 0;
	}
	return This->ref;
}

static HRESULT WINAPI IDirectInputAImpl_CreateDevice(
	LPDIRECTINPUT7A iface,REFGUID rguid,LPDIRECTINPUTDEVICEA* pdev,
	LPUNKNOWN punk
) {
	ICOM_THIS(IDirectInputImpl,iface);
	HRESULT ret_value = DIERR_DEVICENOTREG;
	int i;

	TRACE("(this=%p,%s,%p,%p)\n",This,debugstr_guid(rguid),pdev,punk);

	/* Loop on all the devices to see if anyone matches the given GUID */
	for (i = 0; i < nrof_dinput_devices; i++) {
	  HRESULT ret = DIERR_DEVICENOTREG;
	  if ((ret = DeviceThunkA_create_device(dinput_devices[i], This, rguid, NULL, pdev)) == DI_OK)
	    return DI_OK;

	  if (ret == DIERR_NOINTERFACE)
	    ret_value = DIERR_NOINTERFACE;
	}

	return ret_value;
}

static HRESULT WINAPI IDirectInputWImpl_CreateDevice(
	LPDIRECTINPUT7W iface,REFGUID rguid,LPDIRECTINPUTDEVICEW* pdev,
	LPUNKNOWN punk
) {
	ICOM_THIS(IDirectInputImpl,iface);
	HRESULT ret_value = DIERR_DEVICENOTREG;
	int i;

	TRACE("(this=%p,%s,%p,%p)\n",This,debugstr_guid(rguid),pdev,punk);

	/* Loop on all the devices to see if anyone matches the given GUID */
	for (i = 0; i < nrof_dinput_devices; i++) {
	  HRESULT ret = DIERR_DEVICENOTREG;
	  if ((ret = DeviceThunkW_create_device(dinput_devices[i], This, rguid, NULL, pdev)) == DI_OK)
	    return DI_OK;

	  if (ret == DIERR_NOINTERFACE)
	    ret_value = DIERR_NOINTERFACE;
	}

	return ret_value;
}

static HRESULT WINAPI IDirectInputAImpl_Initialize(
	LPDIRECTINPUT7A iface,HINSTANCE hinst,DWORD x
) {
	return DIERR_ALREADYINITIALIZED;
}

static HRESULT WINAPI IDirectInputWImpl_Initialize(
	LPDIRECTINPUT7W iface,HINSTANCE hinst,DWORD x
) {
	return DIERR_ALREADYINITIALIZED;
}

static HRESULT WINAPI IDirectInputAImpl_GetDeviceStatus(LPDIRECTINPUT7A iface,
							REFGUID rguid) {
  ICOM_THIS(IDirectInputImpl,iface);

  FIXME("(%p)->(%s): stub\n",This,debugstr_guid(rguid));

  return DI_OK;
}

static HRESULT WINAPI IDirectInputWImpl_GetDeviceStatus(LPDIRECTINPUT7W iface,
							REFGUID rguid) {
  ICOM_THIS(IDirectInputImpl,iface);

  FIXME("(%p)->(%s): stub\n",This,debugstr_guid(rguid));

  return DI_OK;
}

static HRESULT WINAPI IDirectInputAImpl_RunControlPanel(LPDIRECTINPUT7A iface,
							HWND hwndOwner,
							DWORD dwFlags) {
  ICOM_THIS(IDirectInputImpl,iface);
  FIXME("(%p)->(%08lx,%08lx): stub\n",This, (DWORD) hwndOwner, dwFlags);

  return DI_OK;
}

static HRESULT WINAPI IDirectInputWImpl_RunControlPanel(LPDIRECTINPUT7W iface,
							HWND hwndOwner,
							DWORD dwFlags) {
  ICOM_THIS(IDirectInputImpl,iface);
  FIXME("(%p)->(%08lx,%08lx): stub\n",This, (DWORD) hwndOwner, dwFlags);

  return DI_OK;
}

static HRESULT WINAPI IDirectInput2AImpl_FindDevice(LPDIRECTINPUT7A iface, REFGUID rguid,
						    LPCSTR pszName, LPGUID pguidInstance) {
  ICOM_THIS(IDirectInputImpl,iface);
  FIXME("(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), debugstr_a(pszName), pguidInstance);

  return DI_OK;
}

static HRESULT WINAPI IDirectInput2WImpl_FindDevice(LPDIRECTINPUT7W iface, REFGUID rguid,
						    LPCWSTR pszName, LPGUID pguidInstance) {
  ICOM_THIS(IDirectInputImpl,iface);
  FIXME("(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), debugstr_w(pszName), pguidInstance);

  return DI_OK;
}

static HRESULT WINAPI IDirectInput7AImpl_CreateDeviceEx(LPDIRECTINPUT7A iface, REFGUID rguid,
							REFIID riid, LPVOID* pvOut, LPUNKNOWN lpUnknownOuter)
{
  ICOM_THIS(IDirectInputImpl,iface);
  HRESULT ret_value = DIERR_DEVICENOTREG;
  int i;

  TRACE("(%p)->(%s, %s, %p, %p)\n", This, debugstr_guid(rguid), debugstr_guid(riid), pvOut, lpUnknownOuter);

  /* Loop on all the devices to see if anyone matches the given GUID */
  for (i = 0; i < nrof_dinput_devices; i++) {
    HRESULT ret;
    if ((ret = DeviceThunkA_create_device(dinput_devices[i], This, rguid, riid, (LPDIRECTINPUTDEVICEA*) pvOut)) == DI_OK)
      return DI_OK;

    if (ret == DIERR_NOINTERFACE)
      ret_value = DIERR_NOINTERFACE;
  }

  return ret_value;
}

static HRESULT WINAPI IDirectInput7WImpl_CreateDeviceEx(LPDIRECTINPUT7W iface, REFGUID rguid,
							REFIID riid, LPVOID* pvOut, LPUNKNOWN lpUnknownOuter)
{
  ICOM_THIS(IDirectInputImpl,iface);
  HRESULT ret_value = DIERR_DEVICENOTREG;
  int i;

  TRACE("(%p)->(%s, %s, %p, %p)\n", This, debugstr_guid(rguid), debugstr_guid(riid), pvOut, lpUnknownOuter);

  /* Loop on all the devices to see if anyone matches the given GUID */
  for (i = 0; i < nrof_dinput_devices; i++) {
    HRESULT ret;
    if ((ret = DeviceThunkW_create_device(dinput_devices[i], This, rguid, riid, (LPDIRECTINPUTDEVICEW*) pvOut)) == DI_OK)
      return DI_OK;

    if (ret == DIERR_NOINTERFACE)
      ret_value = DIERR_NOINTERFACE;
  }

  return ret_value;
}

static HRESULT WINAPI IDirectInput8AImpl_QueryInterface(
	LPDIRECTINPUT8A iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IDirectInputImpl,iface);

	TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	if (IsEqualGUID(&IID_IUnknown,riid) ||
	    IsEqualGUID(&IID_IDirectInput8A,riid)) {
		IDirectInput_AddRef(iface);
		*ppobj = This;
		return 0;
	}
	TRACE("Unsupported interface !\n");
	return E_FAIL;
}

static HRESULT WINAPI IDirectInput8WImpl_QueryInterface(
	LPDIRECTINPUT8W iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IDirectInputImpl,iface);

	TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	if (IsEqualGUID(&IID_IUnknown,riid) ||
	    IsEqualGUID(&IID_IDirectInput8W,riid)) {
		IDirectInput_AddRef(iface);
		*ppobj = This;
		return 0;
	}
	TRACE("Unsupported interface !\n");
	return E_FAIL;
}

static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
	LPDIRECTINPUT8A iface, LPCSTR ptszUserName, LPDIACTIONFORMATA lpdiActionFormat,
	LPDIENUMDEVICESBYSEMANTICSCBA lpCallback,
	LPVOID pvRef, DWORD dwFlags
)
{
	ICOM_THIS(IDirectInputImpl,iface);
	LPDIACTIONA act;
	DWORD c;
	DIACTIONFORMATA tfmt;
	LPDIRECTINPUTDEVICE8A pdev;
	DIDEVICEINSTANCEA devInstance;
	HRESULT hr;
	BOOL res;
	int i, j, r;

	FIXME("(this=%p,%s,%p,%p,%p,%04lx): semi-stub\n", This, debugstr_a(ptszUserName), lpdiActionFormat,
	      lpCallback, pvRef, dwFlags);
	TRACE("dwSize=%ld\n", lpdiActionFormat->dwSize);
	TRACE("dwActionSize=%ld\n", lpdiActionFormat->dwActionSize);
	TRACE("dwDataSize=%ld\n", lpdiActionFormat->dwDataSize);
	TRACE("dwNumActions=%ld\n", lpdiActionFormat->dwNumActions);
	TRACE("guidActionMap=%s\n", debugstr_guid(&lpdiActionFormat->guidActionMap));
	TRACE("dwGenre=0x%lx\n", lpdiActionFormat->dwGenre);
	TRACE("dwBufferSize=%ld\n", lpdiActionFormat->dwBufferSize);
	TRACE("lAxisMin=%ld\n", lpdiActionFormat->lAxisMin);
	TRACE("lAxisMax=%ld\n", lpdiActionFormat->lAxisMax);
	TRACE("hInstString=%08x\n", lpdiActionFormat->hInstString);
	TRACE("tszActionMap=%s\n", debugstr_a(lpdiActionFormat->tszActionMap));

	memcpy(&tfmt, lpdiActionFormat, sizeof(tfmt));
	tfmt.rgoAction = HeapAlloc(GetProcessHeap(), 0, tfmt.dwNumActions * sizeof(DIACTIONA));

	act = lpdiActionFormat->rgoAction;
	for (c=0; c<lpdiActionFormat->dwNumActions; c++,act++) {
		memcpy(tfmt.rgoAction+c, act, sizeof(DIACTIONA));

		TRACE("action %ld\n", c);
		TRACE("  uAppData=%d\n", act->uAppData);
		TRACE("  dwSemantics=0x%08lx\n", act->dwSemantic);
		TRACE("  dwFlags=%ld\n", act->dwFlags);
		TRACE("  lptszActionName=%s\n", debugstr_a(act->u.lptszActionName));
		TRACE("  guidInstance=%s\n", debugstr_guid(&act->guidInstance));
		TRACE("  dwObjID=%ld\n", act->dwObjID);
		TRACE("  dwHow=%ld\n", act->dwHow);
	}


	/* FIXME: keyboard and mouse should be enumerated last, according to MSDN,
	 * and the callback has a "devices left to enumerate" argument, and the
	 * devices should be enumerated in order of user preference, so we should
	 * probably build a list of devices to enumerate first, sort it, and then
	 * show the sorted list to the application. */

	for (i = 0; i < nrof_dinput_devices; i++) {
	  for (j = 0, r = -1; r != 0; j++) {
	    devInstance.dwSize = sizeof(devInstance);
	    r = DeviceThunkA_enum_device(dinput_devices[i], This, 0,
					 dwFlags, &devInstance, j);
	    if (r <= 0) continue;
	    /* try to create device */
	    hr = DeviceThunkA_create_device(dinput_devices[i], This,
					    &devInstance.guidInstance,
					    &IID_IDirectInputDevice8A,
					    (LPDIRECTINPUTDEVICEA*)&pdev);
	    if (FAILED(hr)) continue;
	    /* clear action map to default */
	    for (c=0; c<tfmt.dwNumActions; c++) {
	      tfmt.rgoAction[c].dwObjID = 0;
	      tfmt.rgoAction[c].dwHow = DIAH_UNMAPPED;
	    }
	    /* try to map it (FIXME: what DIDBAM_ flag to use?) */
	    hr = IDirectInputDevice8_BuildActionMap(pdev, &tfmt, ptszUserName, DIDBAM_INITIALIZE);
	    if (FAILED(hr) || hr == DI_NOEFFECT) {
	      IDirectInputDevice8_Release(pdev);
	      continue;
	    }
	    /* FIXME: need to verify that all necessary controls were actually mapped */

	    /* call application callback */
	    res = lpCallback(&devInstance, pdev, DIEDBS_MAPPEDPRI1, 0, pvRef);
	    IDirectInputDevice8_Release(pdev);
	    if (res == DIENUM_STOP)
	      goto cleanup;
	  }
	}

cleanup:
	HeapFree(GetProcessHeap(), 0, tfmt.rgoAction);
	return 0;
}

static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics(
	LPDIRECTINPUT8W iface, LPCWSTR ptszUserName, LPDIACTIONFORMATW lpdiActionFormat,
	LPDIENUMDEVICESBYSEMANTICSCBW lpCallback,
	LPVOID pvRef, DWORD dwFlags
)
{
	ICOM_THIS(IDirectInputImpl,iface);

	FIXME("(this=%p,%s,%p,%p,%p,%04lx): stub\n", This, debugstr_w(ptszUserName), lpdiActionFormat,
	      lpCallback, pvRef, dwFlags);
	return 0;
}

static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
	LPDIRECTINPUT8A iface, LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
	LPDICONFIGUREDEVICESPARAMSA lpdiCDParams, DWORD dwFlags, LPVOID pvRefData
)
{
	ICOM_THIS(IDirectInputImpl,iface);

	FIXME("(this=%p,%p,%p,%04lx,%p): stub\n", This, lpdiCallback, lpdiCDParams,
	      dwFlags, pvRefData);
	return 0;
}

static HRESULT WINAPI IDirectInput8WImpl_ConfigureDevices(
	LPDIRECTINPUT8W iface, LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
	LPDICONFIGUREDEVICESPARAMSW lpdiCDParams, DWORD dwFlags, LPVOID pvRefData
)
{
	ICOM_THIS(IDirectInputImpl,iface);

	FIXME("(this=%p,%p,%p,%04lx,%p): stub\n", This, lpdiCallback, lpdiCDParams,
	      dwFlags, pvRefData);
	return 0;
}

#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
# define XCAST(fun)	(typeof(ddi7avt.fun))
#else
# define XCAST(fun)	(void*)
#endif

static ICOM_VTABLE(IDirectInput7A) ddi7avt = {
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	XCAST(QueryInterface)IDirectInputAImpl_QueryInterface,
	XCAST(AddRef)IDirectInputAImpl_AddRef,
	XCAST(Release)IDirectInputAImpl_Release,
	XCAST(CreateDevice)IDirectInputAImpl_CreateDevice,
	XCAST(EnumDevices)IDirectInputAImpl_EnumDevices,
	XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
	XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
	XCAST(Initialize)IDirectInputAImpl_Initialize,
	XCAST(FindDevice)IDirectInput2AImpl_FindDevice,
	IDirectInput7AImpl_CreateDeviceEx
};
#undef XCAST

#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
# define XCAST(fun)	(typeof(ddi7wvt.fun))
#else
# define XCAST(fun)	(void*)
#endif

static ICOM_VTABLE(IDirectInput7W) ddi7wvt = {
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	XCAST(QueryInterface)IDirectInputWImpl_QueryInterface,
	XCAST(AddRef)IDirectInputWImpl_AddRef,
	XCAST(Release)IDirectInputWImpl_Release,
	XCAST(CreateDevice)IDirectInputWImpl_CreateDevice,
	XCAST(EnumDevices)IDirectInputWImpl_EnumDevices,
	XCAST(GetDeviceStatus)IDirectInputWImpl_GetDeviceStatus,
	XCAST(RunControlPanel)IDirectInputWImpl_RunControlPanel,
	XCAST(Initialize)IDirectInputWImpl_Initialize,
	XCAST(FindDevice)IDirectInput2WImpl_FindDevice,
	IDirectInput7WImpl_CreateDeviceEx
};
#undef XCAST

#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
# define XCAST(fun)	(typeof(ddi8avt.fun))
#else
# define XCAST(fun)	(void*)
#endif

static ICOM_VTABLE(IDirectInput8A) ddi8avt = {
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	XCAST(QueryInterface)IDirectInput8AImpl_QueryInterface,
	XCAST(AddRef)IDirectInputAImpl_AddRef,
	XCAST(Release)IDirectInputAImpl_Release,
	XCAST(CreateDevice)IDirectInputAImpl_CreateDevice,
	XCAST(EnumDevices)IDirectInputAImpl_EnumDevices,
	XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
	XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
	XCAST(Initialize)IDirectInputAImpl_Initialize,
	XCAST(FindDevice)IDirectInput2AImpl_FindDevice,
	IDirectInput8AImpl_EnumDevicesBySemantics,
	IDirectInput8AImpl_ConfigureDevices
};
#undef XCAST

#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
# define XCAST(fun)	(typeof(ddi8wvt.fun))
#else
# define XCAST(fun)	(void*)
#endif

static ICOM_VTABLE(IDirectInput8W) ddi8wvt = {
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	XCAST(QueryInterface)IDirectInput8WImpl_QueryInterface,
	XCAST(AddRef)IDirectInputWImpl_AddRef,
	XCAST(Release)IDirectInputWImpl_Release,
	XCAST(CreateDevice)IDirectInputWImpl_CreateDevice,
	XCAST(EnumDevices)IDirectInputWImpl_EnumDevices,
	XCAST(GetDeviceStatus)IDirectInputWImpl_GetDeviceStatus,
	XCAST(RunControlPanel)IDirectInputWImpl_RunControlPanel,
	XCAST(Initialize)IDirectInputWImpl_Initialize,
	XCAST(FindDevice)IDirectInput2WImpl_FindDevice,
	IDirectInput8WImpl_EnumDevicesBySemantics,
	IDirectInput8WImpl_ConfigureDevices
};
#undef XCAST

/***********************************************************************
 *		DllCanUnloadNow (DINPUT.@)
 */
HRESULT WINAPI DINPUT_DllCanUnloadNow(void)
{
    FIXME("(void): stub\n");

    return S_FALSE;
}

/***********************************************************************
 *		DllGetClassObject (DINPUT.@)
 */
HRESULT WINAPI DINPUT_DllGetClassObject(REFCLSID rclsid, REFIID riid,
					LPVOID *ppv)
{
    FIXME("(%p, %p, %p): stub\n", debugstr_guid(rclsid),
	  debugstr_guid(riid), ppv);

    return CLASS_E_CLASSNOTAVAILABLE;
}

/***********************************************************************
 *		DllRegisterServer (DINPUT.@)
 */
HRESULT WINAPI DINPUT_DllRegisterServer(void)
{
    FIXME("(void): stub\n");

    return S_OK;
}

/***********************************************************************
 *		DllUnregisterServer (DINPUT.@)
 */
HRESULT WINAPI DINPUT_DllUnregisterServer(void)
{
    FIXME("(void): stub\n");

    return S_OK;
}
