/* dpnet.dll
 *
 * Copyright 2002, 2003 TransGaming Technologies
 *
 * David Hammerton (david@transgaming.com)
 */

#include <stdio.h>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winreg.h"
#include "objbase.h"
#include "wine/debug.h"
#include "winerror.h"
#include "winnt.h"
#include "wine/unicode.h"

#define NO_SHLWAPI_STREAM
#include "shlwapi.h"

#include "dplay8.h"
#include "dpaddr.h"
#include "dplay8_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(dplay);

extern IDirectPlay8ServiceProvider *globalSP; /* hack. see general.c to as why */

static ICOM_VTABLE(IDirectPlay8Address) directPlay8AddressVT;
static ICOM_VTABLE(IDirectPlay8AddressIP) directPlay8AddressIPVT;
static DWORD GetDataType(const WCHAR * name);

typedef struct dpna_types_t {
    int index;
    const WCHAR * name;
    DWORD type;
} dpna_types_t;

static dpna_types_t dpna_types[] = {
    { 0, DPNA_KEY_PROVIDER, DPNA_DATATYPE_GUID },
    { 1, DPNA_KEY_APPLICATION_INSTANCE, DPNA_DATATYPE_GUID },
    { 2, DPNA_KEY_HOSTNAME, DPNA_DATATYPE_STRING },
    { 3, DPNA_KEY_BAUD, DPNA_DATATYPE_DWORD },
    { 4, DPNA_KEY_DEVICE, DPNA_DATATYPE_GUID },
    { 5, DPNA_KEY_FLOWCONTROL, DPNA_DATATYPE_STRING },
    { 6, DPNA_KEY_NAMEINFO, DPNA_DATATYPE_STRING },
    { 7, DPNA_KEY_NAT_RESOLVER, DPNA_DATATYPE_STRING },
    { 8, DPNA_KEY_NAT_RESOLVER_USER_STRING, DPNA_DATATYPE_STRING },
    { 9, DPNA_KEY_PARITY, DPNA_DATATYPE_STRING },
    { 10, DPNA_KEY_PHONENUMBER, DPNA_DATATYPE_STRING },
    { 11, DPNA_KEY_PORT, DPNA_DATATYPE_DWORD },
    { 12, DPNA_KEY_PROGRAM, DPNA_DATATYPE_GUID },
    { 13, DPNA_KEY_SCOPE, DPNA_DATATYPE_DWORD },
    { 14, DPNA_KEY_STOPBITS, DPNA_DATATYPE_DWORD },
    { 15, DPNA_KEY_TRAVERSALMODE, DPNA_DATATYPE_DWORD }
};

#define MAX_DPNA_TYPES 16

/* convert a guid to a wstr */
static void guid2wstr(const GUID *guid, LPWSTR wstr)
{
    char str[40];

    sprintf(str, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
        guid->Data1, guid->Data2, guid->Data3,
        guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
        guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);

    MultiByteToWideChar(CP_ACP, 0, str, -1, wstr, 40);
}

HRESULT WINAPI DirectPlay8Address_QueryInterface(PDIRECTPLAY8ADDRESS iface,
                                                 REFIID riid, LPVOID *obj)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);
    TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
    if (IsEqualGUID(&IID_IUnknown, riid) ||
        IsEqualGUID(&IID_IDirectPlay8Address, riid))
    {
        This->ref++;
        *obj = This;
        return S_OK;
    }
    else if (IsEqualGUID(&IID_IDirectPlay8AddressIP, riid))
    {
        This->ref++;
        *obj = (LPVOID *)&This->lpVtbl2;
        return S_OK;
    }
    else
    {
        FIXME("(%p)->(%s, %p): no interface\n", iface, debugstr_guid(riid), obj);
        return E_NOINTERFACE;
    }
}

ULONG WINAPI DirectPlay8Address_AddRef(PDIRECTPLAY8ADDRESS iface)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);
    TRACE("(%p)->()\n", iface);
    return ++This->ref;
}

ULONG WINAPI DirectPlay8Address_Release(PDIRECTPLAY8ADDRESS iface)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);
    TRACE("(%p)->()\n", iface);
    if (--This->ref) return This->ref;

    if (This->spData && !This->spDataRelease) FIXME("no release for data @ %p\n", This->spData);
    if (This->spData) This->spDataRelease(This->spData);
    while (This->component != NULL) {
        struct AddressComponent * component = This->component;
        This->component = This->component->next;
        HeapFree(GetProcessHeap(), 0, component->pwszName);
        HeapFree(GetProcessHeap(), 0, component->lpvData);
        HeapFree(GetProcessHeap(), 0, component);
    }
    if (This->dwDataSize > 0) {
        HeapFree(GetProcessHeap(), 0, This->pvUserData);
    }
    HeapFree(GetProcessHeap(), 0, This);
    return 0;
}

HRESULT WINAPI DirectPlay8Address_BuildFromURLA(PDIRECTPLAY8ADDRESS iface, CHAR *pszSourceURL)
{
    /* ICOM_THIS(IDirectPlay8AddressImpl, iface); */

    WCHAR *pwszSourceURL;
    INT iUrlStringLen;
    HRESULT hr;

    TRACE("(%p)->(%s)\n", iface, debugstr_a(pszSourceURL));

    iUrlStringLen = strlen(pszSourceURL);
    pwszSourceURL = HeapAlloc(GetProcessHeap(), 0, (iUrlStringLen + 1) * sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, pszSourceURL, iUrlStringLen, pwszSourceURL,
                        (iUrlStringLen + 1) * sizeof(WCHAR) / sizeof(CHAR));

    hr = IDirectPlay8Address_BuildFromURLW(iface, pwszSourceURL);

    HeapFree(GetProcessHeap(), 0, pwszSourceURL);

    return hr;
}

HRESULT WINAPI DirectPlay8Address_BuildFromURLW(PDIRECTPLAY8ADDRESS iface, WCHAR *pwszSourceURL)
{
    /*ICOM_THIS(IDirectPlay8AddressImpl, iface);*/
    WCHAR *provider = NULL;
    DWORD providerLen = 0;
    GUID guidSP;
    int i;

    TRACE("(%p)->(%s)\n", iface, debugstr_w(pwszSourceURL));

    if (strncmpW(pwszSourceURL, DPNA_HEADER, strlenW(DPNA_HEADER)) != 0)
        return DPNERR_INVALIDURL;

    /* First, clear the old data out*/
    IDirectPlay8Address_Clear(iface);

    if (DPNET_GetKeyValueW(pwszSourceURL, DPNA_KEY_PROVIDER, provider, &providerLen)
            != DPNERR_BUFFERTOOSMALL)
    {
        return DPNERR_INVALIDURL;
    }

    provider = HeapAlloc(GetProcessHeap(), 0, providerLen * sizeof(WCHAR));
    DPNET_GetKeyValueW(pwszSourceURL, DPNA_KEY_PROVIDER, provider, &providerLen);
    UrlUnescapeW(provider, NULL, NULL, URL_UNESCAPE_INPLACE);
    CLSIDFromString(provider, &guidSP);
    HeapFree(GetProcessHeap(), 0, provider);
    IDirectPlay8Address_SetSP(iface, &guidSP);

    /* start at 1 so we skip the provider we just added */
    for (i = 1; i < MAX_DPNA_TYPES; i++) {
        WCHAR attr[128];
        void * datum = NULL;
        DWORD len = 0;
        DWORD type;

        strcpyW(attr, dpna_types[i].name);
        type = dpna_types[i].type;
        if (DPNET_GetKeyValueW(pwszSourceURL, (const WCHAR *)attr, NULL,
                               &len) == DPNERR_BUFFERTOOSMALL)
        {
            datum = HeapAlloc(GetProcessHeap(), 0, len);
            DPNET_GetKeyValueW(pwszSourceURL, attr, datum, &len);
            IDirectPlay8Address_AddComponent(iface, attr, datum, len, type);
            HeapFree(GetProcessHeap(), 0, datum);
        }
    }

    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_Duplicate(PDIRECTPLAY8ADDRESS iface, PDIRECTPLAY8ADDRESS *ppdpaNewAddress)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);
    HRESULT hr;
    IDirectPlay8AddressImpl *newAddressThis;

    TRACE("(%p)->(%p)\n", iface, ppdpaNewAddress);
    hr = CoCreateInstance(&CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC_SERVER,
                          &IID_IDirectPlay8Address,
                          (LPVOID *) ppdpaNewAddress);
    if (!SUCCEEDED(hr))
    {
        TRACE("duplication failed\n");
        return DPNERR_GENERIC;
    }

    newAddressThis = (IDirectPlay8AddressImpl *)(*ppdpaNewAddress);

    memcpy(&newAddressThis->guidSP, &This->guidSP, sizeof(GUID));
    /* FIXME: SHOULD USE URLS */
    if (This->spData) This->spDataDuplicate(*ppdpaNewAddress, This->spData);
    TRACE("duplicated\n");

    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_Clear(PDIRECTPLAY8ADDRESS iface)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

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

    while (This->component != NULL) {
        struct AddressComponent * component = This->component;
        This->component = This->component->next;
        HeapFree(GetProcessHeap(), 0, component->pwszName);
        HeapFree(GetProcessHeap(), 0, component->lpvData);
        HeapFree(GetProcessHeap(), 0, component);
    }
    if (This->dwDataSize > 0) {
        HeapFree(GetProcessHeap(), 0, This->pvUserData);
        This->dwDataSize = 0;
    }

    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_GetURLW(PDIRECTPLAY8ADDRESS iface, WCHAR *pwszURL, PDWORD pdwNumChars)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);
    DWORD numCharsRequired = 0;
    WCHAR * url = NULL;
    DWORD i = 1;
    DWORD urlsize = 0;
    struct AddressComponent *component;
    WCHAR wide[1024];
    WCHAR equals[] = {'=', 0};
    WCHAR pound[] = {'#', 0};
    WCHAR semicolon[] = {';', 0};

    TRACE("(%p)->(%p, %p (%li))\n", iface, pwszURL, pdwNumChars, *pdwNumChars);

    /* If no GUID is set or no components have been added, we're done */
    if (IsEqualGUID(&This->guidSP, &IID_IUnknown) || This->component == NULL) {
        return DPNERR_INVALIDURL;
    }

    /* First compute the size of the URL. This is a pretty rough "guess" and
     * will likely be a tad too big but we'll get the "right" number later. */
    component = This->component;
    while (component != NULL) {
        if (component->dwDataType == GetDataType(component->pwszName)) {
            urlsize += component->dwDataSize;
            urlsize += strlenW(component->pwszName) + 2; /* include = and ; */
            if (component->dwDataType == DPNA_DATATYPE_GUID) {
                urlsize += 6; /* handle escaping */
            }
        } 
        component = component->next;
    }

    urlsize += 46; /* for the escaped provider GUID */
    urlsize += strlenW(DPNA_HEADER) + 2; /* include /: */
    urlsize += strlenW(DPNA_KEY_PROVIDER) + 2; /* include = and ; */
    urlsize += This->dwDataSize; /* user data */

    /* This should always be true, but should still check it */
    if (urlsize > 0) {
        DWORD needed = 1024;
        url = HeapAlloc(GetProcessHeap(), 0, urlsize * sizeof(WCHAR));

        strcpyW(url, DPNA_HEADER); /* x-directplay:/ */
        strcatW(url, DPNA_KEY_PROVIDER); /* provider */
        strcatW(url, equals);

        StringFromGUID2(&This->guidSP, wide, sizeof(wide));
        UrlUnescapeW(wide, NULL, &needed, URL_UNESCAPE_INPLACE);
        strcatW(url, wide);

        strcatW(url, semicolon);
        component = This->component;

        while (component != NULL) {
            strcatW(url, component->pwszName);
            strcatW(url, equals);
            switch (component->dwDataType) {
                case DPNA_DATATYPE_STRING:
                    strcatW(url, component->lpvData);
                    break;
                case DPNA_DATATYPE_DWORD:
                {
                    const WCHAR fmt[] = {'%', 'l', 'd', 0};
                    wsprintfW(wide, fmt, *((const DWORD *)component->lpvData));
                    strcatW(url, wide);
                    break;
                }
                case DPNA_DATATYPE_GUID:
                {
                    guid2wstr((GUID*)component->lpvData, wide);
                    strcatW(url, wide);
                }
                default:
                    WARN("Unhandled data type %ld\n", component->dwDataType);
                    break;
            }
            strcatW(url, semicolon);
            i++;
            component = component->next;
        } /* while */
    }

    /* remove the trailing semi-colon */
    url[strlenW(url)-1] = '\0';

    if (This->dwDataSize > 0) {
        strcatW(url, pound);
        strcatW(url, This->pvUserData);
    }

    numCharsRequired = strlenW(url) + 1; /* null term */
    if (*pdwNumChars < numCharsRequired)
    {
        *pdwNumChars = numCharsRequired;
        HeapFree(GetProcessHeap(), 0, url);
        return DPNERR_BUFFERTOOSMALL;
    }
    if (pwszURL != NULL)
    {
        strcpyW(pwszURL, url);
        *pdwNumChars = numCharsRequired;
    }
    HeapFree(GetProcessHeap(), 0, url);
    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_GetSP(PDIRECTPLAY8ADDRESS iface, GUID *pguidSP)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%s)\n", iface, debugstr_guid(pguidSP));

    if (IsEqualGUID(&This->guidSP, &IID_IUnknown)) {
        return DPNERR_DOESNOTEXIST;
    }
    memcpy(pguidSP, &This->guidSP, sizeof(GUID));
    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_GetUserData(PDIRECTPLAY8ADDRESS iface,
               void *pvUserData,
               PDWORD pdwBufferSize
)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

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

    if (pvUserData == NULL && pdwBufferSize == 0) {
        *pdwBufferSize = This->dwDataSize;
        return DPNERR_BUFFERTOOSMALL;
    } else {
        if (*pdwBufferSize < This->dwDataSize) {
            *pdwBufferSize = This->dwDataSize;
            return DPNERR_BUFFERTOOSMALL;
        }
        memcpy(pvUserData, This->pvUserData, This->dwDataSize);
        /* Size only gets set when in "query" mode */
        if (*pdwBufferSize == 0)
            *pdwBufferSize = This->dwDataSize;
    }

    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_SetSP(PDIRECTPLAY8ADDRESS iface, const GUID *const pguidSP)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%s)\n", iface, debugstr_guid(pguidSP));
    memcpy(&This->guidSP, pguidSP, sizeof(GUID));
    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_SetUserData(PDIRECTPLAY8ADDRESS iface,
               const void *const pvUserData,
               const DWORD dwDataSize)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%s, %ld)\n", iface, debugstr_w(pvUserData), dwDataSize);

    if (pvUserData == NULL && dwDataSize != 0)
        return DPNERR_NOTALLOWED;

    if (This->dwDataSize > 0) {
        HeapFree(GetProcessHeap(), 0, This->pvUserData);
        This->dwDataSize = 0;
    }
    if (pvUserData != NULL) {
        This->pvUserData = HeapAlloc(GetProcessHeap(), 0, dwDataSize);
        memcpy(This->pvUserData, (void *)pvUserData, dwDataSize);
    } else {
        This->pvUserData = NULL;
    }
    This->dwDataSize = dwDataSize;

    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_GetNumComponents(PDIRECTPLAY8ADDRESS iface, PDWORD pdwNumComponents)
{
    DWORD count = 1; /* DX9 returns the # of components +1 for some reason */
    struct AddressComponent *c;

    ICOM_THIS(IDirectPlay8AddressImpl, iface);

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

    c = This->component;

    while (c != NULL) {
        count++;
        c = c->next;
    }

    TRACE("count: %ld\n", count);

    *pdwNumComponents = count;

    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_GetComponentByName(PDIRECTPLAY8ADDRESS iface,
               const WCHAR *const pwszName,
               void *pvBuffer,
               PDWORD pdwBufferSize,
               PDWORD pdwDataType)
{
    struct AddressComponent *component;
    HRESULT ret = DPNERR_DOESNOTEXIST;

    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%s, %p, %p (%ld), %p)\n", iface,
            debugstr_w(pwszName), pvBuffer, pdwBufferSize,
            *pdwBufferSize, pdwDataType);

    if (pdwBufferSize == NULL)
        return DPNERR_INVALIDPARAM;

    component = This->component;

    while (component != NULL) {
        if (!strcmpW(component->pwszName, pwszName)) {
            if (*pdwBufferSize >= component->dwDataSize) {
                *pdwDataType = component->dwDataType;
                if (component->dwDataType == DPNA_DATATYPE_STRING) {
                    strcpyW(pvBuffer, component->lpvData);
                } else
                    memcpy(pvBuffer, component->lpvData, component->dwDataSize);
                ret = S_OK;
            } else {
                ret = DPNERR_BUFFERTOOSMALL;
            }
            *pdwBufferSize = component->dwDataSize;
             break;
        }
        component = component->next;
    }

    TRACE("returning %lx\n", ret);
    return ret;
}

HRESULT WINAPI DirectPlay8Address_GetComponentByIndex(PDIRECTPLAY8ADDRESS iface,
               const DWORD dwComponentID,
               WCHAR *pwszName,
               PDWORD pdwNameLen,
               void *pvBuffer,
               PDWORD pdwBufferSize,
               PDWORD pdwDataType
)
{
    struct AddressComponent *component;
    HRESULT ret = DPNERR_DOESNOTEXIST;
    int i = 0;

    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%ld, %p, %p, %p, %p, %p)\n", iface, dwComponentID,
            pwszName, pdwNameLen, pvBuffer, pdwBufferSize,
            pdwDataType);

    if (pdwBufferSize == NULL || pdwNameLen == NULL)
        return DPNERR_INVALIDPARAM;

    component = This->component;

    while (component != NULL && i < dwComponentID - 1) {
        i++;
        component = component->next;
    }
    if (component) {
        if (*pdwBufferSize >= component->dwDataSize && *pdwNameLen >= strlenW(component->pwszName)) {
            *pdwDataType = component->dwDataType;
            memcpy(pvBuffer, component->lpvData, component->dwDataSize);
            strcpyW(pwszName, component->pwszName);
            ret = S_OK;
        } else {
            ret = DPNERR_BUFFERTOOSMALL;
        }
        *pdwBufferSize = component->dwDataSize;
        *pdwNameLen = strlenW(component->pwszName);
    }

    TRACE("returning %lx len=%ld\n", ret, *pdwBufferSize);

    return ret;
}

HRESULT WINAPI DirectPlay8Address_AddComponent(PDIRECTPLAY8ADDRESS iface,
               const WCHAR *const pwszName,
               const void *const lpvData,
               const DWORD dwDataSize,
               const DWORD dwDataType)
{
    struct AddressComponent *component;
    int    found = 0;

    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%s, %p, %ld, %ld)\n", iface,
            debugstr_w(pwszName), lpvData,
            dwDataSize, dwDataType);

    if (dwDataType != GetDataType(pwszName)) {
        return DPNERR_INVALIDPARAM;
    }

    component = This->component;
    while (component) {
        if (!strcmpW(component->pwszName, pwszName)) {
            HeapFree(GetProcessHeap(), 0, component->pwszName);
            HeapFree(GetProcessHeap(), 0, component->lpvData);
            found = 1;
            break;
        }
        component = component->next;
    }
    if (!found)
        component = HeapAlloc(GetProcessHeap(), 0, sizeof(struct AddressComponent));
    component->pwszName = HeapAlloc(GetProcessHeap(), 0, strlenW(pwszName)+1);
    component->lpvData = HeapAlloc(GetProcessHeap(), 0, dwDataSize);
    component->dwDataSize = dwDataSize;
    component->dwDataType = dwDataType;
    strcpyW(component->pwszName, pwszName);

    switch(dwDataType) {
        case DPNA_DATATYPE_STRING:
            strcpyW(component->lpvData, lpvData);
            TRACE("string is %s\n", debugstr_w(lpvData));
            break;
        case DPNA_DATATYPE_DWORD:
            TRACE("%ld (%ld)\n", *((const DWORD *)lpvData), dwDataSize);
        default:
            memcpy(component->lpvData, lpvData, dwDataSize);
            break;
    }

    if (found) { /* we are updating an existing entry */
        return S_OK;
    }

    component->next = NULL;

    /* Put new element onto the end of the list. This is necessary so the user
     * can call GetComponentByIndex and expect the component to be stored in
     * the order it was added. */
    if (This->component == NULL) {
        This->component = component;
    } else {
        struct AddressComponent *temp;
        temp = This->component;
        while (temp->next != NULL) {
            temp = temp->next;
        }
        temp->next = component;
    }

    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_SetDevice(PDIRECTPLAY8ADDRESS iface, const GUID *const pguidDevice)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%s)\n", iface, debugstr_guid(pguidDevice));
    memcpy(&This->guidDevice, pguidDevice, sizeof(GUID));
    return S_OK;
}

HRESULT WINAPI DirectPlay8Address_GetDevice(PDIRECTPLAY8ADDRESS iface, GUID *pguidDevice)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    TRACE("(%p)->(%p)\n", iface, pguidDevice);
    if (IsEqualGUID(&This->guidDevice, &IID_IUnknown)) {
        return DPNERR_DOESNOTEXIST;
    }
    memcpy(pguidDevice, &This->guidDevice, sizeof(GUID));
    return S_OK;
}

HRESULT DPNET_CreateDirectPlay8Address(IUnknown *pOuter, REFIID riid, LPVOID *ppobj)
{
    IDirectPlay8AddressImpl *ipDP8A;
    HRESULT hr;
    TRACE("()\n");
    ipDP8A = (IDirectPlay8AddressImpl *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                                   sizeof(IDirectPlay8AddressImpl));
    if (ipDP8A == NULL) return E_OUTOFMEMORY;
    ICOM_VTBL(ipDP8A) = &directPlay8AddressVT;
    ipDP8A->lpVtbl2 = &directPlay8AddressIPVT;
    IDirectPlay8Address_AddRef((IDirectPlay8Address *)ipDP8A);
    ipDP8A->component = NULL;
    ipDP8A->pvUserData = NULL;
    ipDP8A->dwDataSize = 0;
    ipDP8A->guidDevice = IID_IUnknown;
    ipDP8A->guidSP = IID_IUnknown;
    TRACE("Created new object: %p\n", ipDP8A);
    hr = IDirectPlay8Address_QueryInterface((IDirectPlay8Address *)ipDP8A, riid, ppobj);
    IDirectPlay8Address_Release((IDirectPlay8Address *)ipDP8A);

    ipDP8A->hDialogMutex = CreateMutexA(NULL, FALSE, "DPNET Address DialogMutex");
    if (SUCCEEDED(hr))
        return S_OK;
    else
        return E_NOINTERFACE;
}

/* DirectPlayAddressIP wrappers */
HRESULT WINAPI DirectPlay8AddressIP_QueryInterface(PDIRECTPLAY8ADDRESSIP iface,
                                                   REFIID riid, LPVOID *obj)
{
    ICOM_THIS_MULTI(IDirectPlay8AddressImpl, lpVtbl2, iface);
    TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
    return IDirectPlay8Address_QueryInterface((PDIRECTPLAY8ADDRESS) This, riid, obj);
}

ULONG WINAPI DirectPlay8AddressIP_AddRef(PDIRECTPLAY8ADDRESSIP iface)
{
    ICOM_THIS_MULTI(IDirectPlay8AddressImpl, lpVtbl2, iface);
    TRACE("(%p)->()\n", iface);
    return IDirectPlay8Address_AddRef((PDIRECTPLAY8ADDRESS) This);
}

ULONG WINAPI DirectPlay8AddressIP_Release(PDIRECTPLAY8ADDRESSIP iface)
{
    ICOM_THIS_MULTI(IDirectPlay8AddressImpl, lpVtbl2, iface);
    TRACE("(%p)->()\n", iface);
    return IDirectPlay8Address_Release((PDIRECTPLAY8ADDRESS) This);
}

static ICOM_VTABLE(IDirectPlay8Address) directPlay8AddressVT =
{
    ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
    DirectPlay8Address_QueryInterface,
    DirectPlay8Address_AddRef,
    DirectPlay8Address_Release,
    DirectPlay8Address_BuildFromURLW,
    DirectPlay8Address_BuildFromURLA,
    DirectPlay8Address_Duplicate,
    (void*)0xdead7007, /* SetEqual */
    (void*)0xdead7008, /* IsEqual */
    DirectPlay8Address_Clear,
    DirectPlay8Address_GetURLW,
    (void*)0xdead700b, /* GetURLA */
    DirectPlay8Address_GetSP, 
    DirectPlay8Address_GetUserData,
    DirectPlay8Address_SetSP,
    DirectPlay8Address_SetUserData,
    DirectPlay8Address_GetNumComponents, 
    DirectPlay8Address_GetComponentByName,
    DirectPlay8Address_GetComponentByIndex,
    DirectPlay8Address_AddComponent,
    DirectPlay8Address_GetDevice,
    DirectPlay8Address_SetDevice,
    (void*)0xdead7016  /* BuildFromDirectPlay4Address */
};

static ICOM_VTABLE(IDirectPlay8AddressIP) directPlay8AddressIPVT =
{
    ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
    DirectPlay8AddressIP_QueryInterface,
    DirectPlay8AddressIP_AddRef,
    DirectPlay8AddressIP_Release,
    (void*)0xdead7024, /* BuildFromSockAddr */
    (void*)0xdead7025, /* BuildFromAddress */
    (void*)0xdead7026, /* BuildLocalAddress */
    (void*)0xdead7027, /* GetSockAddress */
    (void*)0xdead7028, /* GetLocalAddress */
    (void*)0xdead7029  /* GetAddress */
};

/* private functions. used by the rest of dpnet.
 * Ideally should be in an interface */
HRESULT DPNET_Address_SetSPData(PDIRECTPLAY8ADDRESS iface, void *data,
                                spReleaseAddressInfo spReleaseCallback,
                                spDuplicateAddressInfo spDuplicateCallback)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    This->spData = data;
    This->spDataRelease = spReleaseCallback;
    This->spDataDuplicate = spDuplicateCallback;
    return S_OK;
}

HRESULT DPNET_Address_GetSPData(PDIRECTPLAY8ADDRESS iface, void **data)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    *data = This->spData;
    return S_OK;
}

HRESULT DPNET_Address_GetDialogLock(PDIRECTPLAY8ADDRESS iface)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    if (WaitForSingleObject(This->hDialogMutex, INFINITE) != WAIT_OBJECT_0)
        return E_FAIL;
    return S_OK;
}

HRESULT DPNET_Address_ReleaseDialogLock(PDIRECTPLAY8ADDRESS iface)
{
    ICOM_THIS(IDirectPlay8AddressImpl, iface);

    ReleaseMutex(This->hDialogMutex);
    return S_OK;
}

/* utility functions for parsing URLs */

HRESULT DPNET_GetKeyValueW(const WCHAR *url, const WCHAR *keyName,
                           WCHAR *keyValue /* [OUT] */,
                           PDWORD bufferLen /* [IN OUT] */)
{
    WCHAR *p, *p2, *p3;
    DWORD requiredLen;

    TRACE("%s, %p, %s, %p (%li)\n", debugstr_w(url), keyName,
          debugstr_w(keyValue), bufferLen, *bufferLen);

    p = strstrW(url, keyName);

    if (p == NULL) return DPNERR_DOESNOTEXIST;

    p = strchrW(p, DPNA_SEPARATOR_KEYVALUE);

    if (p == NULL) return DPNERR_DOESNOTEXIST;
    p++;

    p2 = strchrW(p, DPNA_SEPARATOR_COMPONENT);
    p3 = strchrW(p, DPNA_SEPARATOR_USERDATA);
    if (p2 == NULL && p3 == NULL) /* end of string */
        requiredLen = strlenW(p);
    else if (p2 == NULL)
        requiredLen = p3 - p;
    else if (p3 == NULL)
        requiredLen = p2 - p;
    else
        requiredLen = (p2 < p3 ? p2 : p3) - p;

    if (*bufferLen < requiredLen)
    {
        *bufferLen = requiredLen;
        return DPNERR_BUFFERTOOSMALL;
    }
    strncpyW(keyValue, p, requiredLen);
    keyValue[requiredLen] = 0;

    return DPN_OK;
}

static DWORD GetDataType(const WCHAR * name)
{
    int i;

    for (i = 0; i < MAX_DPNA_TYPES; i++) {
        if (!strcmpW(name, dpna_types[i].name)) {
            return dpna_types[i].type;
        }
    }

    return -1;
}
