/*		DirectDraw/Direct3D ExeBuffer stand in
 *
 * Copyright 2001 TransGaming Technologies Inc.
 *
 * This class provides a DirectDrawSurface implementation that represents
 * a vertex buffer. This surface is not supposed to be accessed directly
 * by the application, only through the IDirect3DVertexBuffer7 interface, so
 * we don't need to do much here.
 */

#include "config.h"

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

#include "ddraw.h"
#include "d3d.h"

#include "wine/debug.h"

#include "ddcomimpl.h"
#include "ddraw_private.h"
#include "ddraw/user.h"
#include "ddraw/hal.h"
#include "dsurface/main.h"
#include "dsurface/haleb.h"
#include "d3ddevice/main.h"
#include "d3ddevice/maths.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

static void D3D_SD2VBD( LPDDSURFACEDESC2 sdesc, LPD3DVERTEXBUFFERDESC vbdesc)
{
    memset(vbdesc, 0, sizeof(D3DVERTEXBUFFERDESC));
    vbdesc->dwFVF = sdesc->u4.dwFVF;
    vbdesc->dwNumVertices = sdesc->u1.dwLinearSize / D3D_FVF2Size(vbdesc->dwFVF);
    vbdesc->dwCaps = sdesc->ddsCaps.dwCaps & ~DDSCAPS_EXECUTEBUFFER;
    TRACE("FVF=%lx, NumVertices=%ld, size=%ld, caps=%lx\n", vbdesc->dwFVF,
          vbdesc->dwNumVertices, sdesc->u1.dwLinearSize, vbdesc->dwCaps);
}

static ICOM_VTABLE(IDirectDrawSurface7) HALEB_IDirectDrawSurface7_VTable;
static ICOM_VTABLE(IDirect3DVertexBuffer7) DDRAW_ID3DVB7_VTable;
static ICOM_VTABLE(IDirect3DVertexBuffer) DDRAW_ID3DVB_VTable;
static ICOM_VTABLE(IDirect3DExecuteBuffer) DDRAW_ID3DEB_VTable;

static HRESULT
HALEB_DirectDrawSurface_create_surface(IDirectDrawSurfaceImpl* This,
				       IDirectDrawImpl* pDD)
{
    HALEB_PRIV_VAR(priv, This);
    LPDDRAWI_DIRECTDRAW_GBL dd_gbl = pDD->local.lpGbl;
    LPDDRAWI_DDRAWSURFACE_LCL local = &This->t.lcl;
    DDHAL_CREATESURFACEDATA data;
    HRESULT hr;
    DWORD alloc_size;

    /* DX8 DDK: "DirectDraw passes the linear buffer size to the driver in
     * the lPitch member of [DD_SURFACE_GLOBAL]."
     * Actually since lPitch and dwLinearSize are in the same union, it makes
     * more sense to say it is passed in dwLinearSize.
     * (Already done by Main_DirectDrawSurface_Construct.)
     *
     * "On Windows 2000 and later versions, but not on Windows 98/Me, the
     * wWidth member of this structure is set to the same value."
     */
    This->t.gbl.wWidth = This->surface_desc.u1.dwLinearSize;
    This->t.more.dwFVF = This->surface_desc.u4.dwFVF;

    data.lpDD = dd_gbl;
    data.lpDDSurfaceDesc = (LPDDSURFACEDESC)&This->surface_desc;
    data.lplpSList = &local;
    data.dwSCnt = 1;
    data.ddRVal = 0;
    data.CreateSurface = dd_gbl->lpDDCBtmp->HALDDExeBuf.CreateExecuteBuffer;
    hr = data.CreateSurface(&data);

    if (hr == DDHAL_DRIVER_HANDLED)
    {
	if (FAILED(data.ddRVal)) return data.ddRVal;

	switch (This->t.gbl.fpVidMem)
	{
	case DDHAL_PLEASEALLOC_BLOCKSIZE:
	    FIXME("allocating from usermemory instead of blocksize\n");
	    alloc_size = This->t.gbl.u1.dwBlockSizeY
		* This->t.gbl.u2.dwBlockSizeX;
	    break;

	case DDHAL_PLEASEALLOC_USERMEM:
	    alloc_size = This->t.gbl.u2.dwBlockSizeX;
	    break;

	default:
	    alloc_size = 0;
	    break;
	}
    }
    else
    {
	alloc_size = This->surface_desc.u1.dwLinearSize;
    }

    if (alloc_size > 0)
    {
	This->t.gbl.fpVidMem = (DWORD)HeapAlloc(GetProcessHeap(),
						 HEAP_ZERO_MEMORY,
						 alloc_size);

	if (This->t.gbl.fpVidMem == (DWORD)NULL)
	    return E_OUTOFMEMORY;

	/* we no longer need the lpSurface, set it to NULL */
	This->surface_desc.lpSurface = NULL;

	priv->haleb.hal_allocated = TRUE;
    }

    return DD_OK;

}

HRESULT HALEB_DirectDrawSurface_Construct(IDirectDrawSurfaceImpl *This,
					  IDirectDrawImpl *pDD,
					  const DDSURFACEDESC2 *pDDSD)
{
    HRESULT hr;

    TRACE("(%p,%p,%p)\n",This,pDD,pDDSD);

    assert(pDDSD->ddsCaps.dwCaps & DDSCAPS_EXECUTEBUFFER);

    hr = Main_DirectDrawSurface_Construct(This, pDD, pDDSD);
    if (FAILED(hr)) return hr;

    ICOM_INIT_INTERFACE(This, IDirectDrawSurface7,
			HALEB_IDirectDrawSurface7_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DVertexBuffer7,
			DDRAW_ID3DVB7_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DVertexBuffer,
			DDRAW_ID3DVB_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DExecuteBuffer,
			DDRAW_ID3DEB_VTable);

    This->final_release = HALEB_DirectDrawSurface_final_release;
    This->duplicate_surface = HALEB_DirectDrawSurface_duplicate_surface;

    hr = HALEB_DirectDrawSurface_create_surface(This, pDD);

    if (FAILED(hr))
	Main_DirectDrawSurface_final_release(This);

    return hr;
}

/* Not an API */
HRESULT HALEB_DirectDrawSurface_Create(IDirectDrawImpl* pDD,
				       const DDSURFACEDESC2* pDDSD,
				       LPDIRECTDRAWSURFACE7* ppSurf,
				       IUnknown* pUnkOuter)
{
    IDirectDrawSurfaceImpl* This;
    HRESULT hr;
    assert(pUnkOuter == NULL);

    This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
		     sizeof(*This) + sizeof(HALEB_DirectDrawSurfaceImpl));
    if (This == NULL) return E_OUTOFMEMORY;

    This->private = (HALEB_DirectDrawSurfaceImpl*)(This+1);

    hr = HALEB_DirectDrawSurface_Construct(This, pDD, pDDSD);
    if (FAILED(hr))
	HeapFree(GetProcessHeap(), 0, This);
    else
	*ppSurf = ICOM_INTERFACE(This, IDirectDrawSurface7);

    return hr;
}

void
HALEB_DirectDrawSurface_final_release(IDirectDrawSurfaceImpl* This)
{
    HALEB_PRIV_VAR(priv, This);

    if (priv->haleb.hal_allocated)
    {
	HeapFree(GetProcessHeap(), 0, (LPVOID)This->t.gbl.fpVidMem);
    }
    else
    {
	LPDDRAWI_DIRECTDRAW_GBL dd_gbl = This->t.more.lpDD_lcl->lpGbl;
	DDHAL_DESTROYSURFACEDATA data;

	/* destroy HAL surface */
	data.lpDD = dd_gbl;
	data.lpDDSurface = &This->t.lcl;
	data.ddRVal = 0;
	data.DestroySurface
	    = dd_gbl->lpDDCBtmp->HALDDExeBuf.DestroyExecuteBuffer;
	data.DestroySurface(&data);
    }

    Main_DirectDrawSurface_final_release(This);
}

HRESULT
HALEB_DirectDrawSurface_duplicate_surface(IDirectDrawSurfaceImpl* This,
					  LPDIRECTDRAWSURFACE7* ppDup)
{
    return HALEB_DirectDrawSurface_Create(This->ddraw_owner,
					  &This->surface_desc, ppDup,
					  NULL);
}

HRESULT WINAPI
HALEB_DirectDrawSurface_QueryInterface(LPDIRECTDRAWSURFACE7 iface,
				       const IID* riid, LPVOID* ppvObj)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    if (SUCCEEDED(Main_DirectDrawSurface_QueryInterface(iface, riid, ppvObj)))
	return S_OK;

    if (IsEqualGUID(&IID_IDirect3DVertexBuffer7, riid))
    {
	This->ref++;
	*ppvObj = ICOM_INTERFACE(This, IDirect3DVertexBuffer7);
	TRACE("%p\n", *ppvObj);
	return S_OK;
    }

    return E_NOINTERFACE;
}

HRESULT WINAPI
HALEB_DirectDrawSurface_Lock(LPDIRECTDRAWSURFACE7 iface, LPRECT prect,
			     LPDDSURFACEDESC2 pDDSD, DWORD flags, HANDLE h)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    DDHAL_LOCKDATA data;

    TRACE("(%p)->Lock(%p,%p,%08lx,%08lx)\n",This,prect,pDDSD,flags,(DWORD)h);

    /* First, copy the Surface description */
    DD_STRUCT_COPY_BYSIZE(pDDSD,&(This->surface_desc));

    data.lpDD = This->ddraw_owner->local.lpGbl;
    data.lpDDSurface = &This->t.lcl;
    data.bHasRect = FALSE;
    data.lpSurfData = NULL;
    data.ddRVal = DD_OK;
    data.Lock = NULL;
    data.dwFlags = flags;

    if (This->ddraw_owner->local.lpGbl->lpDDCBtmp->HALDDExeBuf.LockExecuteBuffer(&data) == DDHAL_DRIVER_HANDLED)
    {
	if (FAILED(data.ddRVal))
	    return data.ddRVal;

	pDDSD->lpSurface = data.lpSurfData;
    }
    else
    {
	pDDSD->lpSurface = (LPVOID)This->t.gbl.fpVidMem;
    }

    TRACE("locked at %p\n", pDDSD->lpSurface);

    This->lock_update(This, NULL, flags);

    return DD_OK;
}

HRESULT WINAPI
HALEB_DirectDrawSurface_Unlock(LPDIRECTDRAWSURFACE7 iface, LPRECT pRect)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    DDHAL_UNLOCKDATA data;

    TRACE("(%p)->Unlock(%p)\n",This,pRect);

    data.lpDD = This->ddraw_owner->local.lpGbl;
    data.lpDDSurface = &This->t.lcl;
    data.ddRVal = DD_OK;
    data.Unlock = NULL;

    if (This->ddraw_owner->local.lpGbl->lpDDCBtmp->HALDDExeBuf.UnlockExecuteBuffer(&data) == DDHAL_DRIVER_HANDLED)
    {
	if (FAILED(data.ddRVal))
	    return data.ddRVal;
    }

    This->unlock_update(This, NULL);

    return DD_OK;
}

/* put your breakpoint/abort call here */
static HRESULT cant_do_that(const char *s)
{
    FIXME("attempt to %s execute buffer\n", s);
    return DDERR_UNSUPPORTED;
}

HRESULT WINAPI
HALEB_DirectDrawSurface_Blt(LPDIRECTDRAWSURFACE7 iface, LPRECT rdst,
			    LPDIRECTDRAWSURFACE7 src, LPRECT rsrc,
			    DWORD dwFlags, LPDDBLTFX lpbltfx)
{
    return cant_do_that("blt to a");
}

HRESULT WINAPI
HALEB_DirectDrawSurface_BltFast(LPDIRECTDRAWSURFACE7 iface, DWORD dstx,
				DWORD dsty, LPDIRECTDRAWSURFACE7 src,
				LPRECT rsrc, DWORD trans)
{
    return cant_do_that("bltfast to a");
}

HRESULT WINAPI
HALEB_DirectDrawSurface_GetDC(LPDIRECTDRAWSURFACE7 iface, HDC *phDC)
{
    return cant_do_that("get a DC for a");
}

HRESULT WINAPI
HALEB_DirectDrawSurface_ReleaseDC(LPDIRECTDRAWSURFACE7 iface, HDC hDC)
{
    return cant_do_that("release a DC for a");
}

HRESULT WINAPI
HALEB_DirectDrawSurface_Restore(LPDIRECTDRAWSURFACE7 iface)
{
    return DD_OK;
}

HRESULT WINAPI
HALEB_DirectDrawSurface_SetSurfaceDesc(LPDIRECTDRAWSURFACE7 iface,
				       LPDDSURFACEDESC2 pDDSD,
				       DWORD dwFlags)
{
    /* XXX */
    abort();
    return E_FAIL;
}


static ICOM_VTABLE(IDirectDrawSurface7) HALEB_IDirectDrawSurface7_VTable=
{
    HALEB_DirectDrawSurface_QueryInterface,
    Main_DirectDrawSurface_AddRef,
    Main_DirectDrawSurface_Release,
    Main_DirectDrawSurface_AddAttachedSurface,
    Main_DirectDrawSurface_AddOverlayDirtyRect,
    HALEB_DirectDrawSurface_Blt,
    Main_DirectDrawSurface_BltBatch,
    HALEB_DirectDrawSurface_BltFast,
    Main_DirectDrawSurface_DeleteAttachedSurface,
    Main_DirectDrawSurface_EnumAttachedSurfaces,
    Main_DirectDrawSurface_EnumOverlayZOrders,
    Main_DirectDrawSurface_Flip,
    Main_DirectDrawSurface_GetAttachedSurface,
    Main_DirectDrawSurface_GetBltStatus,
    Main_DirectDrawSurface_GetCaps,
    Main_DirectDrawSurface_GetClipper,
    Main_DirectDrawSurface_GetColorKey,
    HALEB_DirectDrawSurface_GetDC,
    Main_DirectDrawSurface_GetFlipStatus,
    Main_DirectDrawSurface_GetOverlayPosition,
    Main_DirectDrawSurface_GetPalette,
    Main_DirectDrawSurface_GetPixelFormat,
    Main_DirectDrawSurface_GetSurfaceDesc,
    Main_DirectDrawSurface_Initialize,
    Main_DirectDrawSurface_IsLost,
    HALEB_DirectDrawSurface_Lock,
    HALEB_DirectDrawSurface_ReleaseDC,
    HALEB_DirectDrawSurface_Restore,
    Main_DirectDrawSurface_SetClipper,
    Main_DirectDrawSurface_SetColorKey,
    Main_DirectDrawSurface_SetOverlayPosition,
    Main_DirectDrawSurface_SetPalette,
    HALEB_DirectDrawSurface_Unlock,
    Main_DirectDrawSurface_UpdateOverlay,
    Main_DirectDrawSurface_UpdateOverlayDisplay,
    Main_DirectDrawSurface_UpdateOverlayZOrder,
    Main_DirectDrawSurface_GetDDInterface,
    Main_DirectDrawSurface_PageLock,
    Main_DirectDrawSurface_PageUnlock,
    HALEB_DirectDrawSurface_SetSurfaceDesc,
    Main_DirectDrawSurface_SetPrivateData,
    Main_DirectDrawSurface_GetPrivateData,
    Main_DirectDrawSurface_FreePrivateData,
    Main_DirectDrawSurface_GetUniquenessValue,
    Main_DirectDrawSurface_ChangeUniquenessValue,
    Main_DirectDrawSurface_SetPriority,
    Main_DirectDrawSurface_GetPriority,
    Main_DirectDrawSurface_SetLOD,
    Main_DirectDrawSurface_GetLOD
};

#define CONVERT(pd3dvb) COM_INTERFACE_CAST(IDirectDrawSurfaceImpl,	\
					   IDirect3DVertexBuffer7,	\
					   IDirectDrawSurface7,		\
					   (pd3dvb))

static HRESULT WINAPI
Direct3DVertexBuffer7_QueryInterface(LPDIRECT3DVERTEXBUFFER7 iface, REFIID riid,
				     LPVOID *ppObj)
{
    return IDirectDrawSurface7_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3DVertexBuffer7_AddRef(LPDIRECT3DVERTEXBUFFER7 iface)
{
    return IDirectDrawSurface7_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3DVertexBuffer7_Release(LPDIRECT3DVERTEXBUFFER7 iface)
{
    return IDirectDrawSurface7_Release(CONVERT(iface));
}

static HRESULT WINAPI
Direct3DVertexBuffer7_Lock(LPDIRECT3DVERTEXBUFFER7 iface, DWORD dwFlags,
			   LPVOID* ppData, LPDWORD pdwSize)
{
    DDSURFACEDESC2 ddsd;
    HRESULT hr;

    TRACE("(%p)->(0x%lx,%p,%p)\n", iface, dwFlags, ppData, pdwSize);

    ddsd.dwSize = sizeof(ddsd);
    hr = IDirectDrawSurface7_Lock(CONVERT(iface), NULL, &ddsd, dwFlags, (HANDLE)0);

    if (FAILED(hr)) return hr;

    *ppData = ddsd.lpSurface;
    if (pdwSize) *pdwSize = ddsd.u1.dwLinearSize;

    return D3D_OK;
}

static HRESULT WINAPI
Direct3DVertexBuffer7_Unlock(LPDIRECT3DVERTEXBUFFER7 iface)
{
    HRESULT hr;

    TRACE("(%p)\n", iface);

    hr = IDirectDrawSurface7_Unlock(CONVERT(iface), NULL);

    return hr;
}

static HRESULT WINAPI ProcessVerticesStrided(LPDIRECT3DVERTEXBUFFER7 iface,
                   DWORD dwVertexOp, DWORD dwDestIndex, DWORD dwCount,
                   LPD3DDRAWPRIMITIVESTRIDEDDATA lpSrc, DWORD dwSrcFVF,
                   DWORD dwSrcIndex, LPDIRECT3DDEVICE7 lpD3DDevice,
                   DWORD dwFlags)
{
    ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirect3DVertexBuffer7, iface);
    D3DDRAWPRIMITIVESTRIDEDDATA ddata;
    LPVOID dptr;
    DWORD dsize, dmax;
    HRESULT hr = D3D_OK;

    hr = IDirect3DVertexBuffer7_Lock(iface, DDLOCK_WRITEONLY | DDLOCK_WAIT, &dptr, &dsize);
    if (FAILED(hr)) return hr;
    TRACE("dest FVF=%08lx ptr=%p\n", This->surface_desc.u4.dwFVF, dptr);
    dmax = dsize / D3D_FVF2Stride(This->surface_desc.u4.dwFVF, dptr, &ddata);
    if (dmax < dwDestIndex) dwCount = 0;
    else if (dmax - dwDestIndex < dwCount) dwCount = dmax - dwDestIndex;

    if (dwCount)
        hr = ProcessStridedVertices(&ddata, This->surface_desc.u4.dwFVF, dwVertexOp, dwDestIndex,
                                    dwCount, lpSrc, dwSrcFVF, dwSrcIndex, lpD3DDevice, dwFlags,
                                    /* FIXME */ NULL, NULL, NULL);

    IDirect3DVertexBuffer7_Unlock(iface);
    return hr;
}

static HRESULT WINAPI
Direct3DVertexBuffer7_ProcessVertices(LPDIRECT3DVERTEXBUFFER7 iface,
                   DWORD dwVertexOp, DWORD dwDestIndex, DWORD dwCount,
                   LPDIRECT3DVERTEXBUFFER7 lpSrcBuffer,
                   DWORD dwSrcIndex, LPDIRECT3DDEVICE7 lpD3DDevice,
                   DWORD dwFlags)
{
    ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirect3DVertexBuffer7, iface);
    D3DVERTEXBUFFERDESC sdesc;
    D3DDRAWPRIMITIVESTRIDEDDATA sdata;
    LPVOID sptr;
    DWORD ssize, smax;
    HRESULT hr;

    TRACE("(%p)->(%08lx, %08lx, %08lx, %p, %08lx, %p, %08lx)\n",
          This, dwVertexOp, dwDestIndex, dwCount, lpSrcBuffer,
          dwSrcIndex, lpD3DDevice, dwFlags);
    IDirect3DVertexBuffer7_GetVertexBufferDesc(lpSrcBuffer, &sdesc);
    hr = IDirect3DVertexBuffer7_Lock(lpSrcBuffer, DDLOCK_READONLY | DDLOCK_WAIT, &sptr, &ssize);
    if (FAILED(hr)) return hr;
    TRACE("source FVF=%08lx ptr=%p\n", sdesc.dwFVF, sptr);
    smax = ssize / D3D_FVF2Stride(sdesc.dwFVF, sptr, &sdata);
    if (smax < dwSrcIndex) dwCount = 0;
    else if (smax - dwSrcIndex < dwCount) dwCount = smax - dwSrcIndex;
    hr = ProcessVerticesStrided(iface, dwVertexOp, dwDestIndex, dwCount, &sdata,
                                sdesc.dwFVF, dwSrcIndex, lpD3DDevice, dwFlags);
    IDirect3DVertexBuffer7_Unlock(lpSrcBuffer);
    return hr;
}

static HRESULT WINAPI
Direct3DVertexBuffer7_Optimize(LPDIRECT3DVERTEXBUFFER7 iface,
			       LPDIRECT3DDEVICE7 lpD3DDevice,
			       DWORD dwFlags)
{
    TRACE("(%p)->(%p,%lx)\n", iface, lpD3DDevice, dwFlags);
    return D3D_OK;
}


static HRESULT WINAPI
Direct3DVertexBuffer7_GetVertexBufferDesc( LPDIRECT3DVERTEXBUFFER7 iface,
                                           LPD3DVERTEXBUFFERDESC lpD3DVertexBufferDesc )
{
    ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirect3DVertexBuffer7, iface);
    TRACE("(%p)->(%p)\n", iface, lpD3DVertexBufferDesc);

    D3D_SD2VBD(&This->surface_desc, lpD3DVertexBufferDesc);

    return D3D_OK;
}

static HRESULT WINAPI
Direct3DVertexBuffer7_ProcessVerticesStrided(LPDIRECT3DVERTEXBUFFER7 iface,
                   DWORD dwVertexOp, DWORD dwDestIndex, DWORD dwCount,
                   LPD3DDRAWPRIMITIVESTRIDEDDATA lpVertexArray,
                   DWORD dwSrcIndex, LPDIRECT3DDEVICE7 lpD3DDevice,
                   DWORD dwFlags)
{
    ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirect3DVertexBuffer7, iface);

    TRACE("(%p)->(%08lx, %08lx, %08lx, %p, %08lx, %p, %08lx)\n",
          This, dwVertexOp, dwDestIndex, dwCount, lpVertexArray,
          dwSrcIndex, lpD3DDevice, dwFlags);
    return ProcessVerticesStrided(iface, dwVertexOp, dwDestIndex, dwCount,
                                  lpVertexArray, 0, dwSrcIndex, lpD3DDevice,
                                  dwFlags);
}

#undef CONVERT

static ICOM_VTABLE(IDirect3DVertexBuffer7) DDRAW_ID3DVB7_VTable =
{
    Direct3DVertexBuffer7_QueryInterface,
    Direct3DVertexBuffer7_AddRef,
    Direct3DVertexBuffer7_Release,
    Direct3DVertexBuffer7_Lock,
    Direct3DVertexBuffer7_Unlock,
    Direct3DVertexBuffer7_ProcessVertices,
    Direct3DVertexBuffer7_GetVertexBufferDesc,
    Direct3DVertexBuffer7_Optimize,
    Direct3DVertexBuffer7_ProcessVerticesStrided
};

#define CONVERT(pd3dvb) COM_INTERFACE_CAST(IDirectDrawSurfaceImpl,	\
					   IDirect3DVertexBuffer,	\
					   IDirectDrawSurface7,		\
					   (pd3dvb))
#define CONVERT7(pd3dvb) COM_INTERFACE_CAST(IDirectDrawSurfaceImpl,	\
					    IDirect3DVertexBuffer,	\
					    IDirect3DVertexBuffer7,	\
					    (pd3dvb))

static HRESULT WINAPI
Direct3DVertexBuffer_QueryInterface(LPDIRECT3DVERTEXBUFFER iface, REFIID riid,
				    LPVOID *ppObj)
{
    return IDirectDrawSurface7_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3DVertexBuffer_AddRef(LPDIRECT3DVERTEXBUFFER iface)
{
    return IDirectDrawSurface7_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3DVertexBuffer_Release(LPDIRECT3DVERTEXBUFFER iface)
{
    return IDirectDrawSurface7_Release(CONVERT(iface));
}

static HRESULT WINAPI
Direct3DVertexBuffer_Lock(LPDIRECT3DVERTEXBUFFER iface, DWORD dwFlags,
			  LPVOID* ppData, LPDWORD pdwSize)
{
    return IDirect3DVertexBuffer7_Lock(CONVERT7(iface), dwFlags, ppData, pdwSize);
}

static HRESULT WINAPI
Direct3DVertexBuffer_Unlock(LPDIRECT3DVERTEXBUFFER iface)
{
    return IDirect3DVertexBuffer7_Unlock(CONVERT7(iface));
}

static HRESULT WINAPI
Direct3DVertexBuffer_Optimize(LPDIRECT3DVERTEXBUFFER iface,
			      LPDIRECT3DDEVICE3 lpD3DDevice, DWORD dwFlags)
{
    LPDIRECT3DDEVICE7 pD3DDev7;
    HRESULT hr;

    hr = IDirect3DDevice3_QueryInterface(lpD3DDevice, &IID_IDirect3DDevice7,
					 (LPVOID *)&pD3DDev7);
    if (FAILED(hr)) return hr;

    hr = IDirect3DVertexBuffer7_Optimize(CONVERT7(iface), pD3DDev7, dwFlags);

    IDirect3DDevice7_Release(pD3DDev7);
    return hr;
}

#undef CONVERT7
#undef CONVERT

static ICOM_VTABLE(IDirect3DVertexBuffer) DDRAW_ID3DVB_VTable =
{
    Direct3DVertexBuffer_QueryInterface,
    Direct3DVertexBuffer_AddRef,
    Direct3DVertexBuffer_Release,
    Direct3DVertexBuffer_Lock,
    Direct3DVertexBuffer_Unlock,
    (void*)0xdead5003, /* ProcessVertices */
    (void*)0xdead5004, /* GetVertexBufferDesc */
    Direct3DVertexBuffer_Optimize
};

#define CONVERT(pd3deb) COM_INTERFACE_CAST(IDirectDrawSurfaceImpl,	\
					   IDirect3DExecuteBuffer,	\
					   IDirectDrawSurface7,		\
					   (pd3deb))

static HRESULT WINAPI
Direct3DExecuteBuffer_QueryInterface(LPDIRECT3DEXECUTEBUFFER iface, REFIID riid,
				    LPVOID *ppObj)
{
    return IDirectDrawSurface7_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3DExecuteBuffer_AddRef(LPDIRECT3DEXECUTEBUFFER iface)
{
    return IDirectDrawSurface7_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3DExecuteBuffer_Release(LPDIRECT3DEXECUTEBUFFER iface)
{
    return IDirectDrawSurface7_Release(CONVERT(iface));
}

static HRESULT WINAPI
Direct3DExecuteBuffer_Initialize(LPDIRECT3DEXECUTEBUFFER iface,
				 LPDIRECT3DDEVICE lpDirect3DDevice,
				 LPD3DEXECUTEBUFFERDESC lpDesc)
{
    TRACE("(%p)->(%p,%p)\n", iface, lpDirect3DDevice, lpDesc);
    return DDERR_ALREADYINITIALIZED;
}

static HRESULT WINAPI
Direct3DExecuteBuffer_Lock(LPDIRECT3DEXECUTEBUFFER iface,
			   LPD3DEXECUTEBUFFERDESC lpDesc)
{
    DDSURFACEDESC2 ddsd;
    HRESULT hr;

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

    ddsd.dwSize = sizeof(ddsd);
    hr = IDirectDrawSurface7_Lock(CONVERT(iface), NULL, &ddsd, 0, (HANDLE)0);

    if (FAILED(hr)) return hr;

    lpDesc->dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS | D3DDEB_LPDATA;
    lpDesc->dwCaps = 0;
    if (ddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
	lpDesc->dwCaps |= D3DDEBCAPS_SYSTEMMEMORY;
    if (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)
	lpDesc->dwCaps |= D3DDEBCAPS_VIDEOMEMORY;
    lpDesc->dwBufferSize = ddsd.u1.dwLinearSize;
    lpDesc->lpData = ddsd.lpSurface;

    return D3D_OK;
}

static HRESULT WINAPI
Direct3DExecuteBuffer_Unlock(LPDIRECT3DEXECUTEBUFFER iface)
{
    HRESULT hr;

    TRACE("(%p)\n", iface);

    hr = IDirectDrawSurface7_Unlock(CONVERT(iface), NULL);

    return hr;
}

static HRESULT WINAPI
Direct3DExecuteBuffer_SetExecuteData(LPDIRECT3DEXECUTEBUFFER iface,
				     LPD3DEXECUTEDATA lpData)
{
    HRESULT hr;
    TRACE("(%p)->(%p)\n", iface, lpData);
    TRACE(" vertex offset %ld\n", lpData->dwVertexOffset);
    TRACE(" vertex count %ld\n", lpData->dwVertexCount);
    TRACE(" instr offset %ld\n", lpData->dwInstructionOffset);
    TRACE(" instr length %ld\n", lpData->dwInstructionLength);
    TRACE(" h-vertex offset %ld\n", lpData->dwHVertexOffset);

    /* since execute buffers are only used by old games, which
     * aren't very demanding by DX7 standards, we don't need to
     * expend a new field in the IDirectDrawSurfaceImpl structure
     * on it for marginally faster access to an obsolete API,
     * we'll just use the private-data storage. */

    IDirectDrawSurface7_FreePrivateData(CONVERT(iface),
					&IID_IDirect3DExecuteBuffer); /* because our SetPrivateData is crap */

    hr = IDirectDrawSurface7_SetPrivateData(CONVERT(iface),
					    &IID_IDirect3DExecuteBuffer,
					    lpData,
					    sizeof(D3DEXECUTEDATA),
					    0);
    TRACE("=> %08lx\n", hr);
    return hr;
}

static HRESULT WINAPI
Direct3DExecuteBuffer_GetExecuteData(LPDIRECT3DEXECUTEBUFFER iface,
				     LPD3DEXECUTEDATA lpData)
{
    DWORD size = sizeof(D3DEXECUTEDATA);
    HRESULT hr;
    TRACE("(%p)->(%p)\n", iface, lpData);
    hr = IDirectDrawSurface7_GetPrivateData(CONVERT(iface),
					    &IID_IDirect3DExecuteBuffer,
					    lpData,
					    &size);
    TRACE(" vertex offset %ld\n", lpData->dwVertexOffset);
    TRACE(" vertex count %ld\n", lpData->dwVertexCount);
    TRACE(" instr offset %ld\n", lpData->dwInstructionOffset);
    TRACE(" instr length %ld\n", lpData->dwInstructionLength);
    TRACE(" h-vertex offset %ld\n", lpData->dwHVertexOffset);
    TRACE("=> %08lx\n", hr);
    return hr;
}

static HRESULT WINAPI
Direct3DExecuteBuffer_Optimize(LPDIRECT3DEXECUTEBUFFER iface, DWORD dwFlags)
{
    TRACE("(%p)->(%lx)\n", iface, dwFlags);
    /* perhaps d3dgl could compile it into a display list or something. */
    return D3D_OK;
}

#undef CONVERT

static ICOM_VTABLE(IDirect3DExecuteBuffer) DDRAW_ID3DEB_VTable =
{
    Direct3DExecuteBuffer_QueryInterface,
    Direct3DExecuteBuffer_AddRef,
    Direct3DExecuteBuffer_Release,
    Direct3DExecuteBuffer_Initialize,
    Direct3DExecuteBuffer_Lock,
    Direct3DExecuteBuffer_Unlock,
    Direct3DExecuteBuffer_SetExecuteData,
    Direct3DExecuteBuffer_GetExecuteData,
    (void*)0xdead6000, /* Validate */
    Direct3DExecuteBuffer_Optimize
};

