/*****************************************************************************/
/* Sockets.h                              Copyright (c) Ladislav Zezula 2021 */
/*---------------------------------------------------------------------------*/
/* MIME parsing functions for CascLib                                        */
/*---------------------------------------------------------------------------*/
/*   Date    Ver   Who  Comment                                              */
/* --------  ----  ---  -------                                              */
/* 13.02.21  1.00  Lad  Created                                              */
/*****************************************************************************/

#ifndef __SOCKET_H__
#define __SOCKET_H__

//-----------------------------------------------------------------------------
// Defines

#ifndef SOCKET_ERROR
#define SOCKET_ERROR   (-1)
#endif

#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

#ifndef WSAECONNRESET
#define WSAECONNRESET       10054L
#endif

#ifndef EPIPE
#define EPIPE               32
#endif

#define CASC_PORT_HTTP      80
#define CASC_PORT_RIBBIT    1119

//-----------------------------------------------------------------------------
// The CASC_SOCKET class

typedef class CASC_SOCKET_CACHE * PCASC_SOCKET_CACHE;
typedef class CASC_SOCKET * PCASC_SOCKET;
typedef struct addrinfo * PADDRINFO;

class CASC_SOCKET
{
    public:

    char * ReadResponse(const char * request, size_t request_length, CASC_MIME_RESPONSE & MimeResponse);
    DWORD AddRef();
    void Release();

    private:

    // Constructor and destructor
    static int GetSockError();
    static DWORD GetAddrInfoWrapper(const char * hostName, unsigned portNum, PADDRINFO hints, PADDRINFO * ppResult);
    static HANDLE CreateAndConnect(PADDRINFO remoteItem);
    static HANDLE ReconnectAfterShutdown(HANDLE & sock, PADDRINFO remoteItem);
    static PCASC_SOCKET New(PADDRINFO remoteList, PADDRINFO remoteItem, const char * hostName, unsigned portNum, HANDLE sock);
    static PCASC_SOCKET Connect(const char * hostName, unsigned portNum);

    // Frees all resources and deletes the socket
    void Delete();

    // Entities allowed to manipulate with the class
    friend CASC_SOCKET * sockets_connect(const char * hostName, unsigned portNum);
    friend char * sockets_read_response(PCASC_SOCKET pSocket, const char * request, size_t request_length, size_t * PtrLength);
    friend class CASC_SOCKET_CACHE;

    PCASC_SOCKET_CACHE pCache;          // Pointer to the cache. If NULL, the socket is not cached
    PCASC_SOCKET pPrev;                 // Pointer to the prev socket in the list
    PCASC_SOCKET pNext;                 // Pointer to the next socket in the list
    PADDRINFO remoteList;               // List of the remote host informations
    PADDRINFO remoteItem;               // The particular host picked during the last connection attempt
    CASC_LOCK Lock;                     // Lock for single threaded access
    HANDLE sock;                        // Opened and connected socket
    DWORD dwRefCount;                   // Number of references
    DWORD portNum;                      // Port number
    char hostName[1];                   // Buffer for storing remote host (variable length)
};

//-----------------------------------------------------------------------------
// Socket cache class

class CASC_SOCKET_CACHE
{
    public:

    CASC_SOCKET_CACHE();
    ~CASC_SOCKET_CACHE();

    PCASC_SOCKET Find(const char * hostName, unsigned portNum);
    PCASC_SOCKET InsertSocket(PCASC_SOCKET pSocket);
    void UnlinkSocket(PCASC_SOCKET pSocket);

    void SetCaching(bool bAddRef);
    void PurgeAll();

    private:

    PCASC_SOCKET pFirst;
    PCASC_SOCKET pLast;
    DWORD dwRefCount;
};

//-----------------------------------------------------------------------------
// Public functions

PCASC_SOCKET sockets_connect(const char * hostName, unsigned portNum);
void sockets_set_caching(bool caching);

#endif  // __SOCKET_H__
