#ifndef __WINE_DPLAY8_PRIVATE_H
#define __WINE_DPLAY8_PRIVATE_H

#include "dplay8.h"
#include "dplobby8.h"
#include "dplay8sp.h"
#include "dp8threadpool.h"

#include "winsock2.h"

typedef struct IDirectPlay8ClientImpl IDirectPlay8ClientImpl;
typedef struct IDirectPlay8PeerImpl IDirectPlay8PeerImpl;
typedef struct IDirectPlay8ServerImpl IDirectPlay8ServerImpl;
typedef struct IDirectPlay8LobbiedApplicationImpl IDirectPlay8LobbiedApplicationImpl;
typedef struct IDirectPlay8LobbyClientImpl IDirectPlay8LobbyClientImpl;
typedef struct IDirectPlay8AddressImpl IDirectPlay8AddressImpl;

typedef struct IDirectPlay8ThreadPoolImpl IDirectPlay8ThreadPoolImpl;
typedef struct IDirectPlay8SP_TCPImpl IDirectPlay8SP_TCPImpl;

typedef struct tpJobQueueNode tpJobQueueNode;
typedef struct dpOutMessageToAddr dpOutMessageToAddr;

/* FIXME: alot of these structures need a few more mutexes */

struct dpOutMessageToAddr
{
    dpOutMessageToAddr *prev;
    dpOutMessageToAddr *next;

    IDirectPlay8Address *pAddrHost;
    DWORD dwFlags;
    DWORD packetID;

    /* message will be sent repadativly at dwRetryInterval intervals until either:
     *    o) dwTimeout reaches (if > 0)
     *    o) dwRetryLimit reaches (and is not infinite)
     *    o) message is deleted from queue
     */
    void *pTPRetryInfo;
    void *pTPTimeoutInfo;
    DWORD dwRetryLimit;
    DWORD dwRetryInterval;
    DWORD dwTimeout;

    DWORD dwRetries;

    DWORD dwTimeSent;

    void *pvPacketIntData; /* will be HeapFree'd */

    DWORD dwSize;
    BYTE msg[0];
};

typedef struct PEER_player PEER_player;

struct PEER_player
{
    PEER_player *prev; /* only for remote players */
    PEER_player *next; /* ditto */

    BOOL set;
    DPNID dwPlayerID;
    PVOID pvPlayerContext;
    IDirectPlay8Address *pAddress;
    DPN_PLAYER_INFO pdpnPlayerInfo; /* must be at end, expands */
};

struct DirectPlay8GlobalData
{
    PFNDPNMESSAGEHANDLER pfMessageHandler;
    PVOID pvUserContext;

    dpOutMessageToAddr *msgToAddrTail;
    DPN_CAPS dpnCaps;

    /* current service provider list */
    DPN_SERVICE_PROVIDER_INFO *pSPAvailable;
    DWORD SPAvailableCount;

    /* selected service provider */
    GUID guidSP;
    IDirectPlay8ServiceProvider *sp;

    BYTE dwPacketCount;

    /* only needs to be set if its a peer2peer connection.. */
    PEER_player localPlayer; /* local player is just a big hack right now */
    PEER_player *remotePlayers;

    /* set once connected */
    GUID guidApplication;
    GUID guidInstance; /* FIXME? */

    /* connect data. gotah store somewhere because its used async.. */
    /* FIXME need to free */
    LPVOID connect_data;
    DWORD connect_data_len;

    /* reply data, as above. */
    /* FIXME need to free */
    LPVOID reply_data;
    DWORD reply_data_len;
};

struct IDirectPlay8ClientImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8Client);
    DWORD ref;
    /* IDirectPlay8Client fields */
    void *dummy;
};

struct IDirectPlay8PeerImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8Peer);
    DWORD ref;
    /* IDirectPlay8Peer fields */
    BOOL connected;
    IDirectPlay8Address *pHostAddr;
    DirectPlay8GlobalData dp8gdData;
};

struct IDirectPlay8ServerImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8Server);
    DWORD ref;
    /* IDirectPlay8Server fields */
    void *dummy;
};

struct IDirectPlay8LobbiedApplicationImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8LobbiedApplication);
    DWORD ref;
    /* IDirectPlay8LobbiedApplicationImpl fields */
    void *dummy;
};

struct IDirectPlay8LobbyClientImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8LobbyClient);
    DWORD ref;
    /* IDirectPlay8LobbyClientImpl fields */
    void *dummy;
};

struct AddressComponent
{
    WCHAR *pwszName;
    void *lpvData;
    DWORD dwDataSize;
    DWORD dwDataType;
    struct AddressComponent *next;
};

/* a bit hacky. should be in some interface */
typedef void (*spReleaseAddressInfo)(PVOID);
/* shouldn't need to duplicate !!!! SHOULD USE URL. HACK */
typedef void (*spDuplicateAddressInfo)(IDirectPlay8Address *, PVOID);

struct IDirectPlay8AddressImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8Address);
    ICOM_VTABLE(IDirectPlay8AddressIP)* lpVtbl2;
    DWORD ref;
    /* IDriectPlay8Address / IDirectPlay8AddressIP fields */
    GUID guidSP;
    GUID guidDevice;

    struct AddressComponent *component;
    void *pvUserData;
    DWORD dwDataSize;

    HANDLE hDialogMutex; /* HACK: if we are sending to this address and the
                         * service provider wants to show a dialogbox, it needs to
                         * lock this - ensures that we don't show multiple dialog boxs
                         * at once etc.
                         * This can go once sending is windowed and in a single thread.
                         */
    spReleaseAddressInfo spDataRelease;
    spDuplicateAddressInfo spDataDuplicate;
    void *spData;
};

struct tpJobQueueNode
{
    tpJobQueueNode *prev;
    tpJobQueueNode *next;

    tpJobFunc func;
    void *arg1, *arg2;
};

typedef struct tpTimerQueueNode tpTimerQueueNode;

struct tpTimerQueueNode
{
    tpTimerQueueNode *prev;
    tpTimerQueueNode *next;

    DPTP_timerInfo timerInfo;
    DWORD dwTimeRequested; /* milliseconds - from GetTickCount */
    LARGE_INTEGER liDueTime; /* nanoseconds */
    HANDLE waitableTimer;
    BOOL waiting;
};

struct IDirectPlay8ThreadPoolImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8ThreadPool);
    ICOM_VTABLE(IDirectPlay8ThreadPool_Internal) *lpVtbl2;
    DWORD ref;
    /* IDriectPlay8ThreadPool fields */
    DWORD dwThreadCount;
    HANDLE *hThreadArray;

    HANDLE hTimerModified;
    HANDLE hTimerMutex;
    tpTimerQueueNode *ptqTail;

    HANDLE hJobTaken;
    HANDLE hJobPosted;
    HANDLE hQueueMutex;
    tpJobQueueNode *pjqTail;
    tpJobQueueNode *pjqHead;
    tpJobQueueNode *pjqFreeTail;
    DWORD dwCurrentJobQueues;
    DWORD dwMaxJobQueues;
    DWORD dwMinJobQueues;
};


struct IDirectPlay8SP_TCPImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectPlay8ServiceProvider);
    DWORD ref;
    /* IDirectPlay8SPWinsockTCP fields */
    SOCKET socket;
    IDirectPlay8ThreadPool_Internal *threadPool;
    DirectPlay8GlobalData *dp8gdData;
    spMessageReceived msgCallback;
    DPN_SP_CAPS dpnSPCaps;
};

/* client.c */
HRESULT DPNET_CreateDirectPlay8Client(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);

/* peer.c */
HRESULT DPNET_CreateDirectPlay8Peer(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);

/* server.c */
HRESULT DPNET_CreateDirectPlay8Server(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);

/* lobbieapp.c */
HRESULT DPNET_CreateDirectPlay8LobbiedApplication(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);

/* lobbyclient.c */
HRESULT DPNET_CreateDirectPlay8LobbyClient(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);

/* address.c */
HRESULT DPNET_CreateDirectPlay8Address(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);
HRESULT DPNET_Address_SetSPData(PDIRECTPLAY8ADDRESS iface, void *data,
                                spReleaseAddressInfo spReleaseCallback,
                                spDuplicateAddressInfo spDuplicateCallback);
HRESULT DPNET_Address_GetSPData(PDIRECTPLAY8ADDRESS iface, void **data);
HRESULT DPNET_Address_GetDialogLock(PDIRECTPLAY8ADDRESS iface);
HRESULT DPNET_Address_ReleaseDialogLock(PDIRECTPLAY8ADDRESS iface);

HRESULT DPNET_GetKeyValueW(const WCHAR *url, const WCHAR *keyName,
                           WCHAR *keyValue /* [OUT] */,
                           PDWORD bufferLen /* [IN OUT] */);

/* sp_tcp.c */
HRESULT DPNET_CreateDirectPlay8SP_TCP(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);

/* threadpool.c */
HRESULT DPNET_CreateDirectPlay8ThreadPool(IUnknown *pOuter, REFIID riid, LPVOID *ppobj);

/* general.c */
WCHAR *DPNET_strndupW(WCHAR *pwszSource, int len);
WCHAR *DPNET_strdupW(WCHAR *pwszSource);
void DPNET_DirectPlay8GlobalData_Free(DirectPlay8GlobalData *dp8gdData);
HRESULT DPNET_EnumServiceProviders(DirectPlay8GlobalData *dp8gdData,
                                   const GUID *const pguidServiceProvider,
                                   const GUID *const pguidApplication,
                                   DPN_SERVICE_PROVIDER_INFO *pSPInfoBuffer,
                                   DWORD *pcbEnumData,
                                   DWORD *pcReturned,
                                   DWORD dwFlags);
HRESULT DirectPlay8Communicate_GetSPCaps(DirectPlay8GlobalData *dp8gdData,
                                         const GUID *const pguidSP,
                                         DPN_SP_CAPS *const pdpnSPCaps, DWORD dwFlags);
HRESULT DirectPlay8Communicate_GetCaps(DirectPlay8GlobalData *dp8gdData, DPN_CAPS *pdpnCaps, DWORD dwFlags);

/* communicate.c */
HRESULT DPNET_SwitchSP(DirectPlay8GlobalData *dp8gdData, const GUID *const guidNewSP);
HRESULT DirectPlay8Communicate_EnumHosts(DirectPlay8GlobalData *dp8gdData,
                                         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);
HRESULT DirectPlay8Communicate_Peer_Connect(DirectPlay8GlobalData *dp8gdData,
                                            const DPN_APPLICATION_DESC *const pdnAppDesc,
                                            IDirectPlay8Address *pHostAddr, IDirectPlay8Address *pDeviceInfo,
                                            const DPN_SECURITY_DESC * const pdnSecurity,
                                            const DPN_SECURITY_CREDENTIALS *const pdnCredentials,
                                            const void *const pvUserConnectData,
                                            DWORD dwUserConnectDataSize,
                                            void *pvPlayerContext, void *pvAsyncContext,
                                            DPNHANDLE *phAsyncHandle, DWORD dwFlags);
HRESULT DirectPlay8Communicate_SendPeerInfo(DirectPlay8GlobalData *dp8gdData, IDirectPlay8Address *pAddr);
HRESULT DirectPlay8Communicate_Peer_SendTo(DirectPlay8GlobalData *dp8gdData, const DPNID dpnid,
                                           const DPN_BUFFER_DESC *const pBufferDesc,
                                           const DWORD cBufferDesc, const DWORD dwTimeOut,
                                           void const *pvAsyncContext,
                                           DPNHANDLE *const phAsyncHandle, const DWORD dwFlags);


/* dialog.c */
HRESULT DPNET_HostNameDialogInit(HINSTANCE hinstDLL);
HRESULT DPNET_HostNameDialogUninit();
HRESULT DPNET_HostNameShowDialog(PWCHAR *ppUserInput);
HRESULT DPNET_HostNameFree(PWCHAR pUserInput);


/* debug.c */
void debug_hexdump(void *data, unsigned long len);

#endif

