/*		DirectInput Device Thunks
 *
 * Copyright 2002 TransGaming Technologies Inc.
 *
 */
#include "config.h"

#include <string.h>
#include "wine/debug.h"
#include "winbase.h"
#include "winerror.h"
#include "windef.h"
#include "winnls.h"
#include "dinput.h"
#include "device_private.h"
#include "dinput_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(dinput);

static ICOM_VTABLE(IDirectInputDevice8W) DeviceThunkWvt;

typedef struct DeviceThunkWImpl DeviceThunkWImpl;
struct DeviceThunkWImpl
{
	LPVOID	lpVtbl;
	DWORD	ref;
	LPDIRECTINPUTDEVICE8A	dev;
};

/******************************************************************************
 *	IDirectInputDeviceA
 */

static REFIID DeviceThunkW_MapIID(REFIID riid)
{
	if (!riid) return NULL;
	if (IsEqualGUID(&IID_IUnknown,riid)) return &IID_IUnknown;
	if (IsEqualGUID(&IID_IDirectInputDeviceW,riid)) return &IID_IDirectInputDeviceA;
	if (IsEqualGUID(&IID_IDirectInputDevice2W,riid)) return &IID_IDirectInputDevice2A;
	if (IsEqualGUID(&IID_IDirectInputDevice7W,riid)) return &IID_IDirectInputDevice7A;
	if (IsEqualGUID(&IID_IDirectInputDevice8W,riid)) return &IID_IDirectInputDevice8A;
	return NULL;
}

static HRESULT WINAPI DeviceThunkWImpl_QueryInterface(
	LPDIRECTINPUTDEVICE8W iface,REFIID riid,LPVOID *ppobj
)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	const IID *piid;
	LPVOID dummy;
	HRESULT hr;

	TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);

	piid = DeviceThunkW_MapIID(riid);
	if (!piid) {
		TRACE("Unsupported interface !\n");
		return E_NOINTERFACE;
	}

	/* make sure whatever we're thunking to supports this interface too */
	hr = IDirectInputDevice2_QueryInterface(This->dev, piid, &dummy);
	if (FAILED(hr)) return hr;
	IUnknown_Release((IUnknown*)dummy);

	/* success */
	IDirectInputDevice8_AddRef(iface);
	*ppobj = This;
	return S_OK;
}

static ULONG WINAPI DeviceThunkWImpl_AddRef(
	LPDIRECTINPUTDEVICE8W iface)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	return ++This->ref;
}

static ULONG WINAPI DeviceThunkWImpl_Release(
	LPDIRECTINPUTDEVICE8W iface)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	This->ref--;
	if (This->ref)
		return This->ref;
	IDirectInputDevice8_Release(This->dev);
	HeapFree(GetProcessHeap(),0,This);
	return 0;
}

static HRESULT WINAPI DeviceThunkWImpl_GetCapabilities(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIDEVCAPS lpDIDevCaps)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%p)\n", iface, lpDIDevCaps);
	return IDirectInputDevice8_GetCapabilities(This->dev, lpDIDevCaps);
}

static HRESULT WINAPI DeviceThunkWImpl_EnumObjects(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
	LPVOID lpvRef,
	DWORD dwFlags)
{
	FIXME("(this=%p,%p,%p,%08lx): stub!\n", iface, lpCallback, lpvRef, dwFlags);
	if (TRACE_ON(dinput)) {
	  DPRINTF("  - flags = ");
	  _dump_EnumObjects_flags(dwFlags);
	  DPRINTF("\n");
	}

	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_GetProperty(
	LPDIRECTINPUTDEVICE8W iface,
	REFGUID rguid,
	LPDIPROPHEADER pdiph)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%s,%p)\n",
	      iface, debugstr_guid(rguid), pdiph);
	return IDirectInputDevice8_GetProperty(This->dev, rguid, pdiph);
}

static HRESULT WINAPI DeviceThunkWImpl_SetProperty(
	LPDIRECTINPUTDEVICE8W iface,
	REFGUID rguid,
	LPCDIPROPHEADER pdiph)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%s,%p)\n",
	      iface, debugstr_guid(rguid), pdiph);
	return IDirectInputDevice8_SetProperty(This->dev, rguid, pdiph);
}

static HRESULT WINAPI DeviceThunkWImpl_Acquire(
	LPDIRECTINPUTDEVICE8W iface)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p)\n", iface);
	return IDirectInputDevice8_Acquire(This->dev);
}

static HRESULT WINAPI DeviceThunkWImpl_Unacquire(
	LPDIRECTINPUTDEVICE8W iface)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p)\n", iface);
	return IDirectInputDevice8_Unacquire(This->dev);
}

static HRESULT WINAPI DeviceThunkWImpl_GetDeviceState(
	LPDIRECTINPUTDEVICE8W iface,
	DWORD cbData,
	LPVOID lpvData)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%ld,%p)\n",
	      iface, cbData, lpvData);
	return IDirectInputDevice8_GetDeviceState(This->dev, cbData, lpvData);
}

static HRESULT WINAPI DeviceThunkWImpl_GetDeviceData(
	LPDIRECTINPUTDEVICE8W iface,
	DWORD cbObjectData,
	LPDIDEVICEOBJECTDATA rgdod,
	LPDWORD pdwInOut,
	DWORD dwFlags)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%ld,%p,%p,0x%lx)\n",
	      iface, cbObjectData, rgdod, pdwInOut, dwFlags);
	return IDirectInputDevice8_GetDeviceData(This->dev, cbObjectData, rgdod, pdwInOut, dwFlags);
}

static HRESULT WINAPI DeviceThunkWImpl_SetDataFormat(
	LPDIRECTINPUTDEVICE8W iface,
	LPCDIDATAFORMAT lpdf)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%p)\n",
	      iface,lpdf);
	return IDirectInputDevice8_SetDataFormat(This->dev, lpdf);
}

static HRESULT WINAPI DeviceThunkWImpl_SetEventNotification(
	LPDIRECTINPUTDEVICE8W iface,
	HANDLE hEvent)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,0x%08lx): stub\n",
	      iface,(DWORD)hEvent);
	return IDirectInputDevice8_SetEventNotification(This->dev, hEvent);
}

static HRESULT WINAPI DeviceThunkWImpl_SetCooperativeLevel(
	LPDIRECTINPUTDEVICE8W iface,
	HWND hwnd,
	DWORD dwFlags)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,0x%08lx,0x%08lx)\n",
	      iface,(DWORD)hwnd,dwFlags);
	return IDirectInputDevice8_SetCooperativeLevel(This->dev, hwnd, dwFlags);
}

static HRESULT WINAPI DeviceThunkWImpl_GetObjectInfo(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIDEVICEOBJECTINSTANCEW pdidoi,
	DWORD dwObj,
	DWORD dwHow)
{
	FIXME("(this=%p,%p,%ld,0x%08lx): stub!\n",
	      iface, pdidoi, dwObj, dwHow);

	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_GetDeviceInfo(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIDEVICEINSTANCEW pdidi)
{
	FIXME("(this=%p,%p): stub!\n",
	      iface, pdidi);

	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_RunControlPanel(
	LPDIRECTINPUTDEVICE8W iface,
	HWND hwndOwner,
	DWORD dwFlags)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,0x%08lx,0x%08lx)\n",
	      iface,(DWORD)hwndOwner,dwFlags);
	return IDirectInputDevice8_RunControlPanel(This->dev, hwndOwner, dwFlags);
}

static HRESULT WINAPI DeviceThunkWImpl_Initialize(
	LPDIRECTINPUTDEVICE8W iface,
	HINSTANCE hinst,
	DWORD dwVersion,
	REFGUID rguid)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%d,%ld,%s)\n",
	      iface,hinst,dwVersion,debugstr_guid(rguid));
	return IDirectInputDevice8_Initialize(This->dev, hinst, dwVersion, rguid);
}

static HRESULT WINAPI DeviceThunkWImpl_CreateEffect(
	LPDIRECTINPUTDEVICE8W iface,
	REFGUID rguid,
	LPCDIEFFECT lpeff,
	LPDIRECTINPUTEFFECT *ppdef,
	LPUNKNOWN pUnkOuter)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%s,%p,%p,%p)\n",
	      iface, debugstr_guid(rguid), lpeff, ppdef, pUnkOuter);
	return IDirectInputDevice8_CreateEffect(This->dev, rguid, lpeff, ppdef, pUnkOuter);
}

static HRESULT WINAPI DeviceThunkWImpl_EnumEffects(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIENUMEFFECTSCALLBACKW lpCallback,
	LPVOID lpvRef,
	DWORD dwFlags)
{
	/* ICOM_THIS(DeviceThunkWImpl,iface); */
	FIXME("(this=%p,%p,%p,0x%08lx): stub!\n",
	      iface, lpCallback, lpvRef, dwFlags);
	if (lpCallback)
		lpCallback(NULL, lpvRef);
	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_GetEffectInfo(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIEFFECTINFOW lpdei,
	REFGUID rguid)
{
	/* ICOM_THIS(DeviceThunkWImpl,iface); */
	FIXME("(this=%p,%p,%s): stub!\n",
	      iface, lpdei, debugstr_guid(rguid));
	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_GetForceFeedbackState(
	LPDIRECTINPUTDEVICE8W iface,
	LPDWORD pdwOut)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%p)\n",
	      iface, pdwOut);
	return IDirectInputDevice8_GetForceFeedbackState(This->dev, pdwOut);
}

static HRESULT WINAPI DeviceThunkWImpl_SendForceFeedbackCommand(
	LPDIRECTINPUTDEVICE8W iface,
	DWORD dwFlags)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,0x%08lx)\n",
	      iface, dwFlags);
	return IDirectInputDevice8_SendForceFeedbackCommand(This->dev, dwFlags);
}

static HRESULT WINAPI DeviceThunkWImpl_EnumCreatedEffectObjects(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
	LPVOID lpvRef,
	DWORD dwFlags)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%p,%p,0x%08lx)\n",
	      iface, lpCallback, lpvRef, dwFlags);
	return IDirectInputDevice8_EnumCreatedEffectObjects(This->dev, lpCallback, lpvRef, dwFlags);
}

static HRESULT WINAPI DeviceThunkWImpl_Escape(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIEFFESCAPE lpDIEEsc)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,%p)\n",
	      iface, lpDIEEsc);
	return IDirectInputDevice8_Escape(This->dev, lpDIEEsc);
}

static HRESULT WINAPI DeviceThunkWImpl_Poll(
	LPDIRECTINPUTDEVICE8W iface)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p)\n", iface);
	return IDirectInputDevice8_Poll(This->dev);
}

static HRESULT WINAPI DeviceThunkWImpl_SendDeviceData(
	LPDIRECTINPUTDEVICE8W iface,
	DWORD cbObjectData,
	LPCDIDEVICEOBJECTDATA rgdod,
	LPDWORD pdwInOut,
	DWORD dwFlags)
{
	ICOM_THIS(DeviceThunkWImpl,iface);
	TRACE("(this=%p,0x%08lx,%p,%p,0x%08lx)\n",
	      iface, cbObjectData, rgdod, pdwInOut, dwFlags);
	return IDirectInputDevice8_SendDeviceData(This->dev, cbObjectData, rgdod, pdwInOut, dwFlags);
}

static HRESULT WINAPI DeviceThunkWImpl_EnumEffectsInFile(
	LPDIRECTINPUTDEVICE8W iface,
	LPCWSTR lpszFileName,
	LPDIENUMEFFECTSINFILECALLBACK pec,
	LPVOID pvRef,
	DWORD dwFlags)
{
	/* ICOM_THIS(DeviceThunkWImpl,iface); */
	FIXME("(%p)->(%s,%p,%p,%08lx): stub !\n",
	      iface, debugstr_w(lpszFileName), pec, pvRef, dwFlags);

	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_WriteEffectToFile(
	LPDIRECTINPUTDEVICE8W iface,
	LPCWSTR lpszFileName,
	DWORD dwEntries,
	LPDIFILEEFFECT rgDiFileEft,
	DWORD dwFlags)
{
	/* ICOM_THIS(DeviceThunkWImpl,iface); */
	FIXME("(%p)->(%s,%08lx,%p,%08lx): stub !\n",
	      iface, debugstr_w(lpszFileName), dwEntries, rgDiFileEft, dwFlags);

	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_BuildActionMap(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIACTIONFORMATW lpdiaf,
	LPCWSTR lpszUserName,
	DWORD dwFlags)
{
	/* ICOM_THIS(DeviceThunkWImpl,iface); */
	FIXME("(%p)->(%p,%s,%08lx): stub !\n",
	      iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);

	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_SetActionMap(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIACTIONFORMATW lpdiaf,
	LPCWSTR lpszUserName,
	DWORD dwFlags)
{
	/* ICOM_THIS(DeviceThunkWImpl,iface); */
	FIXME("(%p)->(%p,%s,%08lx): stub !\n",
	      iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);

	return DI_OK;
}

static HRESULT WINAPI DeviceThunkWImpl_GetImageInfo(
	LPDIRECTINPUTDEVICE8W iface,
	LPDIDEVICEIMAGEINFOHEADERW lpdiDevImageInfoHeader)
{
	/* ICOM_THIS(DeviceThunkWImpl,iface); */
	FIXME("(%p)->(%p): stub !\n",
	      iface, lpdiDevImageInfoHeader);

	return DI_OK;
}

static ICOM_VTABLE(IDirectInputDevice8W) DeviceThunkWvt =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	DeviceThunkWImpl_QueryInterface,
	DeviceThunkWImpl_AddRef,
	DeviceThunkWImpl_Release,
	DeviceThunkWImpl_GetCapabilities,
	DeviceThunkWImpl_EnumObjects,
	DeviceThunkWImpl_GetProperty,
	DeviceThunkWImpl_SetProperty,
	DeviceThunkWImpl_Acquire,
	DeviceThunkWImpl_Unacquire,
	DeviceThunkWImpl_GetDeviceState,
	DeviceThunkWImpl_GetDeviceData,
	DeviceThunkWImpl_SetDataFormat,
	DeviceThunkWImpl_SetEventNotification,
	DeviceThunkWImpl_SetCooperativeLevel,
	DeviceThunkWImpl_GetObjectInfo,
	DeviceThunkWImpl_GetDeviceInfo,
	DeviceThunkWImpl_RunControlPanel,
	DeviceThunkWImpl_Initialize,
	DeviceThunkWImpl_CreateEffect,
	DeviceThunkWImpl_EnumEffects,
	DeviceThunkWImpl_GetEffectInfo,
	DeviceThunkWImpl_GetForceFeedbackState,
	DeviceThunkWImpl_SendForceFeedbackCommand,
	DeviceThunkWImpl_EnumCreatedEffectObjects,
	DeviceThunkWImpl_Escape,
	DeviceThunkWImpl_Poll,
	DeviceThunkWImpl_SendDeviceData,
	DeviceThunkWImpl_EnumEffectsInFile,
	DeviceThunkWImpl_WriteEffectToFile,
	DeviceThunkWImpl_BuildActionMap,
	DeviceThunkWImpl_SetActionMap,
	DeviceThunkWImpl_GetImageInfo
};

HRESULT DeviceThunkA_create_device(dinput_device* device, IDirectInputImpl* dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
{
	if (device->create_deviceA)
		return device->create_deviceA(dinput, rguid, riid, pdev);
	/* ThunkA not implemented yet */
	return DIERR_DEVICENOTREG;
}

HRESULT DeviceThunkW_create_device(dinput_device* device, IDirectInputImpl* dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
{
	DeviceThunkWImpl* This;
	const IID* piid;
	LPDIRECTINPUTDEVICEA dev;
	HRESULT hr;
	if (device->create_deviceW)
		return device->create_deviceW(dinput, rguid, riid, pdev);
	if (!device->create_deviceA)
		return DIERR_DEVICENOTREG;
	piid = riid ? DeviceThunkW_MapIID(riid) : NULL;
	hr = device->create_deviceA(dinput, rguid, piid, &dev);
	if (FAILED(hr)) return hr;
	if (riid && !piid) {
		IDirectInputDevice_Release(dev);
		return DIERR_NOINTERFACE;
	}
	This = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceThunkWImpl));
	This->lpVtbl = &DeviceThunkWvt;
	This->ref = 1;
	This->dev = (IDirectInputDevice8A*)dev;
	*pdev = (IDirectInputDeviceW*)This;
	return hr;
}

HRESULT DeviceThunkA_enum_device(dinput_device* device, IDirectInputImpl* dinput, DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD dwID)
{
	if (device->enum_deviceA)
		return device->enum_deviceA(dinput, dwDevType, dwFlags, lpddi, dwID);
	/* ThunkA not implemented yet */
	return 0;
}

HRESULT DeviceThunkW_enum_device(dinput_device* device, IDirectInputImpl* dinput, DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD dwID)
{
	int r;
	DIDEVICEINSTANCEA ddi;
	if (device->enum_deviceW)
		return device->enum_deviceW(dinput, dwDevType, dwFlags, lpddi, dwID);
	if (!device->enum_deviceA)
		return 0;
	memset(&ddi, 0, sizeof(ddi));
	ddi.dwSize = sizeof(ddi);
	r = device->enum_deviceA(dinput, dwDevType, dwFlags, &ddi, dwID);
	if (r <= 0)
		return r;
	lpddi->guidInstance = ddi.guidInstance;
	lpddi->guidProduct = ddi.guidProduct;
	lpddi->dwDevType = ddi.dwDevType;
	MultiByteToWideChar(CP_ACP, 0, ddi.tszInstanceName, -1, lpddi->tszInstanceName, MAX_PATH);
	MultiByteToWideChar(CP_ACP, 0, ddi.tszProductName, -1, lpddi->tszProductName, MAX_PATH);
	lpddi->guidFFDriver = ddi.guidFFDriver;
	lpddi->wUsagePage = ddi.wUsagePage;
	lpddi->wUsage = ddi.wUsage;
	return 1;
}
