/*	Legacy Direct3DMaterial interface
 *
 * Copyright 2001 TransGaming Technologies Inc.
 */

#include "config.h"

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

#include "wine/debug.h"
#include "ddrawi.h"
#include "d3dhal.h"
#include "d3d_private.h"
#include "d3ddevice/main.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

static ICOM_VTABLE(IDirect3DMaterial3) IDirect3DMaterial3_VTable;
static ICOM_VTABLE(IDirect3DMaterial2) IDirect3DMaterial2_VTable;

/* Non-interface functions */
HRESULT
Direct3DMaterial3_Construct(IDirect3DMaterialImpl* This,
			    IDirectDrawImpl* pDD)
{
    This->ref = 1;
    This->d3d_owner = pDD;
    This->tex = NULL;

    ICOM_INIT_INTERFACE(This, IDirect3DMaterial3,
			IDirect3DMaterial3_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3DMaterial2,
			IDirect3DMaterial2_VTable);

    /* Direct3D_AddMaterial(This->d3d_owner, This); */

    return D3D_OK;
}

HRESULT
Direct3DMaterial3_Create(IDirectDrawImpl *pDD,
			 LPDIRECT3DMATERIAL3 *ppd3dm)
{
    IDirect3DMaterialImpl* This;
    HRESULT hr;

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

    hr = Direct3DMaterial3_Construct(This, pDD);
    if (FAILED(hr))
	HeapFree(GetProcessHeap(), 0, This);
    else
	*ppd3dm = ICOM_INTERFACE(This, IDirect3DMaterial3);

    return hr;
}

static void Direct3DMaterial3_Destroy(IDirect3DMaterialImpl* This)
{
#if 0 /* ramp device only */
    if (This->tex)
	IDirectDrawSurface7_Release(ICOM_INTERFACE(This->tex, IDirectDrawSurface7));
#endif

    /* Direct3D_RemoveMaterial(This->d3d_owner, This); */
    HeapFree(GetProcessHeap(), 0, This);
}

void Direct3DMaterial3_ForceDestroy(IDirect3DMaterialImpl* This)
{
    WARN("destroying 3D material %p with refcnt %lu\n", This, This->ref);
    Direct3DMaterial3_Destroy(This);
}

/* Interface functions */
ULONG WINAPI Direct3DMaterial3_Release(LPDIRECT3DMATERIAL3 iface)
{
    ICOM_THIS(IDirect3DMaterialImpl, iface);

    if (--This->ref == 0)
    {
	Direct3DMaterial3_Destroy(This);
	return 0;
    }

    return This->ref;
}

ULONG WINAPI Direct3DMaterial3_AddRef(LPDIRECT3DMATERIAL3 iface)
{
    ICOM_THIS(IDirect3DMaterialImpl, iface);

    return ++This->ref;
}

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

    if (IsEqualGUID(&IID_IUnknown, riid)
	|| IsEqualGUID(&IID_IDirect3DMaterial3, riid))
    {
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirect3DMaterial3);
	return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirect3DMaterial2, riid))
    {
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirect3DMaterial2);
	return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirect3DMaterial, riid))
    {
	FIXME("IDirect3DMaterial v1\n");
	return E_NOINTERFACE;
#if 0
	This->ref++;
	*ppObj = ICOM_INTERFACE(This, IDirect3DMaterial);
	return S_OK;
#endif
    }
    else
	return E_NOINTERFACE;
}

void
Direct3DMaterial_update(IDirect3DMaterialImpl* This)
{
    if (This->dev_owner)
	IDirect3DDevice7_SetMaterial(ICOM_INTERFACE(This->dev_owner, IDirect3DDevice7),
				     &This->m);

#if 0 /* ramp device only */
    if (This->tex)
	IDirectDrawSurface7_Release(ICOM_INTERFACE(This->tex, IDirectDrawSurface7));
    This->tex = (IDirectDrawSurfaceImpl*)lpMat->hTexture;
    if (This->tex)
	IDirectDrawSurface7_AddRef(ICOM_INTERFACE(This->tex, IDirectDrawSurface7));

    if (This->dev_owner)
	IDirect3DDevice7_SetTexture(ICOM_INTERFACE(This->dev_owner, IDirect3DDevice7),
				    0, ICOM_INTERFACE(This->tex, IDirectDrawSurface7));
#endif

}

HRESULT WINAPI
Direct3DMaterial3_SetMaterial(LPDIRECT3DMATERIAL3 iface, LPD3DMATERIAL lpMat)
{
    ICOM_THIS(IDirect3DMaterialImpl, iface);
    TRACE("(%p)->(%p)\n", This, lpMat);
    This->m.u.dcvDiffuse   = lpMat->u.dcvDiffuse;
    This->m.u1.dcvAmbient  = lpMat->u1.dcvAmbient;
    This->m.u2.dcvSpecular = lpMat->u2.dcvSpecular;
    This->m.u3.dcvEmissive = lpMat->u3.dcvEmissive;
    This->m.u4.dvPower     = lpMat->u4.dvPower;

    return DD_OK;
}

HRESULT WINAPI
Direct3DMaterial3_GetMaterial(LPDIRECT3DMATERIAL3 iface, LPD3DMATERIAL lpMat)
{
    ICOM_THIS(IDirect3DMaterialImpl, iface);
    TRACE("(%p)->(%p)\n", This, lpMat);
    lpMat->u.dcvDiffuse   = This->m.u.dcvDiffuse;
    lpMat->u1.dcvAmbient  = This->m.u1.dcvAmbient;
    lpMat->u2.dcvSpecular = This->m.u2.dcvSpecular;
    lpMat->u3.dcvEmissive = This->m.u3.dcvEmissive;
    lpMat->u4.dvPower     = This->m.u4.dvPower;
#if 0 /* ramp device only */
    lpMat->hTexture = (D3DTEXTUREHANDLE)This->tex;
    lpMat->dwRampSize = 0;
#endif
    return DD_OK;
}

HRESULT WINAPI
Direct3DMaterial3_GetHandle(LPDIRECT3DMATERIAL3 iface, LPDIRECT3DDEVICE3 lpd3dd3,
			    LPD3DMATERIALHANDLE lpHandle)
{
    ICOM_THIS(IDirect3DMaterialImpl, iface);
    TRACE("(%p)->(%p,%p)\n", This, lpd3dd3, lpHandle);
    /* being lazy for now, just return a pointer to self */
    *lpHandle = (D3DMATERIALHANDLE)This;
    return DD_OK;
}

static ICOM_VTABLE(IDirect3DMaterial3) IDirect3DMaterial3_VTable =
{
    Direct3DMaterial3_QueryInterface,
    Direct3DMaterial3_AddRef,
    Direct3DMaterial3_Release,
    Direct3DMaterial3_SetMaterial,
    Direct3DMaterial3_GetMaterial,
    Direct3DMaterial3_GetHandle
};

#define CONVERT(pddgc) COM_INTERFACE_CAST(IDirect3DMaterialImpl,	\
					  IDirect3DMaterial2,		\
					  IDirect3DMaterial3,		\
					  (pddgc))

static HRESULT WINAPI
Direct3DMaterial2_QueryInterface(LPDIRECT3DMATERIAL2 iface, REFIID riid,
				 LPVOID *ppObj)
{
    return IDirect3DMaterial3_QueryInterface(CONVERT(iface), riid, ppObj);
}

static ULONG WINAPI
Direct3DMaterial2_AddRef(LPDIRECT3DMATERIAL2 iface)
{
    return IDirect3DMaterial3_AddRef(CONVERT(iface));
}

static ULONG WINAPI
Direct3DMaterial2_Release(LPDIRECT3DMATERIAL2 iface)
{
    return IDirect3DMaterial3_Release(CONVERT(iface));
}

HRESULT WINAPI
Direct3DMaterial2_SetMaterial(LPDIRECT3DMATERIAL2 iface, LPD3DMATERIAL lpMat)
{
    return IDirect3DMaterial3_SetMaterial(CONVERT(iface), lpMat);
}

HRESULT WINAPI
Direct3DMaterial2_GetMaterial(LPDIRECT3DMATERIAL2 iface, LPD3DMATERIAL lpMat)
{
    return IDirect3DMaterial3_GetMaterial(CONVERT(iface), lpMat);
}

HRESULT WINAPI
Direct3DMaterial2_GetHandle(LPDIRECT3DMATERIAL2 iface, LPDIRECT3DDEVICE2 lpd3dd2,
			    LPD3DMATERIALHANDLE lpHandle)
{
    return IDirect3DMaterial3_GetHandle(CONVERT(iface),
					COM_INTERFACE_CAST(IDirect3DDeviceImpl,
							   IDirect3DDevice2,
							   IDirect3DDevice3,
							   lpd3dd2),
					lpHandle);
}

#undef CONVERT

static ICOM_VTABLE(IDirect3DMaterial2) IDirect3DMaterial2_VTable =
{
    Direct3DMaterial2_QueryInterface,
    Direct3DMaterial2_AddRef,
    Direct3DMaterial2_Release,
    Direct3DMaterial2_SetMaterial,
    Direct3DMaterial2_GetMaterial,
    Direct3DMaterial2_GetHandle
};

#if 0
HRESULT WINAPI
Direct3DMaterial_Initialize(LPDIRECT3DMATERIAL iface, LPDIRECT3D pd3d)
{
    ICOM_THIS(IDirect3DMaterialImpl, iface);
    TRACE("(%p)->(%p)\n", This, pd3d);
    return DDERR_ALREADYINITIALIZED;
}
#endif
