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

#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 "winreg.h"
#include "winnls.h"
#include "wine/unicode.h"

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

#define WINE_UNICODE_REWRITE

WINE_DEFAULT_DEBUG_CHANNEL(dplay);

/* This is set by dp8gdData's DPNET_SwitchSP.
 * We need this so that address objects can access the service provider.
 * THIS IS BAD though.. We shouldn't have this here.. But I'm not sure how
 * we are supposed to do address objects then.. How do they get into the service provider?
 * (no, they can't create their own instance of the sp class...)
 */
IDirectPlay8ServiceProvider *globalSP = NULL;

/* File that holds miscilanous functions used by all main DirectPlay8 classee */

WCHAR *DPNET_strndupW(WCHAR *pwszSource, int len)
{
    WCHAR *pwszNewString;

    if (strlenW(pwszSource) < len)
        len = strlenW(pwszSource);
    pwszNewString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (len + 1) * sizeof(WCHAR));
    memcpy(pwszNewString, pwszSource, len * sizeof(WCHAR));
    pwszNewString[len] = '\0';
    return pwszNewString;
}

WCHAR *DPNET_strdupW(WCHAR *pwszSource)
{
    return DPNET_strndupW(pwszSource, strlenW(pwszSource));
}

void DPNET_DirectPlay8GlobalData_Free(DirectPlay8GlobalData *dp8gdData)
{
    int i;
    PEER_player *curplayer;

    TRACE("%p\n", dp8gdData);
    for (i = 0; i < dp8gdData->SPAvailableCount; i++)
    {
        HeapFree(GetProcessHeap(), 0, dp8gdData->pSPAvailable[i].pwszName);
    }
    HeapFree(GetProcessHeap(), 0, dp8gdData->pSPAvailable);
    dp8gdData->SPAvailableCount = 0;
    if (dp8gdData->sp)
    {
        if (globalSP != dp8gdData->sp)
            ERR("OH NO! looks like there could be multiple instances of this object around. we don't support that yet. (fix the hack)\n");
        dp8gdData->sp->lpVtbl->Release(dp8gdData->sp);
        globalSP = NULL;
    }
    else if (globalSP)
        ERR("OH NO! looks like there could be multiple instances of this object around. we don't support that yet. (fix the hack)\n");

    curplayer = dp8gdData->remotePlayers;
    while (curplayer)
    {
        PEER_player *tempplayer;
        curplayer->pAddress->lpVtbl->Release(curplayer->pAddress);
        tempplayer = curplayer;
        curplayer = curplayer->prev;
        HeapFree(GetProcessHeap(), 0, tempplayer);
    }
    if (dp8gdData->localPlayer.set)
    {
        dp8gdData->localPlayer.pAddress->lpVtbl->Release(dp8gdData->localPlayer.pAddress);
    }
    HeapFree(GetProcessHeap(), 0, dp8gdData);
}

HRESULT DPNET_EnumServiceProviders(DirectPlay8GlobalData *dp8gdData,
                   const GUID *const pguidServiceProvider,
                   const GUID *const pguidApplication,
                   DPN_SERVICE_PROVIDER_INFO * pSPInfoBuffer,
                   DWORD * pcbEnumData,
                   DWORD * pcReturned,
                   const DWORD dwFlags)
{
    TRACE("(%s, %s, %p, %p (%li), %p, 0x%08lx)\n",
          debugstr_guid(pguidServiceProvider), debugstr_guid(pguidApplication),
          pSPInfoBuffer, pcbEnumData, *pcbEnumData, pcReturned, dwFlags);

    if (pguidServiceProvider)
    {
        FIXME("ignoring pguidServiceProvider\n");
    }
    if (pguidApplication)
    {
        FIXME("ignoring pguidApplication\n");
    }
    if (dwFlags && dwFlags & ~DPNENUMSERVICEPROVIDERS_ALL)
    {
        FIXME("unrecognised flags\n");
    }

    if (!dp8gdData->pSPAvailable) /* must fill our structure of available service providers */
    {
        HKEY kDPSPRoot;
        DWORD subKeys, maxSubKeyLen;
        LPSTR lpSubKeyBuffer;
        int i;

        RegOpenKeyA(HKEY_LOCAL_MACHINE,
                    "Software\\Microsoft\\DirectPlay8\\Service Providers", &kDPSPRoot);
        RegQueryInfoKeyA(kDPSPRoot, NULL, NULL, NULL, &subKeys, &maxSubKeyLen,
                         NULL, NULL, NULL, NULL, NULL, NULL);
        dp8gdData->pSPAvailable = HeapAlloc(GetProcessHeap(), 0, sizeof(DPN_SERVICE_PROVIDER_INFO) * subKeys);
        dp8gdData->SPAvailableCount = subKeys;
        lpSubKeyBuffer = HeapAlloc(GetProcessHeap(), 0, maxSubKeyLen + 1);
        for (i = 0; i < subKeys; i++)
        {
            HKEY kDPSP;
            DWORD RegValueSize;
            LPSTR lpRegValue;
            LPWSTR lpRegValueW;
            int WCharNameSize;

            RegEnumKeyA(kDPSPRoot, i, lpSubKeyBuffer, maxSubKeyLen + 1);
            RegOpenKeyA(kDPSPRoot, lpSubKeyBuffer, &kDPSP);

            dp8gdData->pSPAvailable[i].dwFlags = 0;

            RegQueryValueExA(kDPSP, "Friendly Name", 0, NULL, NULL, &RegValueSize);
            lpRegValue = HeapAlloc(GetProcessHeap(), 0, RegValueSize);
            RegQueryValueExA(kDPSP, "Friendly Name", 0, NULL, lpRegValue, &RegValueSize);
            WCharNameSize = RegValueSize * sizeof(WCHAR) / sizeof(CHAR);
            dp8gdData->pSPAvailable[i].pwszName = HeapAlloc(GetProcessHeap(), 0, WCharNameSize);
            MultiByteToWideChar(CP_ACP, 0, lpRegValue, -1, dp8gdData->pSPAvailable[i].pwszName,
                                WCharNameSize);
            HeapFree(GetProcessHeap(), 0, lpRegValue);

            RegQueryValueExA(kDPSP, "GUID", 0, NULL, NULL, &RegValueSize);
            lpRegValue = HeapAlloc(GetProcessHeap(), 0, RegValueSize);
            lpRegValueW = HeapAlloc(GetProcessHeap(), 0, RegValueSize * sizeof(WCHAR) / sizeof(CHAR));
            RegQueryValueExA(kDPSP, "GUID", 0, NULL, lpRegValue, &RegValueSize);
            MultiByteToWideChar(CP_ACP, 0, lpRegValue, -1, lpRegValueW,
                                RegValueSize * sizeof(WCHAR) / sizeof(CHAR));
            CLSIDFromString(lpRegValueW, &dp8gdData->pSPAvailable[i].guid);
            HeapFree(GetProcessHeap(), 0, lpRegValue);
            HeapFree(GetProcessHeap(), 0, lpRegValueW);

            dp8gdData->pSPAvailable[i].pvReserved = 0;
            dp8gdData->pSPAvailable[i].dwReserved = 0;
        }
        HeapFree(GetProcessHeap(), 0, lpSubKeyBuffer);
    }
    if (*pcbEnumData < sizeof(DPN_SERVICE_PROVIDER_INFO) * dp8gdData->SPAvailableCount)
    {
        *pcbEnumData = sizeof(DPN_SERVICE_PROVIDER_INFO) * dp8gdData->SPAvailableCount;
        *pcReturned = 0;
        return DPNERR_BUFFERTOOSMALL;
    }
    /* FIXME: shouldn't use memcpy :( */
    memcpy(pSPInfoBuffer, dp8gdData->pSPAvailable, sizeof(DPN_SERVICE_PROVIDER_INFO) * dp8gdData->SPAvailableCount);
    *pcReturned = dp8gdData->SPAvailableCount;
    return S_OK;
}

HRESULT DirectPlay8Communicate_GetSPCaps(DirectPlay8GlobalData *dp8gdData,
                                         const GUID *const pguidSP,
                                         DPN_SP_CAPS *const pdpnSPCaps, DWORD dwFlags)
{
    HRESULT hr;
    if (FAILED(hr = DPNET_SwitchSP(dp8gdData, pguidSP)))
        return hr;
    return dp8gdData->sp->lpVtbl->GetSPCaps(dp8gdData->sp, pdpnSPCaps, dwFlags);
}

HRESULT DirectPlay8Communicate_GetCaps(DirectPlay8GlobalData *dp8gdData, DPN_CAPS *pdpnCaps, DWORD dwFlags)
{
    if (pdpnCaps->dwSize != sizeof(DPN_CAPS))
    return DPNERR_INVALIDPARAM;
    memcpy(pdpnCaps, &dp8gdData->dpnCaps, sizeof(DPN_CAPS));
    return S_OK;
}

