/*		DDraw Direct3D interface implementation
 *
 * Copyright 2001 TransGaming Technologies Inc.
 */

#include "config.h"
#include "winerror.h"

#include <assert.h>
#include <stdlib.h>

#include "wine/debug.h"
#include "ddraw_private.h"
#include "d3d_private.h"
#include "ddraw/main.h"
#include "d3ddevice/main.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

#define CONVERT(pd3d) COM_INTERFACE_CAST(IDirectDrawImpl,	\
					 IDirect3D7,		\
					 IDirectDraw7,		\
					 (pd3d))

void Direct3D7_MakeDeviceDesc(LPD3DDEVICEDESC7 desc,
			      LPD3DDEVICEDESC_V1 desc1,
			      LPD3DHAL_D3DEXTENDEDCAPS desc2,
			      LPGUID lpGuid)
{
    desc->dwDevCaps		  = desc1->dwDevCaps;
    desc->dpcLineCaps		  = desc1->dpcLineCaps;
    desc->dpcTriCaps		  = desc1->dpcTriCaps;
    desc->dwDeviceRenderBitDepth  = desc1->dwDeviceRenderBitDepth;
    desc->dwDeviceZBufferBitDepth = desc1->dwDeviceZBufferBitDepth;

    desc->dwMinTextureWidth	  = desc2->dwMinTextureWidth;
    desc->dwMaxTextureWidth	  = desc2->dwMaxTextureWidth;
    desc->dwMinTextureHeight	  = desc2->dwMinTextureHeight;
    desc->dwMaxTextureHeight	  = desc2->dwMaxTextureHeight;

    memcpy(&desc->dwMaxTextureRepeat, &desc2->dwMaxTextureRepeat,
	   ((LPBYTE)&desc->deviceGUID) -
	   ((LPBYTE)&desc->dwMaxTextureRepeat));
    desc->deviceGUID = *lpGuid;
    memcpy(&desc->wMaxUserClipPlanes, &desc2->wMaxUserClipPlanes,
	   sizeof(D3DDEVICEDESC7) -
	   (((LPBYTE)&desc->wMaxUserClipPlanes) -
	    ((LPBYTE)desc)));
}

static HRESULT WINAPI
Direct3D7_QueryInterface(LPDIRECT3D7 iface, REFIID riid,
			 LPVOID *ppObj)
{
    return IDirectDraw7_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3D7_AddRef(LPDIRECT3D7 iface)
{
    return IDirectDraw7_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3D7_Release(LPDIRECT3D7 iface)
{
    return IDirectDraw7_Release(CONVERT(iface));
}

struct Direct3D7_cb_info {
    LPD3DENUMDEVICESCALLBACK7 cb;
    LPVOID context;
};

static HRESULT Direct3D7_enum_cb(IDirectDrawImpl *This, LPGUID lpGuid,
				 LPSTR lpDeviceDesc, LPSTR lpDeviceName,
				 LPD3DDEVICEDESC_V1 desc1, LPD3DHAL_D3DEXTENDEDCAPS desc2,
				 LPVOID lpContext)
{
    struct Direct3D7_cb_info* info = (struct Direct3D7_cb_info*)lpContext;
    D3DDEVICEDESC7 desc;
    Direct3D7_MakeDeviceDesc(&desc, desc1, desc2, lpGuid);
    TRACE("enumerating %s/%s\n", lpDeviceName, lpDeviceDesc);
    return info->cb(lpDeviceDesc, lpDeviceName, &desc, info->context);
}

static HRESULT WINAPI
Direct3D7_EnumDevices(LPDIRECT3D7 iface, LPD3DENUMDEVICESCALLBACK7 cb, LPVOID context)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D7, iface);
    struct Direct3D7_cb_info info = { cb, context };

    TRACE("(%p)->(%p,%p)\n",iface,cb,context);

    if (This->enum_3ddevices)
	return This->enum_3ddevices(This, &info, Direct3D7_enum_cb);

    return D3D_OK;
}

static HRESULT WINAPI
Direct3D7_CreateDevice(LPDIRECT3D7 iface, REFCLSID rclsid, LPDIRECTDRAWSURFACE7 surface,
		       LPDIRECT3DDEVICE7 *device)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D7, iface);

    TRACE("(%p)->(%s,%p,%p)\n",iface,debugstr_guid(rclsid),surface,device);

    if (This->create_3ddevice)
	return This->create_3ddevice(This, rclsid, surface, device, NULL);

    return DDERR_INVALIDPARAMS;
}

static void D3D_VBD2SD(LPD3DVERTEXBUFFERDESC vbdesc, LPDDSURFACEDESC2 sdesc)
{
    memset(sdesc, 0, sizeof(DDSURFACEDESC2));
    sdesc->dwSize = sizeof(DDSURFACEDESC2);
    sdesc->dwFlags = DDSD_CAPS | DDSD_LINEARSIZE | DDSD_FVF;
    sdesc->u1.dwLinearSize = D3D_FVF2Size(vbdesc->dwFVF)
			   * vbdesc->dwNumVertices;
    sdesc->u4.dwFVF = vbdesc->dwFVF;
    sdesc->ddsCaps.dwCaps = DDSCAPS_EXECUTEBUFFER | (vbdesc->dwCaps &
			   (D3DVBCAPS_SYSTEMMEMORY | D3DVBCAPS_WRITEONLY |
			    D3DVBCAPS_OPTIMIZED | D3DVBCAPS_DONOTCLIP));
    sdesc->ddsCaps.dwCaps2 = DDSCAPS2_VERTEXBUFFER;
    TRACE("FVF=%lx, NumVertices=%ld, size=%ld, caps=%lx\n", vbdesc->dwFVF,
	  vbdesc->dwNumVertices, sdesc->u1.dwLinearSize, vbdesc->dwCaps);
}

static HRESULT WINAPI
Direct3D7_CreateVertexBuffer(LPDIRECT3D7 iface, LPD3DVERTEXBUFFERDESC lpD3DVertBufDesc,
			     LPDIRECT3DVERTEXBUFFER7* lplpD3DVertBuf,
			     DWORD dwFlags)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D7, iface);
    LPDIRECTDRAWSURFACE7 surf = (LPDIRECTDRAWSURFACE7)NULL;
    DDSURFACEDESC2 desc;
    HRESULT ret;

    TRACE("(%p)->(%p,%p,%lx)\n",iface,lpD3DVertBufDesc,lplpD3DVertBuf,dwFlags);

    D3D_VBD2SD(lpD3DVertBufDesc, &desc);
    ret = This->create_exebuffer(This, &desc, &surf, NULL);
    if (surf)
	*lplpD3DVertBuf = COM_INTERFACE_CAST(IDirectDrawSurfaceImpl,
					     IDirectDrawSurface7,
					     IDirect3DVertexBuffer7,
					     surf);
    else
	*lplpD3DVertBuf = NULL;

    return ret;
}

static HRESULT WINAPI
Direct3D7_EnumZBufferFormats(LPDIRECT3D7 iface, REFCLSID riidDevice,
			     LPD3DENUMPIXELFORMATSCALLBACK lpEnumCallback,
			     LPVOID lpContext)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D7, iface);
    LPDDRAWI_DIRECTDRAW_GBL dd_gbl = This->local.lpGbl;
    unsigned i;

    TRACE("(%p)->(%s,%p,%p)\n",iface,debugstr_guid(riidDevice),lpEnumCallback,lpContext);

    for (i=0; i<dd_gbl->dwNumZPixelFormats; i++)
	if (!lpEnumCallback(&dd_gbl->lpZPixelFormats[i], lpContext))
	    return D3D_OK;

    return D3D_OK;
}

static HRESULT WINAPI
Direct3D7_EvictManagedTextures(LPDIRECT3D7 iface)
{
    FIXME("(%p): stub\n",iface);
    return D3D_OK;
}

#undef CONVERT

ICOM_VTABLE(IDirect3D7) DDRAW_IDD3D7_VTable =
{
    Direct3D7_QueryInterface,
    Direct3D7_AddRef,
    Direct3D7_Release,
    Direct3D7_EnumDevices,
    Direct3D7_CreateDevice,
    Direct3D7_CreateVertexBuffer,
    Direct3D7_EnumZBufferFormats,
    Direct3D7_EvictManagedTextures
};

#define CONVERT(pd3d) COM_INTERFACE_CAST(IDirectDrawImpl,	\
					 IDirect3D3,		\
					 IDirectDraw7,		\
					 (pd3d))
#define CONVERT7(pd3d) COM_INTERFACE_CAST(IDirectDrawImpl,	\
					  IDirect3D3,		\
					  IDirect3D7,		\
					  (pd3d))

void Direct3D3_MakeDeviceDesc(LPD3DDEVICEDESC desc,
			      LPD3DDEVICEDESC_V1 desc1,
			      LPD3DHAL_D3DEXTENDEDCAPS desc2)
{
    memcpy(desc, desc1, sizeof(D3DDEVICEDESC_V1));

    desc->dwMinTextureWidth	  = desc2->dwMinTextureWidth;
    desc->dwMaxTextureWidth	  = desc2->dwMaxTextureWidth;
    desc->dwMinTextureHeight	  = desc2->dwMinTextureHeight;
    desc->dwMaxTextureHeight	  = desc2->dwMaxTextureHeight;

    memcpy(&desc->dwMinStippleWidth, &desc2->dwMinStippleWidth,
	   sizeof(D3DDEVICEDESC) -
	   (((LPBYTE)&desc->dwMinStippleWidth) -
	    ((LPBYTE)desc)));

    desc->dwSize = sizeof(D3DDEVICEDESC);
}

static HRESULT WINAPI
Direct3D3_QueryInterface(LPDIRECT3D3 iface, REFIID riid,
			 LPVOID *ppObj)
{
    return IDirectDraw7_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3D3_AddRef(LPDIRECT3D3 iface)
{
    return IDirectDraw7_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3D3_Release(LPDIRECT3D3 iface)
{
    return IDirectDraw7_Release(CONVERT(iface));
}

struct Direct3D3_cb_info {
    LPD3DENUMDEVICESCALLBACK cb;
    LPVOID context;
};

static HRESULT Direct3D3_enum_cb(IDirectDrawImpl *This, LPGUID lpGuid,
				 LPSTR lpDeviceDesc, LPSTR lpDeviceName,
				 LPD3DDEVICEDESC_V1 desc1, LPD3DHAL_D3DEXTENDEDCAPS desc2,
				 LPVOID lpContext)
{
    struct Direct3D3_cb_info* info = (struct Direct3D3_cb_info*)lpContext;
    D3DDEVICEDESC desc;
    Direct3D3_MakeDeviceDesc(&desc, desc1, desc2);
    TRACE("enumerating %s/%s\n", lpDeviceName, lpDeviceDesc);
    return info->cb(lpGuid, lpDeviceDesc, lpDeviceName, &desc, &desc, info->context);
}

static HRESULT WINAPI
Direct3D3_EnumDevices(LPDIRECT3D3 iface, LPD3DENUMDEVICESCALLBACK cb, LPVOID context)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface);
    struct Direct3D3_cb_info info = { cb, context };

    TRACE("(%p)->(%p,%p)\n",iface,cb,context);

    if (This->enum_3ddevices)
	return This->enum_3ddevices(This, &info, Direct3D3_enum_cb);

    return D3D_OK;
}

static HRESULT WINAPI
Direct3D3_CreateLight(LPDIRECT3D3 iface, LPDIRECT3DLIGHT *lplpLight, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface);

    TRACE("(%p)->(%p,%p)\n",iface,lplpLight,lpUnkOuter);

    return Direct3DLight_Create(This, lplpLight);
}

static HRESULT WINAPI
Direct3D3_CreateMaterial(LPDIRECT3D3 iface, LPDIRECT3DMATERIAL3 *lplpMat, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface);

    TRACE("(%p)->(%p,%p)\n",iface,lplpMat,lpUnkOuter);

    return Direct3DMaterial3_Create(This, lplpMat);
}

static HRESULT WINAPI
Direct3D3_CreateViewport(LPDIRECT3D3 iface, LPDIRECT3DVIEWPORT3 *lplpView, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface);

    TRACE("(%p)->(%p,%p)\n",iface,lplpView,lpUnkOuter);

    return Direct3DViewport3_Create(This, lplpView);
}

static HRESULT WINAPI
Direct3D3_FindDevice(LPDIRECT3D3 iface, LPD3DFINDDEVICESEARCH lpSearch,
		     LPD3DFINDDEVICERESULT lpResult)
{
    /* ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface); */

    FIXME("(%p)->(%p,%p): stub\n",iface,lpSearch,lpResult);
    return D3D_OK;
}


static HRESULT WINAPI
Direct3D3_CreateDevice(LPDIRECT3D3 iface, REFCLSID rclsid, LPDIRECTDRAWSURFACE4 surface,
		       LPDIRECT3DDEVICE3 *device, LPUNKNOWN lpUnk)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface);
    /* there's no IDirectDrawSurface4 vtable */
    LPDIRECTDRAWSURFACE7 surf7 = (LPDIRECTDRAWSURFACE7)surface;
    LPDIRECT3DDEVICE7 dev7 = (LPDIRECT3DDEVICE7)NULL;
    HRESULT ret = DDERR_INVALIDPARAMS;

    TRACE("(%p)->(%s,%p,%p,%p)\n",iface,debugstr_guid(rclsid),surface,device,lpUnk);

    if (This->create_3ddevice)
	ret = This->create_3ddevice(This, rclsid, surf7, &dev7, lpUnk);

    if (dev7)
	*device = COM_INTERFACE_CAST(IDirect3DDeviceImpl,
				     IDirect3DDevice7,
				     IDirect3DDevice3,
				     dev7);
    else
	*device = NULL;

    return ret;
}

static HRESULT WINAPI
Direct3D3_CreateVertexBuffer(LPDIRECT3D3 iface, LPD3DVERTEXBUFFERDESC lpD3DVertBufDesc,
			     LPDIRECT3DVERTEXBUFFER* lplpD3DVertBuf,
			     DWORD dwFlags, LPUNKNOWN lpUnk)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface);
    LPDIRECTDRAWSURFACE7 surf = (LPDIRECTDRAWSURFACE7)NULL;
    DDSURFACEDESC2 desc;
    HRESULT ret;

    TRACE("(%p)->(%p,%p,%lx,%p)\n",iface,lpD3DVertBufDesc,lplpD3DVertBuf,dwFlags,lpUnk);

    D3D_VBD2SD(lpD3DVertBufDesc, &desc);
    if (dwFlags & D3DDP_DONOTCLIP) desc.ddsCaps.dwCaps |= D3DVBCAPS_DONOTCLIP;
    ret = This->create_exebuffer(This, &desc, &surf, lpUnk);
    if (surf)
	*lplpD3DVertBuf = COM_INTERFACE_CAST(IDirectDrawSurfaceImpl,
					     IDirectDrawSurface7,
					     IDirect3DVertexBuffer,
					     surf);
    else
	*lplpD3DVertBuf = NULL;

    return ret;
}

static HRESULT WINAPI
Direct3D3_EnumZBufferFormats(LPDIRECT3D3 iface, REFCLSID riidDevice,
			     LPD3DENUMPIXELFORMATSCALLBACK lpEnumCallback,
			     LPVOID lpContext)
{
    return IDirect3D7_EnumZBufferFormats(CONVERT7(iface), riidDevice, lpEnumCallback, lpContext);
}

static HRESULT WINAPI
Direct3D3_EvictManagedTextures(LPDIRECT3D3 iface)
{
    FIXME("(%p): stub\n",iface);
    return D3D_OK;
}

#undef CONVERT7
#undef CONVERT

ICOM_VTABLE(IDirect3D3) DDRAW_IDD3D3_VTable =
{
    Direct3D3_QueryInterface,
    Direct3D3_AddRef,
    Direct3D3_Release,
    Direct3D3_EnumDevices,
    Direct3D3_CreateLight,
    Direct3D3_CreateMaterial,
    Direct3D3_CreateViewport,
    Direct3D3_FindDevice,
    Direct3D3_CreateDevice,
    Direct3D3_CreateVertexBuffer,
    Direct3D3_EnumZBufferFormats,
    Direct3D3_EvictManagedTextures
};

#define CONVERT(pd3d) COM_INTERFACE_CAST(IDirectDrawImpl,	\
					 IDirect3D2,		\
					 IDirectDraw7,		\
					  (pd3d))
#define CONVERT3(pd3d) COM_INTERFACE_CAST(IDirectDrawImpl,	\
					  IDirect3D2,		\
					  IDirect3D3,		\
					  (pd3d))

static HRESULT WINAPI
Direct3D2_QueryInterface(LPDIRECT3D2 iface, REFIID riid,
			 LPVOID *ppObj)
{
    return IDirectDraw7_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3D2_AddRef(LPDIRECT3D2 iface)
{
    return IDirectDraw7_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3D2_Release(LPDIRECT3D2 iface)
{
    return IDirectDraw7_Release(CONVERT(iface));
}

static HRESULT WINAPI
Direct3D2_EnumDevices(LPDIRECT3D2 iface, LPD3DENUMDEVICESCALLBACK cb, LPVOID context)
{
    return IDirect3D3_EnumDevices(CONVERT3(iface), cb, context);
}

static HRESULT WINAPI
Direct3D2_CreateLight(LPDIRECT3D2 iface, LPDIRECT3DLIGHT *lplpLight, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D2, iface);

    TRACE("(%p)->(%p,%p)\n",iface,lplpLight,lpUnkOuter);

    return Direct3DLight_Create(This, lplpLight);
}

static HRESULT WINAPI
Direct3D2_CreateMaterial(LPDIRECT3D2 iface, LPDIRECT3DMATERIAL2 *lplpMat, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D2, iface);
    LPDIRECT3DMATERIAL3 mat3 = (LPDIRECT3DMATERIAL3)NULL;
    HRESULT ret;

    TRACE("(%p)->(%p,%p)\n",iface,lplpMat,lpUnkOuter);

    ret = Direct3DMaterial3_Create(This, &mat3);
    if (mat3)
	*lplpMat = COM_INTERFACE_CAST(IDirect3DMaterialImpl,
				      IDirect3DMaterial3,
				      IDirect3DMaterial2,
				      mat3);
    else
	*lplpMat = NULL;

    return ret;
}

static HRESULT WINAPI
Direct3D2_CreateViewport(LPDIRECT3D2 iface, LPDIRECT3DVIEWPORT2 *lplpView, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D2, iface);

    TRACE("(%p)->(%p,%p)\n",iface,lplpView,lpUnkOuter);

    /* IDirect3DViewportImpl has only one vtable */
    return Direct3DViewport3_Create(This, (LPDIRECT3DVIEWPORT3*)lplpView);
}

static HRESULT WINAPI
Direct3D2_FindDevice(LPDIRECT3D2 iface, LPD3DFINDDEVICESEARCH lpSearch,
		     LPD3DFINDDEVICERESULT lpResult)
{
    /* ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D2, iface); */

    FIXME("(%p)->(%p,%p): stub\n",iface,lpSearch,lpResult);
    return D3D_OK;
}

static HRESULT WINAPI
Direct3D2_CreateDevice(LPDIRECT3D2 iface, REFCLSID rclsid, LPDIRECTDRAWSURFACE surface,
		       LPDIRECT3DDEVICE2 *device)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D2, iface);
    LPDIRECTDRAWSURFACE7 surf7 = COM_INTERFACE_CAST(IDirectDrawSurfaceImpl,
						    IDirectDrawSurface3,
						    IDirectDrawSurface7,
						    surface);
    LPDIRECT3DDEVICE7 dev7 = (LPDIRECT3DDEVICE7)NULL;
    HRESULT ret = DDERR_INVALIDPARAMS;

    TRACE("(%p)->(%s,%p,%p)\n",iface,debugstr_guid(rclsid),surface,device);

    if (This->create_3ddevice)
	ret = This->create_3ddevice(This, rclsid, surf7, &dev7, NULL);

    if (dev7)
	*device = COM_INTERFACE_CAST(IDirect3DDeviceImpl,
				     IDirect3DDevice7,
				     IDirect3DDevice2,
				     dev7);
    else
	*device = NULL;

    return ret;
}

#undef CONVERT3
#undef CONVERT

ICOM_VTABLE(IDirect3D2) DDRAW_IDD3D2_VTable =
{
    Direct3D2_QueryInterface,
    Direct3D2_AddRef,
    Direct3D2_Release,
    Direct3D2_EnumDevices,
    Direct3D2_CreateLight,
    Direct3D2_CreateMaterial,
    Direct3D2_CreateViewport,
    Direct3D2_FindDevice,
    Direct3D2_CreateDevice
};

#define CONVERT(pd3d) COM_INTERFACE_CAST(IDirectDrawImpl,	\
					 IDirect3D,		\
					 IDirectDraw7,		\
					 (pd3d))
#define CONVERT3(pd3d) COM_INTERFACE_CAST(IDirectDrawImpl,	\
					  IDirect3D,		\
					  IDirect3D3,		\
					  (pd3d))

static HRESULT WINAPI
Direct3D_QueryInterface(LPDIRECT3D iface, REFIID riid,
			LPVOID *ppObj)
{
    return IDirectDraw7_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3D_AddRef(LPDIRECT3D iface)
{
    return IDirectDraw7_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3D_Release(LPDIRECT3D iface)
{
    return IDirectDraw7_Release(CONVERT(iface));
}

static HRESULT WINAPI
Direct3D_Initialize(LPDIRECT3D iface, REFIID riid)
{
    TRACE("(%p)->(%s)\n",iface,debugstr_guid(riid));
    return DDERR_ALREADYINITIALIZED;
}

static HRESULT WINAPI
Direct3D_EnumDevices(LPDIRECT3D iface, LPD3DENUMDEVICESCALLBACK cb, LPVOID context)
{
    return IDirect3D3_EnumDevices(CONVERT3(iface), cb, context);
}

static HRESULT WINAPI
Direct3D_CreateLight(LPDIRECT3D iface, LPDIRECT3DLIGHT *lplpLight, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D, iface);

    TRACE("(%p)->(%p,%p)\n",iface,lplpLight,lpUnkOuter);

    return Direct3DLight_Create(This, lplpLight);
}

static HRESULT WINAPI
Direct3D_CreateMaterial(LPDIRECT3D iface, LPDIRECT3DMATERIAL *lplpMat, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D, iface);
    LPDIRECT3DMATERIAL3 mat3 = (LPDIRECT3DMATERIAL3)NULL;
    HRESULT ret;

    TRACE("(%p)->(%p,%p)\n",iface,lplpMat,lpUnkOuter);

    ret = Direct3DMaterial3_Create(This, &mat3);
    if (mat3)
	*lplpMat = COM_INTERFACE_CAST(IDirect3DMaterialImpl,
				      IDirect3DMaterial3,
				      IDirect3DMaterial,
				      mat3);
    else
	*lplpMat = NULL;

    return ret;
}

static HRESULT WINAPI
Direct3D_CreateViewport(LPDIRECT3D iface, LPDIRECT3DVIEWPORT *lplpView, IUnknown *lpUnkOuter)
{
    ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D, iface);

    TRACE("(%p)->(%p,%p)\n",iface,lplpView,lpUnkOuter);

    /* IDirect3DViewportImpl has only one vtable */
    return Direct3DViewport3_Create(This, (LPDIRECT3DVIEWPORT3*)lplpView);
}

static HRESULT WINAPI
Direct3D_FindDevice(LPDIRECT3D iface, LPD3DFINDDEVICESEARCH lpSearch,
		    LPD3DFINDDEVICERESULT lpResult)
{
    /* ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D, iface); */

    FIXME("(%p)->(%p,%p): stub\n",iface,lpSearch,lpResult);
    return D3D_OK;
}

#undef CONVERT3
#undef CONVERT

ICOM_VTABLE(IDirect3D) DDRAW_IDD3D_VTable =
{
    Direct3D_QueryInterface,
    Direct3D_AddRef,
    Direct3D_Release,
    Direct3D_Initialize,
    Direct3D_EnumDevices,
    Direct3D_CreateLight,
    Direct3D_CreateMaterial,
    Direct3D_CreateViewport,
    Direct3D_FindDevice
};
