/* 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 "dplay8.h"
#include "dplay8_private.h"

#define WINE_UNICODE_REWRITE

WINE_DEFAULT_DEBUG_CHANNEL(dplay);

static ICOM_VTABLE(IDirectPlay8Peer) directPlay8PeerVT;

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

ULONG WINAPI DirectPlay8Peer_AddRef(PDIRECTPLAY8PEER iface)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    TRACE("(%p)->()\n", iface);
    return ++This->ref;
}

ULONG WINAPI DirectPlay8Peer_Release(PDIRECTPLAY8PEER iface)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    TRACE("(%p)->()\n", iface);
    if (--This->ref) return This->ref;

    DPNET_DirectPlay8GlobalData_Free(&This->dp8gdData);

    HeapFree(GetProcessHeap(), 0, This);
    return 0;
}

HRESULT WINAPI DirectPlay8Peer_Initilize(PDIRECTPLAY8PEER iface, PVOID const pvUserContext,
                                        const PFNDPNMESSAGEHANDLER pfn, const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    FIXME("(%p)->(%p, %p, 0x%08lx): stub\n", iface, pvUserContext, pfn, dwFlags);

    This->dp8gdData.pfMessageHandler = pfn;
    This->dp8gdData.pvUserContext = pvUserContext;
    This->dp8gdData.dpnCaps.dwSize = sizeof(DPN_CAPS);
    This->dp8gdData.dpnCaps.dwFlags = 0;
    This->dp8gdData.dpnCaps.dwConnectTimeout = 2000;
    This->dp8gdData.dpnCaps.dwConnectRetries = 14;
    This->dp8gdData.dpnCaps.dwTimeoutUntilKeepAlive = 60000;
    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_EnumServiceProviders(PDIRECTPLAY8PEER iface,
                                                    const GUID *const pguidServiceProvider,
                                                    const GUID *const pguidApplication,
                                                    DPN_SERVICE_PROVIDER_INFO *const pSPInfoBuffer,
                                                    DWORD *const pcbEnumData, DWORD *const pcReturned,
                                                    const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);

    TRACE("(%p)->(%s, %s, %p, %p (%li), %p, 0x%08lx)\n",
          iface, debugstr_guid(pguidServiceProvider), debugstr_guid(pguidApplication),
          pSPInfoBuffer, pcbEnumData, *pcbEnumData, pcReturned, dwFlags);

    return DPNET_EnumServiceProviders(&This->dp8gdData, pguidServiceProvider, pguidApplication,
                                      pSPInfoBuffer, pcbEnumData, pcReturned, dwFlags);
}

HRESULT WINAPI DirectPlay8Peer_GetPeerAddress(PDIRECTPLAY8PEER iface,
                                              const DPNID dpnid,
                                              IDirectPlay8Address **const pAddress,
                                              const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    PEER_player *cur;

    TRACE("(%p)->(%li, %p, %08lx)\n", iface, dpnid, pAddress, dwFlags);

    cur = This->dp8gdData.remotePlayers;

    while (cur)
    {
        if (cur->dwPlayerID == dpnid)
        {
            *pAddress = cur->pAddress;
            IDirectPlay8Address_AddRef(*pAddress);
            return S_OK;
        }
        cur = cur->next;
    }

    /* we don't check local player, this is correct. local player isn't checked */
    return DPNERR_INVALIDPLAYER;
}


HRESULT WINAPI DirectPlay8Peer_GetLocalHostAddresses(PDIRECTPLAY8PEER iface,
                                                     IDirectPlay8Address **const prgpAddress,
                                                     DWORD *const pcAddress,
                                                     const DWORD dwFlags)
{
    /*ICOM_THIS(IDirectPlay8PeerImpl, iface);*/
    FIXME("(%p)->(%p, %p, 0x%08lx): semi-stub\n", iface, prgpAddress, pcAddress, dwFlags);

    return DPNERR_NOTHOST;
}

HRESULT WINAPI DirectPlay8Peer_Close(PDIRECTPLAY8PEER iface, const DWORD dwFlags)
{
    FIXME("(%p)->(0x%08lx)\n", iface, dwFlags);
    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_EnumHosts(PDIRECTPLAY8PEER iface, PDPN_APPLICATION_DESC const pApplicationDesc,
                                         IDirectPlay8Address *const pAddrHost, IDirectPlay8Address *const pDeviceInfo,
                                         PVOID const pUserEnumData, const DWORD dwUserEnumDataSize,
                                         const DWORD dwEnumCount, const DWORD dwRetryInterval,
                                         const DWORD dwTimeOut, PVOID const pvUserContext, DPNHANDLE *const pAsyncHandle,
                                         const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);

    TRACE("(%p)->(%p, %p, %p, %p, %li, %li, %li, %li, %p, %p, 0x%08lx)\n", iface, pApplicationDesc, pAddrHost,
          pDeviceInfo, pUserEnumData, dwUserEnumDataSize, dwEnumCount, dwRetryInterval, dwTimeOut,
          pvUserContext, pAsyncHandle, dwFlags);
    return DirectPlay8Communicate_EnumHosts(&This->dp8gdData, pApplicationDesc, pAddrHost,
                                            pDeviceInfo, pUserEnumData, dwUserEnumDataSize, dwEnumCount,
                                            dwRetryInterval, dwTimeOut, pvUserContext, pAsyncHandle, dwFlags);
}

HRESULT WINAPI DirectPlay8Peer_CancelAsyncOperation(PDIRECTPLAY8PEER iface, const DPNHANDLE hAsyncHandle,
                                                    const DWORD dwFlags)
{
    /*ICOM_THIS(IDirectPlay8PeerImpl, iface);*/
    FIXME("(%p)->(0x%08lx, 0x%08lx): stub\n", iface, hAsyncHandle, dwFlags);
    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_Connect(PDIRECTPLAY8PEER iface,
                                       const DPN_APPLICATION_DESC *const pdnAppDesc,
                                       IDirectPlay8Address *const pHostAddr,
                                       IDirectPlay8Address *const pDeviceInfo,
                                       const DPN_SECURITY_DESC *const pdnSecurity,
                                       const DPN_SECURITY_CREDENTIALS *const pdnCredentials,
                                       const void *const pvUserConnectData,
                                       const DWORD dwUserConnectDataSize,
                                       void *const pvPlayerContext, void *const pvAsyncContext,
                                       DPNHANDLE *const phAsyncHandle, const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);

    TRACE("(%p)->(%p, %p, %p, %p, %p, %p, %li, %p, %p, %p, 0x%08lx)\n", iface, pdnAppDesc,
          pHostAddr, pDeviceInfo, pdnSecurity, pdnCredentials, pvUserConnectData,
          dwUserConnectDataSize, pvPlayerContext, pvAsyncContext, phAsyncHandle, dwFlags);
    if (pdnSecurity || pdnCredentials)
        FIXME("we do not yet support securtity stuff.. Either does dplay8 or 9\n");
    memcpy(&This->dp8gdData.guidApplication, &pdnAppDesc->guidApplication, sizeof(GUID));
    memcpy(&This->dp8gdData.guidInstance, &pdnAppDesc->guidInstance, sizeof(GUID));

    DirectPlay8Communicate_Peer_Connect(&This->dp8gdData, pdnAppDesc, pHostAddr, pDeviceInfo,
                                        pdnSecurity, pdnCredentials, pvUserConnectData,
                                        dwUserConnectDataSize, pvPlayerContext, pvAsyncContext,
                                        phAsyncHandle, dwFlags);

    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_SendTo(PDIRECTPLAY8PEER iface, const DPNID dpnid,
                                      const DPN_BUFFER_DESC *const pBufferDesc, const DWORD cBufferDesc,
                                      const DWORD dwTimeOut, void const *pvAsyncContext,
                                      DPNHANDLE *const phAsyncHandle, const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);

    TRACE("(%p)->(%li, %p, %li, %li, %p, %p, 0x%08lx)\n", iface, dpnid, pBufferDesc, cBufferDesc,
          dwTimeOut, pvAsyncContext, phAsyncHandle, dwFlags);
    if (dwFlags & DPNSEND_SYNC)
    {
        FIXME("can't send synchronously right now\n");
        return DPNERR_GENERIC;
    }
    if (dwFlags & DPNSEND_NOCOPY)
        FIXME("we always copy buffer, for now\n");
    if (dwFlags & DPNSEND_COMPLETEONPROCESS)
        FIXME("we dont support COMPLETEONPROCESS\n");
    if (dwFlags & DPNSEND_PRIORITY_HIGH || dwFlags & DPNSEND_PRIORITY_LOW)
        FIXME("we dont support priority flags\n");
    if (dwFlags & DPNSEND_NOLOOPBACK)
        FIXME("we dont support no loopback\n");
    return DirectPlay8Communicate_Peer_SendTo(&This->dp8gdData, dpnid, pBufferDesc, cBufferDesc,
                                              dwTimeOut, pvAsyncContext, phAsyncHandle, dwFlags);
}

HRESULT WINAPI DirectPlay8Peer_Host(PDIRECTPLAY8PEER iface, 
                                    const DPN_APPLICATION_DESC *const pdnAppDesc,
                                    IDirectPlay8Address **const prgpDeviceInfo,
                                    const DWORD cDeviceInfo,
                                    const DPN_SECURITY_DESC *const pdpSecurity,
                                    const DPN_SECURITY_CREDENTIALS *const pdpCredentials,
                                    VOID *const pvPlayerContext,
                                    const DWORD dwFlags)
{
    /*ICOM_THIS(IDirectPlay8PeerImpl, iface);*/

    FIXME("(%p): stub\n", iface);

    return S_OK;
}


static void DPNET_PEER_SetLocalPeerInfo(void *pvIface, void *pvPlayerInfo)
{
    PDIRECTPLAY8PEER iface = (PDIRECTPLAY8PEER) pvIface;
    DPN_PLAYER_INFO * pdpnPlayerInfo = (DPN_PLAYER_INFO *) pvPlayerInfo;

    ICOM_THIS(IDirectPlay8PeerImpl, iface);

    TRACE("(%p, %p)\n", pvIface, pvPlayerInfo);

    if (This->dp8gdData.localPlayer.set)
    {
        HeapFree(GetProcessHeap(), 0, This->dp8gdData.localPlayer.pdpnPlayerInfo.pwszName);
        HeapFree(GetProcessHeap(), 0, This->dp8gdData.localPlayer.pdpnPlayerInfo.pvData);
    }
    This->dp8gdData.localPlayer.set = TRUE;
    This->dp8gdData.localPlayer.pdpnPlayerInfo.dwSize = sizeof(This->dp8gdData.localPlayer.pdpnPlayerInfo);
    This->dp8gdData.localPlayer.pdpnPlayerInfo.dwInfoFlags = pdpnPlayerInfo->dwInfoFlags;
    This->dp8gdData.localPlayer.pdpnPlayerInfo.pwszName = DPNET_strdupW(pdpnPlayerInfo->pwszName);
    This->dp8gdData.localPlayer.pdpnPlayerInfo.pvData = HeapAlloc(GetProcessHeap(), 0,
                                                        pdpnPlayerInfo->dwDataSize);
    memcpy(This->dp8gdData.localPlayer.pdpnPlayerInfo.pvData, pdpnPlayerInfo->pvData, pdpnPlayerInfo->dwDataSize);
    This->dp8gdData.localPlayer.pdpnPlayerInfo.dwDataSize = pdpnPlayerInfo->dwDataSize;
    This->dp8gdData.localPlayer.pdpnPlayerInfo.dwPlayerFlags = DPNPLAYER_LOCAL;
    return;
}


HRESULT WINAPI DirectPlay8Peer_EnumPlayersAndGroups(PDIRECTPLAY8PEER iface,
                                                    DPNID *const prgdpnid,
                                                    DWORD *const pcdpnid,
                                                    const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    int count;
    PEER_player *cur;

    TRACE("(%p)->(%p, %p (%08li), %08lx)\n", iface, prgdpnid, pcdpnid, *pcdpnid, dwFlags);

    if (!(dwFlags & DPNENUM_PLAYERS))
    {
        FIXME("only support enumeration of players\n");
        return DPNERR_INVALIDFLAGS;
    }

    count = 1; /* local */

    cur = This->dp8gdData.remotePlayers;
    while (cur)
    {
        count++;
        cur = cur->next;
    }

    if (*pcdpnid < count)
    {
        *pcdpnid = count;
        return DPNERR_BUFFERTOOSMALL;
    }

    count = 0;
    prgdpnid[count++] = This->dp8gdData.localPlayer.dwPlayerID;
    cur = This->dp8gdData.remotePlayers;
    while (cur)
    {
        prgdpnid[count] = cur->dwPlayerID;
        count++;
        cur = cur->next;
    }

    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_SetPeerInfo(PDIRECTPLAY8PEER iface,
                                           const DPN_PLAYER_INFO *const pdpnPlayerInfo,
                                           PVOID const pvAsyncContext, DPNHANDLE *const phAsyncHandle,
                                           const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);

    TRACE("(%p)->(%p, %p, %p, 0x%08lx)\n", iface, pdpnPlayerInfo,
          pvAsyncContext, phAsyncHandle, dwFlags);

    if (pdpnPlayerInfo->dwInfoFlags != DPNINFO_NAME)
        FIXME("don't support player data yet (or no name)\n");

    if (dwFlags & DPNSETPEERINFO_SYNC)
    {
        DPNET_PEER_SetLocalPeerInfo((void *)iface, (void *)pdpnPlayerInfo);
        if (This->connected)
        {
            ERR("need to send peer info\n");
        }
        return S_OK;
    }
    if (!This->connected)
    {
        DPNET_PEER_SetLocalPeerInfo((void *)iface, (void *)pdpnPlayerInfo);
        /* FIXME: I don't get why this has to be done asyncly.. */
        return S_OK;
    }
    ERR("need to send peer info\n");
    return DPNSUCCESS_PENDING;
}

static PEER_player *DPNET_PEER_FindPlayer(PDIRECTPLAY8PEER iface, const DPNID dpnid)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    PEER_player *current = This->dp8gdData.remotePlayers;

    if (This->dp8gdData.localPlayer.dwPlayerID == dpnid) return &This->dp8gdData.localPlayer;
    while (current)
    {
        if (current->dwPlayerID == dpnid) return current;
        current = current->prev;
    }
    return NULL;
}

HRESULT WINAPI DirectPlay8Peer_GetPeerInfo(PDIRECTPLAY8PEER iface,
                                           const DPNID dpnid,
                                           DPN_PLAYER_INFO *const pdpnPlayerInfo,
                                           DWORD *const pdwSize, const DWORD dwFlags)
{
    /*ICOM_THIS(IDirectPlay8PeerImpl, iface);*/
    PEER_player *player;

    TRACE("(%p)->(%lx, %p, %p (%li), 0x%08lx)\n", iface, dpnid, pdpnPlayerInfo, pdwSize, *pdwSize, dwFlags);

    if ((player = DPNET_PEER_FindPlayer(iface, dpnid)) == NULL)
        return DPNERR_INVALIDPLAYER;
    if (*pdwSize < player->pdpnPlayerInfo.dwSize)
    {
        *pdwSize = player->pdpnPlayerInfo.dwSize;
        return DPNERR_BUFFERTOOSMALL;
    }
    memcpy(pdpnPlayerInfo, &player->pdpnPlayerInfo, player->pdpnPlayerInfo.dwSize);
    *pdwSize = player->pdpnPlayerInfo.dwSize;
    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_ReturnBuffer(PDIRECTPLAY8PEER iface, const DPNHANDLE hBufferHandle,
                                           const DWORD dwFlags)
{
    /*ICOM_THIS(IDirectPlay8PeerImpl, iface);*/

    FIXME("(%p)->(%li, 0x%08lx): stub\n", iface, hBufferHandle, dwFlags);
    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_GetPlayerContext(PDIRECTPLAY8PEER iface, const DPNID dpnid,
                                       PVOID *const ppvPlayerContext, const DWORD dwFlags)
{
    /*ICOM_THIS(IDirectPlay8PeerImpl, iface);*/
    PEER_player *player;

    TRACE("(%p)->(%li, %p, 0x%08lx)\n", iface, dpnid, ppvPlayerContext, dwFlags);
    if ((player = DPNET_PEER_FindPlayer(iface, dpnid)) == NULL)
        return DPNERR_INVALIDPLAYER;

    *ppvPlayerContext = player->pvPlayerContext;
    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_GetCaps(PDIRECTPLAY8PEER iface, DPN_CAPS *const pdpCaps,
                                       const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    TRACE("(%p)->(%p, 0x%08lx)\n", iface, pdpCaps, dwFlags);
    if (sizeof(DPN_CAPS) != pdpCaps->dwSize)
        return DPNERR_INVALIDPARAM;
    DirectPlay8Communicate_GetCaps(&This->dp8gdData, pdpCaps, dwFlags);
    return S_OK;
}

HRESULT WINAPI DirectPlay8Peer_GetSPCaps(PDIRECTPLAY8PEER iface, const GUID *const pguidSP,
                                       DPN_SP_CAPS *const pdpnSPCaps,
                                       const DWORD dwFlags)
{
    ICOM_THIS(IDirectPlay8PeerImpl, iface);
    TRACE("(%p)->(%s, %p, 0x%08lx)\n", iface, debugstr_guid(pguidSP),
          pdpnSPCaps, dwFlags);
    return DirectPlay8Communicate_GetSPCaps(&This->dp8gdData, pguidSP, pdpnSPCaps, dwFlags);
}

HRESULT DPNET_CreateDirectPlay8Peer(IUnknown *pOuter, REFIID riid, LPVOID *ppobj)
{
    IDirectPlay8PeerImpl *ipDP8P;
    HRESULT hr;
    TRACE("()\n");
    ipDP8P = (IDirectPlay8PeerImpl *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                        sizeof(IDirectPlay8PeerImpl));
    if (ipDP8P == NULL) return E_OUTOFMEMORY;
    ICOM_VTBL(ipDP8P) = &directPlay8PeerVT;
    IDirectPlay8Peer_AddRef((IDirectPlay8Peer *)ipDP8P);
    TRACE("Created new object: %p\n", ipDP8P);
    hr = IDirectPlay8Peer_QueryInterface((IDirectPlay8Peer *)ipDP8P, riid, ppobj);
    IDirectPlay8Peer_Release((IDirectPlay8Peer *)ipDP8P);
    if (SUCCEEDED(hr))
        return S_OK;
    else
        return E_NOINTERFACE;
}

static ICOM_VTABLE(IDirectPlay8Peer) directPlay8PeerVT =
{
    ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
    DirectPlay8Peer_QueryInterface,
    DirectPlay8Peer_AddRef,
    DirectPlay8Peer_Release,
    DirectPlay8Peer_Initilize,
    DirectPlay8Peer_EnumServiceProviders,
    DirectPlay8Peer_CancelAsyncOperation,
    DirectPlay8Peer_Connect,
    DirectPlay8Peer_SendTo,
    (void*)0xdead2009, /* GetSendQueueInfo */
    DirectPlay8Peer_Host, /* Host */
    (void*)0xdead200b, /* GetApplicationDesc */
    (void*)0xdead200c, /* SetApplicationDesc */
    (void*)0xdead200d, /* CreateGroup */
    (void*)0xdead200e, /* DestroyGroup */
    (void*)0xdead200f, /* AddPlayerToGroup */
    (void*)0xdead2010, /* RemovePlayerFromGroup */
    (void*)0xdead2011, /* SetGroupInfo */
    (void*)0xdead2012, /* GetGroupInfo */
    DirectPlay8Peer_EnumPlayersAndGroups,
    (void*)0xdead2014, /* EnumGroupMembers */
    DirectPlay8Peer_SetPeerInfo,
    DirectPlay8Peer_GetPeerInfo,
    DirectPlay8Peer_GetPeerAddress,
    DirectPlay8Peer_GetLocalHostAddresses,
    DirectPlay8Peer_Close,
    DirectPlay8Peer_EnumHosts,
    (void*)0xdead201b, /* DestroyPeer */
    DirectPlay8Peer_ReturnBuffer,
    DirectPlay8Peer_GetPlayerContext,
    (void*)0xdead201e, /* GetGroupContext */
    DirectPlay8Peer_GetCaps,
    (void*)0xdead2020, /* SetCaps */
    (void*)0xdead2021, /* SetSPCaps */
    DirectPlay8Peer_GetSPCaps,
    (void*)0xdead2023, /* GetConnectionInfo */
    (void*)0xdead2024, /* RegisterLobby */
    (void*)0xdead2025  /* TerminateSession */
};

