/*		DirectDrawSurface base implementation
 *
 * Copyright 1997-2000 Marcus Meissner
 * Copyright 1998-2000 Lionel Ulmer (most of Direct3D stuff)
 * Copyright 2000-2003 TransGaming Technologies Inc.
 * Copyright 2003 Jarmo Nikkanen (colorkey modifications)

 */
#include "config.h"
#include "winerror.h"

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

#include "wine/debug.h"
#include "ddraw_private.h"
#include "dsurface/main.h"
#include "ddraw/main.h"
#include "dsurface/thunks.h"
#include "dsurface/gamma.h"
#include "dsurface/vbuffer.h"
#include "dsurface/texture.h"

#include "d3d_private.h"
#include "d3ddevice/main.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

/** Creation/Destruction functions */

HRESULT
Main_DirectDrawSurface_Construct(IDirectDrawSurfaceImpl *This,
				 IDirectDrawImpl *pDD,
				 const DDSURFACEDESC2 *pDDSD)
{
    if (pDDSD != &This->surface_desc) {
	This->surface_desc.dwSize = sizeof(This->surface_desc);
	DD_STRUCT_COPY_BYSIZE(&(This->surface_desc),pDDSD);
    }
    This->uniqueness_value = 1; /* unchecked */
    This->ref = 1;

    This->t.lcl.lpSurfMore = &This->t.more;
    This->t.lcl.lpGbl = &This->t.gbl;
    This->t.lcl.dwProcessId = GetCurrentProcessId();
    This->t.lcl.dwFlags = 0; /* FIXME */
    This->t.lcl.ddsCaps.dwCaps = This->surface_desc.ddsCaps.dwCaps;
    /* FIXME: more local stuff */
    This->t.more.lpDD_lcl = &pDD->local;
    This->t.more.ddsCapsEx.dwCaps2 = This->surface_desc.ddsCaps.dwCaps2;
    This->t.more.ddsCapsEx.dwCaps3 = This->surface_desc.ddsCaps.dwCaps3;
    This->t.more.ddsCapsEx.dwCaps4 = This->surface_desc.ddsCaps.dwCaps4;
    This->t.more.pCreatedDDSurfaceDesc2 = &This->surface_desc;
    /* FIXME: more more stuff */
    This->t.gmore = &This->t.gbl_more;
    This->t.gbl.u3.lpDD = pDD->local.lpGbl;
    This->t.gbl.wWidth = This->surface_desc.dwWidth;
    This->t.gbl.wHeight = This->surface_desc.dwHeight;
    This->t.gbl.u4.dwLinearSize = This->surface_desc.u1.dwLinearSize;
    This->t.gbl.ddpfSurface = This->surface_desc.u4.ddpfPixelFormat;
    /* FIXME: more global stuff */
    This->sibling.lpAttached = &This->t.lcl;

    This->final_release = Main_DirectDrawSurface_final_release;
    This->late_allocate = Main_DirectDrawSurface_late_allocate;
    This->attach = Main_DirectDrawSurface_attach;
    This->detach = Main_DirectDrawSurface_detach;
    This->lock_update = Main_DirectDrawSurface_lock_update;
    This->unlock_update = Main_DirectDrawSurface_unlock_update;
    This->lose_surface = Main_DirectDrawSurface_lose_surface;
    This->set_palette    = Main_DirectDrawSurface_set_palette;
    This->update_palette = Main_DirectDrawSurface_update_palette;
    This->get_display_window = Main_DirectDrawSurface_get_display_window;
    This->get_gamma_ramp = Main_DirectDrawSurface_get_gamma_ramp;
    This->set_gamma_ramp = Main_DirectDrawSurface_set_gamma_ramp;
    This->lazy_alloc_dibsection = Main_DirectDrawSurface_lazy_alloc_dibsection;


    /* There is no generic implementation of IDDS7 */
    ICOM_INIT_INTERFACE(This, IDirectDrawSurface3,
			DDRAW_IDDS3_Thunk_VTable);
    ICOM_INIT_INTERFACE(This, IDirectDrawGammaControl,
			DDRAW_IDDGC_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DVertexBuffer7,
			DDRAW_ID3DVB7_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DVertexBuffer,
			DDRAW_ID3DVB_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DTexture2,
			DDRAW_ID3DT2_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DTexture,
			DDRAW_ID3DT_VTable);

    Main_DirectDraw_AddSurface(pDD, This);
    return DD_OK;
}

void Main_DirectDrawSurface_final_release(IDirectDrawSurfaceImpl* This)
{
    LPDIRECTDRAWSURFACE7 iface;
    LPATTACHLIST next;
    IDirectDrawSurfaceImpl* surf;
    PrivateData* data;

    TRACE("destroyed %p\n", This);
    Main_DirectDraw_RemoveSurface(This->ddraw_owner, This);

    /* free any private data (includes any aggregated d3d device,
     * which probably should be released before detaching surfaces) */
    data = This->private_data;
    while (data) {
	PrivateData* ndata = data->next;

	if (data->flags & DDSPD_IUNKNOWNPTR)
	{
	    if (data->ptr.object != NULL)
		IUnknown_Release(data->ptr.object);
	}
	else
	    HeapFree(GetProcessHeap(), 0, data->ptr.data);

	HeapFree(GetProcessHeap(), 0, data);

	data = ndata;
    }

    /* release attached surfaces */
    iface = ICOM_INTERFACE(This, IDirectDrawSurface7);
    next = This->t.lcl.lpAttachList;
    while (next) {
	surf = OBJECT_FROM_FIELD(IDirectDrawSurfaceImpl, t.lcl, next->lpAttached);
	next = next->lpLink;
	if (surf == This) break; /* break on circular chains (cubemaps) */
	IDirectDrawSurface7_DeleteAttachedSurface(iface, 0, ICOM_INTERFACE(surf, IDirectDrawSurface7));
    }

    /* release ourselves from any surface we're attached from */
    next = This->t.lcl.lpAttachListFrom;
    if (next) {
	surf = OBJECT_FROM_FIELD(IDirectDrawSurfaceImpl, t.lcl, next->lpAttached);
	IDirectDrawSurface7_DeleteAttachedSurface(ICOM_INTERFACE(surf, IDirectDrawSurface7), 0, iface);
    }
}

HRESULT Main_DirectDrawSurface_late_allocate(IDirectDrawSurfaceImpl* This)
{
    return DD_OK;
}

static void Main_DirectDrawSurface_Destroy(IDirectDrawSurfaceImpl* This)
{
    This->final_release(This);
    if (This->private != This+1) HeapFree(GetProcessHeap(), 0, This->private);
    HeapFree(GetProcessHeap(), 0, This);
}

void Main_DirectDrawSurface_ForceDestroy(IDirectDrawSurfaceImpl* This)
{
    WARN("destroying surface %p with refcnt %lu\n", This, This->ref);
    This->ref = 0; /* prevent recursive destroys */
    Main_DirectDrawSurface_Destroy(This);
}

ULONG WINAPI Main_DirectDrawSurface_Release(LPDIRECTDRAWSURFACE7 iface)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->() decrementing from %lu\n", This, This->ref);

    if (--This->ref == 0)
    {
	if (This->aux_release)
	    This->aux_release(This->aux_ctx, This->aux_data);
	Main_DirectDrawSurface_Destroy(This);
	return 0;
    }

    return This->ref;
}

ULONG WINAPI Main_DirectDrawSurface_AddRef(LPDIRECTDRAWSURFACE7 iface)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->() incrementing from %lu\n", This, This->ref);

    return ++This->ref;
}

HRESULT WINAPI
Main_DirectDrawSurface_QueryInterface(LPDIRECTDRAWSURFACE7 iface, REFIID riid,
				      LPVOID* ppObj)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppObj);

    if (IsEqualGUID(&IID_IUnknown, riid)
	|| IsEqualGUID(&IID_IDirectDrawSurface7, riid)
	|| IsEqualGUID(&IID_IDirectDrawSurface4, riid))
    {
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirectDrawSurface7);
	return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirectDrawSurface, riid)
	     || IsEqualGUID(&IID_IDirectDrawSurface2, riid)
	     || IsEqualGUID(&IID_IDirectDrawSurface3, riid))
    {
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirectDrawSurface3);
	return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirectDrawGammaControl, riid))
    {
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirectDrawGammaControl);
	return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirect3DTexture2, riid))
    {
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirect3DTexture2);
	return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirect3DTexture, riid))
    {
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirect3DTexture);
	return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirect3DRampDevice, riid) ||
	     IsEqualGUID(&IID_IDirect3DRGBDevice, riid) ||
	     IsEqualGUID(&IID_IDirect3DHALDevice, riid) ||
	     IsEqualGUID(&IID_IDirect3DMMXDevice, riid) ||
	     IsEqualGUID(&IID_IDirect3DRefDevice, riid) ||
	     IsEqualGUID(&IID_IDirect3DTnLHalDevice, riid))
    {
	LPDIRECT3DDEVICE dev = (LPDIRECT3DDEVICE)NULL;
	DWORD sdev = sizeof(dev);
	/* The first version of Direct3D worked by having apps
	 * query a Direct3DDevice interface from a render target
	 * surface, instead of creating the device explicitly.
	 * I seem to recall that *only* the first Direct3DDevice interface
	 * could be obtained via this approach, since later interfaces
	 * added SetRenderTarget, which would obviously only be
	 * available on surface-independent devices, which can't
	 * be obtained this way. */

	/* Since this feature is 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_GetPrivateData(iface, &IID_IDirect3DDevice, &dev, &sdev);

	if (dev) {
	    /* I'd like to know if this ever happens. */
	    ERR("3D device already created\n");
	}

	if (!dev) {
	    LPDIRECT3DDEVICE7 dev7 = (LPDIRECT3DDEVICE7)NULL;
	    HRESULT hr;

	    /* I'd like to know if this ever happens. */
	    if (This->ddraw_owner->devices) {
		ERR("other 3D device already exists\n");
	    }

	    /* FIXME: the device interface should probably be properly aggregated,
	     * i.e. QueryInterface for IDirectDrawSurface on the device ought to get
	     * us back here. */
	    hr = IDirect3D7_CreateDevice(ICOM_INTERFACE(This->ddraw_owner, IDirect3D7),
					 riid, iface, &dev7);
	    if (!dev7) return hr;
	    dev = COM_INTERFACE_CAST(IDirect3DDeviceImpl, IDirect3DDevice7, IDirect3DDevice, dev7);
	    /* we could return the created device directly without storing it, then it would be
	     * destroyed as soon as the caller releases the device. Not sure if that's a good idea,
	     * so let's do it safe and destroy it when the surface is destroyed. */
	    IDirectDrawSurface7_SetPrivateData(iface, &IID_IDirect3DDevice, dev, 0, DDSPD_IUNKNOWNPTR);
	}
	*ppObj = dev;
	return S_OK;
    }
    /* If we get here, we couldn't find anything */
    WARN("interface %s not found\n", debugstr_guid(riid));
    return E_NOINTERFACE;
}

/*** Callbacks */

BOOL
Main_DirectDrawSurface_attach(IDirectDrawSurfaceImpl *This,
			      IDirectDrawSurfaceImpl *to)
{
    return TRUE;
}

BOOL Main_DirectDrawSurface_detach(IDirectDrawSurfaceImpl *This)
{
    /* if the primary surface is destroyed and we're being detached from it,
     * we're supposed to be destroyed. Currently we don't, and may still linger
     * around for a bit, but we still need to destroy any 3D context we're holding,
     * to avoid crashing some OpenGL drivers. */
    if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER)
	IDirectDrawSurface7_FreePrivateData(ICOM_INTERFACE(This, IDirectDrawSurface7), &IID_IDirect3DDevice);
    return TRUE;
}

LPVOID
Main_DirectDrawSurface_lock_update(IDirectDrawSurfaceImpl* This, LPCRECT pRect, DWORD dwFlags)
{
    return (LPVOID)This->t.gbl.fpVidMem;
}

void
Main_DirectDrawSurface_unlock_update(IDirectDrawSurfaceImpl* This,
				     LPCRECT pRect)
{
}

void
Main_DirectDrawSurface_lose_surface(IDirectDrawSurfaceImpl* This)
{
}

void
Main_DirectDrawSurface_set_palette(IDirectDrawSurfaceImpl* This,
				   IDirectDrawPaletteImpl* pal)
{
}

void
Main_DirectDrawSurface_update_palette(IDirectDrawSurfaceImpl* This,
				      IDirectDrawPaletteImpl* pal,
				      DWORD dwStart, DWORD dwCount,
				      LPPALETTEENTRY palent)
{
}

HWND
Main_DirectDrawSurface_get_display_window(IDirectDrawSurfaceImpl* This)
{
    return 0;
}

HRESULT
Main_DirectDrawSurface_get_gamma_ramp(IDirectDrawSurfaceImpl* This,
				      DWORD dwFlags,
				      LPDDGAMMARAMP lpGammaRamp)
{
    HDC hDC;
    HRESULT hr;
    hr = This->get_dc(This, &hDC);
    if (FAILED(hr)) return hr;
    hr = GetDeviceGammaRamp(hDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
    This->release_dc(This, hDC);
    return hr;
}

HRESULT
Main_DirectDrawSurface_set_gamma_ramp(IDirectDrawSurfaceImpl* This,
				      DWORD dwFlags,
				      LPDDGAMMARAMP lpGammaRamp)
{
    HDC hDC;
    HRESULT hr;
    hr = This->get_dc(This, &hDC);
    if (FAILED(hr)) return hr;
    hr = SetDeviceGammaRamp(hDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
    This->release_dc(This, hDC);
    return hr;
}

HRESULT
Main_DirectDrawSurface_lazy_alloc_dibsection (IDirectDrawSurfaceImpl* This)
{
    /* Do nothing - this is only useful for the dib-based derived classes */
    return DD_OK;
}

/*** Interface functions */

HRESULT WINAPI
Main_DirectDrawSurface_AddAttachedSurface(LPDIRECTDRAWSURFACE7 iface,
					  LPDIRECTDRAWSURFACE7 pAttach)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    IDirectDrawSurfaceImpl* surf = ICOM_OBJECT(IDirectDrawSurfaceImpl,
					       IDirectDrawSurface7, pAttach);

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

    CHECK_LOST(This);

    /* Does windows check this? */
    if (surf == This)
	return DDERR_CANNOTATTACHSURFACE; /* unchecked */

    /* Does windows check this? */
    if (surf->ddraw_owner != This->ddraw_owner)
	return DDERR_CANNOTATTACHSURFACE; /* unchecked */

    if (surf->t.lcl.lpAttachListFrom)
	return DDERR_SURFACEALREADYATTACHED; /* unchecked */

    /* TODO MSDN: "You can attach only z-buffer surfaces with this method."
     * But apparently backbuffers and mipmaps can be attached too. */

    /* Set MIPMAPSUBLEVEL if this seems to be one */
    if (This->surface_desc.ddsCaps.dwCaps &
	surf->surface_desc.ddsCaps.dwCaps & DDSCAPS_MIPMAP) {
	surf->surface_desc.ddsCaps.dwCaps2 |= DDSCAPS2_MIPMAPSUBLEVEL;
	surf->t.more.ddsCapsEx.dwCaps2 |= DDSCAPS2_MIPMAPSUBLEVEL;
	/* FIXME: we should probably also add to dwMipMapCount of this
	 * and all parent surfaces (update create_texture if you do) */
    }

    /* Callback to allow the surface to do something special now that it is
     * attached. (e.g. maybe the Z-buffer tells the renderer to use it.) */
    if (!surf->attach(surf, This))
	return DDERR_CANNOTATTACHSURFACE;

    /* check: Where should it go in the chain? This puts it on the head. */
    surf->t.lcl.lpAttachListFrom = &This->sibling;
    surf->sibling.lpLink = This->t.lcl.lpAttachList;
    This->t.lcl.lpAttachList = &surf->sibling;

    IDirectDrawSurface7_AddRef(pAttach);

    return DD_OK;
}

/* MSDN: "not currently implemented." */
HRESULT WINAPI
Main_DirectDrawSurface_AddOverlayDirtyRect(LPDIRECTDRAWSURFACE7 iface,
					   LPRECT pRect)
{
    TRACE("(%p)->(%p)\n",iface,pRect);
    return DDERR_UNSUPPORTED; /* unchecked */
}

/* MSDN: "not currently implemented." */
HRESULT WINAPI
Main_DirectDrawSurface_BltBatch(LPDIRECTDRAWSURFACE7 iface,
				LPDDBLTBATCH pBatch, DWORD dwCount,
				DWORD dwFlags)
{
    TRACE("(%p)->(%p,%ld,%08lx)\n",iface,pBatch,dwCount,dwFlags);
    return DDERR_UNSUPPORTED; /* unchecked */
}

HRESULT WINAPI
Main_DirectDrawSurface_ChangeUniquenessValue(LPDIRECTDRAWSURFACE7 iface)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    volatile IDirectDrawSurfaceImpl* vThis = This;

    TRACE("(%p)\n",This);
    /* A uniquness value of 0 is apparently special.
     * This needs to be checked. */
    while (1)
    {
	DWORD old_uniqueness_value = vThis->uniqueness_value;
	DWORD new_uniqueness_value = old_uniqueness_value+1;

	if (old_uniqueness_value == 0) break;
	if (new_uniqueness_value == 0) new_uniqueness_value = 1;

	if (InterlockedCompareExchange((LONG*)&vThis->uniqueness_value,
                                       old_uniqueness_value,
                                       new_uniqueness_value)
	    == old_uniqueness_value)
	    break;
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_DeleteAttachedSurface(LPDIRECTDRAWSURFACE7 iface,
					     DWORD dwFlags,
					     LPDIRECTDRAWSURFACE7 pAttach)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    IDirectDrawSurfaceImpl* surf = ICOM_OBJECT(IDirectDrawSurfaceImpl,
					       IDirectDrawSurface7, pAttach);
    LPATTACHLIST prev, next;

    TRACE("(%p)->(%08lx,%p)\n",This,dwFlags,pAttach);

    if (!surf || (surf->t.lcl.lpAttachListFrom != &This->sibling))
	return DDERR_SURFACENOTATTACHED; /* unchecked */

    surf->detach(surf);

    /* Remove MIPMAPSUBLEVEL if this seemed to be one */
    if (This->surface_desc.ddsCaps.dwCaps &
	surf->surface_desc.ddsCaps.dwCaps & DDSCAPS_MIPMAP) {
	surf->surface_desc.ddsCaps.dwCaps2 &= ~DDSCAPS2_MIPMAPSUBLEVEL;
	surf->t.more.ddsCapsEx.dwCaps2 &= ~DDSCAPS2_MIPMAPSUBLEVEL;
	/* FIXME: we should probably also subtract from dwMipMapCount of this
	 * and all parent surfaces */
    }

    prev = NULL;
    next = This->t.lcl.lpAttachList;
    while (next && next != &surf->sibling) {
	prev = next;
	next = next->lpLink;
	if (next == &This->sibling) next = NULL;
    }

    if (next) {
	if (prev) prev->lpLink = next->lpLink;
	else This->t.lcl.lpAttachList = next->lpLink;
    }

    surf->sibling.lpLink = NULL;
    surf->t.lcl.lpAttachListFrom = NULL;

    IDirectDrawSurface7_Release(pAttach);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_EnumAttachedSurfaces(LPDIRECTDRAWSURFACE7 iface,
					    LPVOID context,
					    LPDDENUMSURFACESCALLBACK7 cb)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    LPATTACHLIST next;

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

    next = This->t.lcl.lpAttachList;
    while (next)
    {
	IDirectDrawSurfaceImpl* surf = OBJECT_FROM_FIELD(IDirectDrawSurfaceImpl, t.lcl, next->lpAttached);
	next = next->lpLink;
	if (surf == This) break;
	/* check: != DDENUMRET_OK or == DDENUMRET_CANCEL? */
	if (cb(ICOM_INTERFACE(surf, IDirectDrawSurface7), &surf->surface_desc,
	       context) == DDENUMRET_CANCEL)
	    break;
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_EnumOverlayZOrders(LPDIRECTDRAWSURFACE7 iface,
					  DWORD dwFlags, LPVOID context,
					  LPDDENUMSURFACESCALLBACK7 cb)
{
    TRACE("(%p)->(%08lx,%p,%p)\n",iface,dwFlags,context,cb);
    return DD_OK;
}

BOOL Main_DirectDrawSurface_flip_data(IDirectDrawSurfaceImpl* front,
				      IDirectDrawSurfaceImpl* back,
				      DWORD dwFlags)
{
    /* uniqueness_value? */
    /* This is necessary. But is it safe? */
    {
	HDC tmp = front->hDC;
	front->hDC = back->hDC;
	back->hDC = tmp;
    }

    {
	BOOL tmp = front->dc_in_use;
	front->dc_in_use = back->dc_in_use;
	back->dc_in_use = tmp;
    }

    {
	FLATPTR tmp = front->t.gbl.fpVidMem;
	front->t.gbl.fpVidMem = back->t.gbl.fpVidMem;
	back->t.gbl.fpVidMem = tmp;
    }

    {
	ULONG_PTR tmp = front->t.gbl_more.hKernelSurface;
	front->t.gbl_more.hKernelSurface = back->t.gbl_more.hKernelSurface;
	back->t.gbl_more.hKernelSurface = tmp;
    }

    return TRUE;
}

HRESULT WINAPI
Main_DirectDrawSurface_Flip(LPDIRECTDRAWSURFACE7 iface,
			    LPDIRECTDRAWSURFACE7 override, DWORD dwFlags)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    IDirectDrawSurfaceImpl* target;
    HRESULT hr;

    TRACE("(%p)->(%p,%08lx)\n",This,override,dwFlags);

    /* MSDN: "This method can be called only for a surface that has the
     * DDSCAPS_FLIP and DDSCAPS_FRONTBUFFER capabilities." */
    if ((This->surface_desc.ddsCaps.dwCaps&(DDSCAPS_FLIP|DDSCAPS_FRONTBUFFER))
	!= (DDSCAPS_FLIP|DDSCAPS_FRONTBUFFER))
	return DDERR_NOTFLIPPABLE;

    if (This->aux_flip)
	if (This->aux_flip(This->aux_ctx, This->aux_data))
	    return DD_OK;

    /* 1. find the flip target */
    /* XXX I don't think this algorithm works for more than 1 backbuffer. */
    if (override == NULL)
    {
	static DDSCAPS2 back_caps = { DDSCAPS_BACKBUFFER };
	LPDIRECTDRAWSURFACE7 tgt;

	hr = IDirectDrawSurface7_GetAttachedSurface(iface, &back_caps, &tgt);
	if (FAILED(hr)) return DDERR_NOTFLIPPABLE; /* unchecked */

	target = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7,
			     tgt);
	IDirectDrawSurface7_Release(tgt);
    }
    else
    {
	BOOL on_chain = FALSE;
	LPATTACHLIST next;

	/* MSDN: "The method fails if the specified [override] surface is not
	 * a member of the flipping chain." */

	/* Verify that override is on this flip chain. We assume that
	 * surf is the head of the flipping chain, because it's the front
	 * buffer. */
	target = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7,
			     override);

	/* Either target is (indirectly) attached to This or This is
	 * (indirectly) attached to target. */
	for (next = &target->sibling; next; next = next->lpAttached->lpAttachListFrom)
	{
	    IDirectDrawSurfaceImpl* surf = OBJECT_FROM_FIELD(IDirectDrawSurfaceImpl, t.lcl, next->lpAttached);
	    if (surf == This)
	    {
		on_chain = TRUE;
		break;
	    }
	}

	if (!on_chain)
	    return DDERR_INVALIDPARAMS; /* unchecked */
    }

    TRACE("flip to backbuffer: %p\n",target);
    if (This->flip_data(This, target, dwFlags))
	This->flip_update(This, dwFlags);

    return DD_OK;
}

static PrivateData* find_private_data(IDirectDrawSurfaceImpl *This,
				      REFGUID tag)
{
    PrivateData* data;
    for (data = This->private_data; data != NULL; data = data->next)
    {
	if (IsEqualGUID(&data->tag, tag)) break;
    }

    return data;
}

HRESULT WINAPI
Main_DirectDrawSurface_FreePrivateData(LPDIRECTDRAWSURFACE7 iface, REFGUID tag)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    PrivateData *data;

    TRACE("(%p)->(%s)\n", This, debugstr_guid(tag));
    data = find_private_data(This, tag);
    if (data == NULL) return DDERR_NOTFOUND;

    if (data->prev)
	data->prev->next = data->next;
    if (data->next)
	data->next->prev = data->prev;
    if (This->private_data == data)
	This->private_data = data->next;

    if (data->flags & DDSPD_IUNKNOWNPTR)
    {
	if (data->ptr.object != NULL)
	    IUnknown_Release(data->ptr.object);
    }
    else
	HeapFree(GetProcessHeap(), 0, data->ptr.data);

    HeapFree(GetProcessHeap(), 0, data);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetAttachedSurface(LPDIRECTDRAWSURFACE7 iface,
					  LPDDSCAPS2 pCaps,
					  LPDIRECTDRAWSURFACE7* ppSurface)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    LPATTACHLIST next;
    IDirectDrawSurfaceImpl* found = NULL;
    DWORD caps1, caps2, caps3, caps4;

    caps1 = pCaps->dwCaps;
    /* we frequently get garbage in dwCaps2 for DX6 apps, only regard it for DX7 */
    if (This->t.more.lpDD_lcl->dwLocalFlags & DDRAWILCL_DIRECTDRAW7) {
        caps2 = pCaps->dwCaps2;
        caps3 = pCaps->dwCaps3;
        caps4 = pCaps->dwCaps4;
    } else {
        caps2 = 0;
        caps3 = 0;
        caps4 = 0;
    }

    TRACE("(%p)->Looking for caps: %08lx,%08lx,%08lx,%08lx output: %p\n",This, caps1, caps2,
          caps3, caps4, ppSurface);

    for (next = This->t.lcl.lpAttachList; next; next = next->lpLink)
    {
	IDirectDrawSurfaceImpl* surf = OBJECT_FROM_FIELD(IDirectDrawSurfaceImpl, t.lcl, next->lpAttached);
        TRACE("Surface: (%p) caps: %lx,%lx,%lx,%lx \n",surf ,
               surf->surface_desc.ddsCaps.dwCaps,
               surf->surface_desc.ddsCaps.dwCaps2,
               surf->surface_desc.ddsCaps.dwCaps3,
               surf->surface_desc.ddsCaps.dwCaps4);
	if (((surf->surface_desc.ddsCaps.dwCaps & caps1) == caps1)
	   && ((surf->surface_desc.ddsCaps.dwCaps2 & caps2) == caps2))
	{
	    /* MSDN: "This method fails if more than one surface is attached
	     * that matches the capabilities requested." */
	    if (found != NULL)
            {
                FIXME("More than one attached surface matches requested caps.  What should we do here?\n");
                /* Previous code returned 'DDERR_NOTFOUND'.  That appears not
                   to be correct, given what 3DMark expects from MipMapped surfaces.
                   We shall just continue instead. */
            }

	    found = surf;
	    TRACE(" => found\n");
	}
    }

    if (found == NULL) {
	TRACE(" => not found\n");
	return DDERR_NOTFOUND;
    }

    *ppSurface = ICOM_INTERFACE(found, IDirectDrawSurface7);

    /* XXX d3dframe.cpp sometimes AddRefs things that it gets from us. */
    IDirectDrawSurface7_AddRef(ICOM_INTERFACE(found, IDirectDrawSurface7));
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetBltStatus(LPDIRECTDRAWSURFACE7 iface, DWORD dwFlags)
{
    TRACE("(%p)->(%08lx)\n",iface,dwFlags);

    switch (dwFlags)
    {
    case DDGBS_CANBLT:
    case DDGBS_ISBLTDONE:
	return DD_OK;

    default:
	return DDERR_INVALIDPARAMS;
    }
}

HRESULT WINAPI
Main_DirectDrawSurface_GetCaps(LPDIRECTDRAWSURFACE7 iface, LPDDSCAPS2 pCaps)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pCaps);
    *pCaps = This->surface_desc.ddsCaps;
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetClipper(LPDIRECTDRAWSURFACE7 iface,
				  LPDIRECTDRAWCLIPPER* ppClipper)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,ppClipper);
    if (This->clipper == NULL)
	return DDERR_NOCLIPPERATTACHED;

    *ppClipper = ICOM_INTERFACE(This->clipper, IDirectDrawClipper);
    IDirectDrawClipper_AddRef(ICOM_INTERFACE(This->clipper,
					     IDirectDrawClipper));
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetColorKey(LPDIRECTDRAWSURFACE7 iface, DWORD dwFlags,
				   LPDDCOLORKEY pCKey)
{
    /* There is a DDERR_NOCOLORKEY error, but how do we know if a color key
     * isn't there? That's like saying that an int isn't there. (Which MS
     * has done in other docs.) */
    /* We can check the colorkey flags ! ??*/ 

    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%08lx,%p)\n",This,dwFlags,pCKey);
    switch (dwFlags)
    {
    case DDCKEY_DESTBLT:
	if ((This->surface_desc.dwFlags&DDSD_CKDESTBLT) == 0) return DDERR_NOCOLORKEY;
	*pCKey = This->surface_desc.ddckCKDestBlt;
	break;

    case DDCKEY_DESTOVERLAY:
	if ((This->surface_desc.dwFlags&DDSD_CKDESTOVERLAY) == 0) return DDERR_NOCOLORKEY;  
	*pCKey = This->surface_desc.u3.ddckCKDestOverlay;
	break;

    case DDCKEY_SRCBLT:
	if ((This->surface_desc.dwFlags&DDSD_CKSRCBLT) == 0) return DDERR_NOCOLORKEY;
	*pCKey = This->surface_desc.ddckCKSrcBlt;
	break;

    case DDCKEY_SRCOVERLAY:
	if ((This->surface_desc.dwFlags&DDSD_CKSRCOVERLAY) == 0) return DDERR_NOCOLORKEY;
	*pCKey = This->surface_desc.ddckCKSrcOverlay;
	break;

    default:
	return DDERR_INVALIDPARAMS;
    }

    return DD_OK;
}

/* XXX We need to do something with the DC if the surface gets lost. */
HRESULT WINAPI
Main_DirectDrawSurface_GetDC(LPDIRECTDRAWSURFACE7 iface, HDC *phDC)
{
    DDSURFACEDESC2 ddsd;
    HRESULT hr;
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,phDC);
    CHECK_LOST(This);

    LOCK_OBJECT(This);

    if (This->dc_in_use)
    {
	UNLOCK_OBJECT(This);
	return DDERR_DCALREADYCREATED;
    }

    /* Lock as per MSDN.
     * Strange: Lock lists DDERR_SURFACEBUSY as an error, meaning that another
     * thread has it locked, but GetDC does not. */
    ddsd.dwSize = sizeof(ddsd);
    hr = IDirectDrawSurface7_Lock(iface, NULL, &ddsd, DDLOCK_READONLY, 0);
    if (FAILED(hr))
    {
	UNLOCK_OBJECT(This);
	return hr;
    }

    hr = This->get_dc(This, &This->hDC);
    if (SUCCEEDED(hr))
    {
	TRACE("returning %08x\n",This->hDC);

	*phDC = This->hDC;
	This->dc_in_use = TRUE;
    }
    else WARN("No DC! Prepare for trouble\n");

    UNLOCK_OBJECT(This);
    return hr;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetDDInterface(LPDIRECTDRAWSURFACE7 iface, LPVOID* pDD)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pDD);
    *pDD = ICOM_INTERFACE(This->ddraw_owner, IDirectDraw7);
    IDirectDraw7_AddRef(ICOM_INTERFACE(This->ddraw_owner, IDirectDraw7));
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetFlipStatus(LPDIRECTDRAWSURFACE7 iface, DWORD dwFlags)
{
    /* XXX: DDERR_INVALIDSURFACETYPE */

    TRACE("(%p)->(%08lx)\n",iface,dwFlags);
    switch (dwFlags)
    {
    case DDGFS_CANFLIP:
    case DDGFS_ISFLIPDONE:
	return DD_OK;

    default:
	return DDERR_INVALIDPARAMS;
    }
}

HRESULT WINAPI
Main_DirectDrawSurface_GetLOD(LPDIRECTDRAWSURFACE7 iface, LPDWORD pdwMaxLOD)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pdwMaxLOD);
    CHECK_TEXTURE(This);

    *pdwMaxLOD = This->max_lod;
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetOverlayPosition(LPDIRECTDRAWSURFACE7 iface,
					  LPLONG pX, LPLONG pY)
{
    return DDERR_NOTAOVERLAYSURFACE;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetPalette(LPDIRECTDRAWSURFACE7 iface,
				  LPDIRECTDRAWPALETTE* ppPalette)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,ppPalette);
    if (This->palette == NULL)
	return DDERR_NOPALETTEATTACHED;

    *ppPalette = ICOM_INTERFACE(This->palette, IDirectDrawPalette);
    IDirectDrawPalette_AddRef(ICOM_INTERFACE(This->palette,
					     IDirectDrawPalette));
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetPixelFormat(LPDIRECTDRAWSURFACE7 iface,
				      LPDDPIXELFORMAT pDDPixelFormat)
{
    /* What is DDERR_INVALIDSURFACETYPE for here? */
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pDDPixelFormat);
    DD_STRUCT_COPY_BYSIZE(pDDPixelFormat,&This->surface_desc.u4.ddpfPixelFormat);
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetPriority(LPDIRECTDRAWSURFACE7 iface,
				   LPDWORD pdwPriority)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pdwPriority);
    CHECK_TEXTURE(This);

    *pdwPriority = This->priority;
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetPrivateData(LPDIRECTDRAWSURFACE7 iface,
				      REFGUID tag, LPVOID pBuffer,
				      LPDWORD pcbBufferSize)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);
    PrivateData* data;

    TRACE("(%p)->(%s,%p,%p)\n", This, debugstr_guid(tag), pBuffer, pcbBufferSize);
    data = find_private_data(This, tag);
    if (data == NULL) return DDERR_NOTFOUND;

    /* This may not be right. */
    if ((data->flags & DDSPD_VOLATILE)
	&& data->uniqueness_value != This->uniqueness_value)
	return DDERR_EXPIRED;

    if (*pcbBufferSize < data->size)
    {
	*pcbBufferSize = data->size;
	return DDERR_MOREDATA;
    }

    if (data->flags & DDSPD_IUNKNOWNPTR)
    {
	*(LPUNKNOWN *)pBuffer = data->ptr.object;
	IUnknown_AddRef(data->ptr.object);
    }
    else
    {
	memcpy(pBuffer, data->ptr.data, data->size);
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetSurfaceDesc(LPDIRECTDRAWSURFACE7 iface,
				      LPDDSURFACEDESC2 pDDSD)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pDDSD);
    if ((pDDSD->dwSize < sizeof(DDSURFACEDESC)) ||
    	(pDDSD->dwSize > sizeof(DDSURFACEDESC2))) {
	ERR("Impossible/Strange struct size %ld.\n",pDDSD->dwSize);
	return DDERR_GENERIC;
    }

    DD_STRUCT_COPY_BYSIZE(pDDSD,&This->surface_desc);
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_GetUniquenessValue(LPDIRECTDRAWSURFACE7 iface,
					  LPDWORD pValue)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pValue);
    *pValue = This->uniqueness_value;
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_Initialize(LPDIRECTDRAWSURFACE7 iface,
				  LPDIRECTDRAW pDD, LPDDSURFACEDESC2 pDDSD)
{
    TRACE("(%p)->(%p,%p)\n",iface,pDD,pDDSD);
    return DDERR_ALREADYINITIALIZED;
}

HRESULT WINAPI
Main_DirectDrawSurface_IsLost(LPDIRECTDRAWSURFACE7 iface)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p) is%s lost\n",This, (This->lost ? "" : " not"));
    return This->lost ? DDERR_SURFACELOST : DD_OK;
}


/* XXX This doesn't actually do any locking or keep track of the locked
 * rectangles. The behaviour is poorly documented. */
HRESULT WINAPI
Main_DirectDrawSurface_Lock(LPDIRECTDRAWSURFACE7 iface, LPRECT prect,
			    LPDDSURFACEDESC2 pDDSD, DWORD flags, HANDLE h)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

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

    CHECK_LOST(This);

    if (flags & ~(DDLOCK_WAIT|DDLOCK_READONLY|DDLOCK_WRITEONLY))
	WARN("(%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));

    TRACE("locked surface: height=%ld, width=%ld, pitch=%ld, flags=%08lx, bits=%p\n",
	  pDDSD->dwHeight, pDDSD->dwWidth, pDDSD->u1.lPitch,
	  pDDSD->dwFlags, (LPVOID)This->t.gbl.fpVidMem);

    /* If asked only for a part, change the surface pointer.
     * (Not documented.) */
    if (prect != NULL) {
	TRACE("	lprect: %dx%d-%dx%d\n",
		prect->top,prect->left,prect->bottom,prect->right
	);
	if (This->surface_desc.dwFlags & DDSD_LINEARSIZE) {
	  ERR(" LPRECT used for linear surface !!\n");
	  return DDERR_INVALIDPARAMS;
	}
	if ((prect->top < 0) ||
	    (prect->left < 0) ||
	    (prect->bottom < 0) ||
	    (prect->right < 0)) {
	  ERR(" Negative values in LPRECT !!!\n");
	  return DDERR_INVALIDPARAMS;
       }

	pDDSD->lpSurface = (char*)This->lock_update(This, prect, flags)
	    + prect->top * This->t.gbl.u4.lPitch
	    + prect->left * GET_BPP(This->surface_desc);
    } else {
	pDDSD->lpSurface = This->lock_update(This, NULL, flags);
    }
    pDDSD->dwFlags |= DDSD_LPSURFACE;
    TRACE("bits=%p\n", pDDSD->lpSurface);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_PageLock(LPDIRECTDRAWSURFACE7 iface, DWORD dwFlags)
{
    /* Some surface types should return DDERR_CANTPAGELOCK. */
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_PageUnlock(LPDIRECTDRAWSURFACE7 iface, DWORD dwFlags)
{
    /* Some surface types should return DDERR_CANTPAGEUNLOCK, and we should
     * keep track so we can return DDERR_NOTPAGELOCKED as appropriate. */
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_ReleaseDC(LPDIRECTDRAWSURFACE7 iface, HDC hDC)
{
    HRESULT hr;
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%08x)\n",This,hDC);

    if (!This->dc_in_use || This->hDC != hDC)
	return DDERR_INVALIDPARAMS;

    This->release_dc(This, hDC);

    hr = IDirectDrawSurface7_Unlock(iface, NULL);
    if (FAILED(hr)) return hr;

    This->dc_in_use = FALSE;
    This->hDC = 0;

    return DD_OK;
}

/* Restore */

HRESULT WINAPI
Main_DirectDrawSurface_SetClipper(LPDIRECTDRAWSURFACE7 iface,
				  LPDIRECTDRAWCLIPPER pDDClipper)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pDDClipper);
    if (pDDClipper == ICOM_INTERFACE(This->clipper, IDirectDrawClipper))
	return DD_OK;

    if (This->clipper != NULL)
	IDirectDrawClipper_Release(ICOM_INTERFACE(This->clipper,
						  IDirectDrawClipper));

    This->clipper = ICOM_OBJECT(IDirectDrawClipperImpl, IDirectDrawClipper,
				pDDClipper);
    if (pDDClipper != NULL)
	IDirectDrawClipper_AddRef(pDDClipper);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_SetColorKey(LPDIRECTDRAWSURFACE7 iface,
				   DWORD dwFlags, LPDDCOLORKEY pCKey)
{
    DWORD depth, shift, left;
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%08lx,%p)\n",This,dwFlags,pCKey);

    if (pCKey == NULL)
    {
	switch (dwFlags & ~DDCKEY_COLORSPACE){
        case DDCKEY_DESTBLT: This->surface_desc.dwFlags &= ~DDSD_CKDESTBLT; break;
        case DDCKEY_DESTOVERLAY: This->surface_desc.dwFlags &= ~DDSD_CKDESTOVERLAY; break;
        case DDCKEY_SRCOVERLAY: This->surface_desc.dwFlags &= ~DDSD_CKSRCOVERLAY; break;
        case DDCKEY_SRCBLT: This->surface_desc.dwFlags &= ~DDSD_CKSRCBLT; break;
        default:
          ERR("Invalid parameter\n");
	        return DDERR_INVALIDPARAMS;
	}
	return DD_OK;
    }

    depth = GET_BPP(This->surface_desc);
    shift = (depth<<3);
    left = pCKey->dwColorSpaceHighValue>>shift | pCKey->dwColorSpaceLowValue>>shift;
    if (left>0) {  /* Should we return an error ? ? ? */
      if (depth==2) {
	WARN("Invalid Colorkey %lX for %ld bpp, fixing.. \n", pCKey->dwColorSpaceHighValue | pCKey->dwColorSpaceLowValue, depth<<3);
	pCKey->dwColorSpaceHighValue &= 0xFFFF;
	pCKey->dwColorSpaceLowValue &= 0xFFFF;
      }
      if (depth==1) {
	WARN("Invalid Colorkey %lX for %ld bpp, fixing.. \n", pCKey->dwColorSpaceHighValue | pCKey->dwColorSpaceLowValue, depth<<3);
	pCKey->dwColorSpaceHighValue &= 0xFF;
	pCKey->dwColorSpaceLowValue &= 0xFF;
      }
    }


    switch (dwFlags & ~DDCKEY_COLORSPACE)
    {
    case DDCKEY_DESTBLT:
	This->surface_desc.ddckCKDestBlt = *pCKey;
	This->surface_desc.dwFlags |= DDSD_CKDESTBLT;
	break;

    case DDCKEY_DESTOVERLAY:
	This->surface_desc.u3.ddckCKDestOverlay = *pCKey;
	This->surface_desc.dwFlags |= DDSD_CKDESTOVERLAY;
	break;

    case DDCKEY_SRCOVERLAY:
	This->surface_desc.ddckCKSrcOverlay = *pCKey;
	This->surface_desc.dwFlags |= DDSD_CKSRCOVERLAY;
	break;

    case DDCKEY_SRCBLT:
	This->surface_desc.ddckCKSrcBlt = *pCKey;
	This->surface_desc.dwFlags |= DDSD_CKSRCBLT;
	break;

    default:
	return DDERR_INVALIDPARAMS;
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_SetLOD(LPDIRECTDRAWSURFACE7 iface, DWORD dwMaxLOD)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%08lx)\n",This,dwMaxLOD);
    CHECK_TEXTURE(This);

    This->max_lod = dwMaxLOD;
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_SetOverlayPosition(LPDIRECTDRAWSURFACE7 iface,
					  LONG X, LONG Y)
{
    return DDERR_NOTAOVERLAYSURFACE;
}

HRESULT WINAPI
Main_DirectDrawSurface_SetPalette(LPDIRECTDRAWSURFACE7 iface,
				  LPDIRECTDRAWPALETTE pPalette)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%p)\n",This,pPalette);
    if (pPalette == ICOM_INTERFACE(This->palette, IDirectDrawPalette))
	return DD_OK;

    if (This->palette != NULL) {
	if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
	    This->palette->global.dwFlags &= ~DDPCAPS_PRIMARYSURFACE;
	IDirectDrawPalette_Release(ICOM_INTERFACE(This->palette,
						  IDirectDrawPalette));
	This->t.lcl.lpDDPalette = NULL;
	This->palette = NULL;
    }

    if (pPalette != NULL) {
	This->palette = ICOM_OBJECT(IDirectDrawPaletteImpl, IDirectDrawPalette,
				    pPalette);
	IDirectDrawPalette_AddRef(pPalette);
	if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
	    This->palette->global.dwFlags |= DDPCAPS_PRIMARYSURFACE;
	This->t.lcl.lpDDPalette = &This->palette->inter;
    }

    This->set_palette(This, This->palette);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_SetPriority(LPDIRECTDRAWSURFACE7 iface,
				   DWORD dwPriority)
{
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%08lx)\n",This,dwPriority);
    CHECK_TEXTURE(This);

    This->priority = dwPriority;
    return DD_OK;
}

/* Be careful when locking this: it is risky to call the object's AddRef
 * or Release holding a lock. */
HRESULT WINAPI
Main_DirectDrawSurface_SetPrivateData(LPDIRECTDRAWSURFACE7 iface,
				      REFGUID tag, LPVOID pData,
				      DWORD cbSize, DWORD dwFlags)
{
    PrivateData* data;
    ICOM_THIS(IDirectDrawSurfaceImpl, iface);

    TRACE("(%p)->(%s,%p,%ld,%lx)\n", This, debugstr_guid(tag), pData, cbSize, dwFlags);
    data = find_private_data(This, tag);
    if (data == NULL)
    {
	data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data));
	if (data == NULL) return DDERR_OUTOFMEMORY;

	data->tag = *tag;
	data->flags = dwFlags;
	data->uniqueness_value = This->uniqueness_value;

	if (dwFlags & DDSPD_IUNKNOWNPTR)
	{
	    data->ptr.object = (LPUNKNOWN)pData;
	    data->size = sizeof(LPUNKNOWN);
	    IUnknown_AddRef(data->ptr.object);
	}
	else
	{
	    data->ptr.data = HeapAlloc(GetProcessHeap(), 0, cbSize);
	    if (data->ptr.data == NULL)
	    {
		HeapFree(GetProcessHeap(), 0, data);
		return DDERR_OUTOFMEMORY;
	    }
	    data->size = cbSize;
	    memcpy(data->ptr.data, pData, cbSize);
	}

	/* link it in */
	data->next = This->private_data;
	data->prev = NULL;
	if (This->private_data)
	    This->private_data->prev = data;
	This->private_data = data;

	return DD_OK;
    }
    else
    {
	/* I don't actually know how windows handles this case. The only
	 * reason I don't just call FreePrivateData is because I want to
	 * guarantee SetPrivateData working when using LPUNKNOWN or data
	 * that is no larger than the old data. */

	return E_FAIL;
    }
}

/* SetSurfaceDesc */

HRESULT WINAPI
Main_DirectDrawSurface_Unlock(LPDIRECTDRAWSURFACE7 iface, LPRECT pRect)
{
    ICOM_THIS(IDirectDrawSurfaceImpl,iface);

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

    CHECK_LOST(This);

    This->unlock_update(This, pRect);
    if (This->aux_unlock)
	This->aux_unlock(This->aux_ctx, This->aux_data, pRect);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDrawSurface_UpdateOverlay(LPDIRECTDRAWSURFACE7 iface,
				     LPRECT pSrcRect,
				     LPDIRECTDRAWSURFACE7 pDstSurface,
				     LPRECT pDstRect, DWORD dwFlags,
				     LPDDOVERLAYFX pFX)
{
    return DDERR_UNSUPPORTED;
}

/* MSDN: "not currently implemented." */
HRESULT WINAPI
Main_DirectDrawSurface_UpdateOverlayDisplay(LPDIRECTDRAWSURFACE7 iface,
					    DWORD dwFlags)
{
    return DDERR_UNSUPPORTED;
}

HRESULT WINAPI
Main_DirectDrawSurface_UpdateOverlayZOrder(LPDIRECTDRAWSURFACE7 iface,
					   DWORD dwFlags,
					   LPDIRECTDRAWSURFACE7 pDDSRef)
{
    return DDERR_NOTAOVERLAYSURFACE;
}
