/*
        Written by Christopher E. Miller
        Placed into public domain.
*/


module std.c.windows.winsock;

private import std.stdint;
private import std.c.windows.windows;


extern(Windows):

alias int  SOCKET;
alias uint socklen_t;

const SOCKET INVALID_SOCKET = cast(SOCKET)~0;
const int SOCKET_ERROR = -1;

const int WSADESCRIPTION_LEN = 256;
const int WSASYS_STATUS_LEN = 128;

struct WSADATA
{
        WORD wVersion;
        WORD wHighVersion;
        char szDescription[WSADESCRIPTION_LEN + 1];
        char szSystemStatus[WSASYS_STATUS_LEN + 1];
        USHORT iMaxSockets;
        USHORT iMaxUdpDg;
        char* lpVendorInfo;
}
alias WSADATA* LPWSADATA;


const int IOCPARM_MASK =  0x7F;
const int IOC_IN =        cast(int)0x80000000;
const int FIONBIO =       cast(int)(IOC_IN | ((UINT.sizeof & IOCPARM_MASK) << 16) | (102 << 8) | 126);


int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
int WSACleanup();
SOCKET socket(int af, int type, int protocol);
int ioctlsocket(SOCKET s, int cmd, uint* argp);
int bind(SOCKET s, sockaddr* name, socklen_t namelen);
int connect(SOCKET s, sockaddr* name, socklen_t namelen);
int listen(SOCKET s, int backlog);
SOCKET accept(SOCKET s, sockaddr* addr, socklen_t* addrlen);
int closesocket(SOCKET s);
int shutdown(SOCKET s, int how);
int getpeername(SOCKET s, sockaddr* name, socklen_t* namelen);
int getsockname(SOCKET s, sockaddr* name, socklen_t* namelen);
int send(SOCKET s, const(void)* buf, int len, int flags);
int sendto(SOCKET s, const(void)* buf, int len, int flags, sockaddr* to, socklen_t tolen);
int recv(SOCKET s, void* buf, int len, int flags);
int recvfrom(SOCKET s, void* buf, int len, int flags, sockaddr* from, socklen_t* fromlen);
int getsockopt(SOCKET s, int level, int optname, void* optval, socklen_t* optlen);
int setsockopt(SOCKET s, int level, int optname, void* optval, socklen_t optlen);
uint inet_addr(const char* cp);
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, timeval* timeout);
char* inet_ntoa(in_addr ina);
hostent* gethostbyname(const char* name);
hostent* gethostbyaddr(void* addr, int len, int type);
protoent* getprotobyname(const char* name);
protoent* getprotobynumber(int number);
servent* getservbyname(const char* name, const char* proto);
servent* getservbyport(int port, const char* proto);
int gethostname(const char* name, int namelen);
int getaddrinfo(char* nodename, char* servname, addrinfo* hints, addrinfo** res);
void freeaddrinfo(addrinfo* ai);
int getnameinfo(sockaddr* sa, socklen_t salen, char* host, DWORD hostlen, char* serv, DWORD servlen, int flags);

enum: int
{
        WSAEWOULDBLOCK =     10035,
        WSAEINTR =           10004,
        WSAHOST_NOT_FOUND =  11001,
}

int WSAGetLastError();


enum: int
{
        AF_UNSPEC =     0,

        AF_UNIX =       1,
        AF_INET =       2,
        AF_IMPLINK =    3,
        AF_PUP =        4,
        AF_CHAOS =      5,
        AF_NS =         6,
        AF_IPX =        AF_NS,
        AF_ISO =        7,
        AF_OSI =        AF_ISO,
        AF_ECMA =       8,
        AF_DATAKIT =    9,
        AF_CCITT =      10,
        AF_SNA =        11,
        AF_DECnet =     12,
        AF_DLI =        13,
        AF_LAT =        14,
        AF_HYLINK =     15,
        AF_APPLETALK =  16,
        AF_NETBIOS =    17,
        AF_VOICEVIEW =  18,
        AF_FIREFOX =    19,
        AF_UNKNOWN1 =   20,
        AF_BAN =        21,
        AF_ATM =        22,
        AF_INET6 =      23,
        AF_CLUSTER =    24,
        AF_12844 =      25,
        AF_IRDA =       26,
        AF_NETDES =     28,

        AF_MAX =        29,


        PF_UNSPEC     = AF_UNSPEC,

        PF_UNIX =       AF_UNIX,
        PF_INET =       AF_INET,
        PF_IMPLINK =    AF_IMPLINK,
        PF_PUP =        AF_PUP,
        PF_CHAOS =      AF_CHAOS,
        PF_NS =         AF_NS,
        PF_IPX =        AF_IPX,
        PF_ISO =        AF_ISO,
        PF_OSI =        AF_OSI,
        PF_ECMA =       AF_ECMA,
        PF_DATAKIT =    AF_DATAKIT,
        PF_CCITT =      AF_CCITT,
        PF_SNA =        AF_SNA,
        PF_DECnet =     AF_DECnet,
        PF_DLI =        AF_DLI,
        PF_LAT =        AF_LAT,
        PF_HYLINK =     AF_HYLINK,
        PF_APPLETALK =  AF_APPLETALK,
        PF_VOICEVIEW =  AF_VOICEVIEW,
        PF_FIREFOX =    AF_FIREFOX,
        PF_UNKNOWN1 =   AF_UNKNOWN1,
        PF_BAN =        AF_BAN,
        PF_INET6 =      AF_INET6,

        PF_MAX        = AF_MAX,
}


enum: int
{
        SOL_SOCKET = 0xFFFF,
}


enum: int
{
        SO_DEBUG =        0x0001,
        SO_ACCEPTCONN =   0x0002,
        SO_REUSEADDR =    0x0004,
        SO_KEEPALIVE =    0x0008,
        SO_DONTROUTE =    0x0010,
        SO_BROADCAST =    0x0020,
        SO_USELOOPBACK =  0x0040,
        SO_LINGER =       0x0080,
        SO_DONTLINGER =   ~SO_LINGER,
        SO_OOBINLINE =    0x0100,
        SO_SNDBUF =       0x1001,
        SO_RCVBUF =       0x1002,
        SO_SNDLOWAT =     0x1003,
        SO_RCVLOWAT =     0x1004,
        SO_SNDTIMEO =     0x1005,
        SO_RCVTIMEO =     0x1006,
        SO_ERROR =        0x1007,
        SO_TYPE =         0x1008,
        SO_EXCLUSIVEADDRUSE = ~SO_REUSEADDR,

        TCP_NODELAY =    1,

        IP_MULTICAST_LOOP =  0x4,
        IP_ADD_MEMBERSHIP =  0x5,
        IP_DROP_MEMBERSHIP = 0x6,

        IPV6_UNICAST_HOPS =    4,
        IPV6_MULTICAST_IF =    9,
        IPV6_MULTICAST_HOPS =  10,
        IPV6_MULTICAST_LOOP =  11,
        IPV6_ADD_MEMBERSHIP =  12,
        IPV6_DROP_MEMBERSHIP = 13,
        IPV6_JOIN_GROUP =      IPV6_ADD_MEMBERSHIP,
        IPV6_LEAVE_GROUP =     IPV6_DROP_MEMBERSHIP,
}


const uint FD_SETSIZE = 64;


struct fd_set
{
        UINT fd_count;
        SOCKET[FD_SETSIZE] fd_array;
}


// Removes.
void FD_CLR(SOCKET fd, fd_set* set)
{
        uint c = set.fd_count;
        SOCKET* start = set.fd_array.ptr;
        SOCKET* stop = start + c;

        for(; start != stop; start++)
        {
                if(*start == fd)
                        goto found;
        }
        return; //not found

        found:
        for(++start; start != stop; start++)
        {
                *(start - 1) = *start;
        }

        set.fd_count = c - 1;
}


// Tests.
int FD_ISSET(SOCKET fd, fd_set* set)
{
        SOCKET* start = set.fd_array.ptr;
        SOCKET* stop = start + set.fd_count;

        for(; start != stop; start++)
        {
                if(*start == fd)
                        return true;
        }
        return false;
}


// Adds.
void FD_SET(SOCKET fd, fd_set* set)
{
        uint c = set.fd_count;
        set.fd_array.ptr[c] = fd;
        set.fd_count = c + 1;
}


// Resets to zero.
void FD_ZERO(fd_set* set)
{
        set.fd_count = 0;
}


struct linger
{
        USHORT l_onoff;
        USHORT l_linger;
}


struct protoent
{
        char* p_name;
        char** p_aliases;
        SHORT p_proto;
}


struct servent
{
        char* s_name;
        char** s_aliases;
        SHORT s_port;
        char* s_proto;
}


/+
union in6_addr
{
        private union _u_t
        {
                BYTE[16] Byte;
                WORD[8] Word;
        }
        _u_t u;
}


struct in_addr6
{
        BYTE[16] s6_addr;
}
+/


version(BigEndian)
{
        uint16_t htons(uint16_t x)
        {
                return x;
        }


        uint32_t htonl(uint32_t x)
        {
                return x;
        }
}
else version(LittleEndian)
{
        private import core.bitop;


        uint16_t htons(uint16_t x)
        {
                return cast(uint16_t)((x >> 8) | (x << 8));
        }


        uint32_t htonl(uint32_t x)
        {
                return bswap(x);
        }
}
else
{
        static assert(0);
}


uint16_t ntohs(uint16_t x)
{
        return htons(x);
}


uint32_t ntohl(uint32_t x)
{
        return htonl(x);
}


enum: int
{
        SOCK_STREAM =     1,
        SOCK_DGRAM =      2,
        SOCK_RAW =        3,
        SOCK_RDM =        4,
        SOCK_SEQPACKET =  5,
}


enum: int
{
        IPPROTO_IP =    0,
        IPPROTO_ICMP =  1,
        IPPROTO_IGMP =  2,
        IPPROTO_GGP =   3,
        IPPROTO_TCP =   6,
        IPPROTO_PUP =   12,
        IPPROTO_UDP =   17,
        IPPROTO_IDP =   22,
        IPPROTO_IPV6 =  41,
        IPPROTO_ND =    77,
        IPPROTO_RAW =   255,

        IPPROTO_MAX =   256,
}


enum: int
{
        MSG_OOB =        0x1,
        MSG_PEEK =       0x2,
        MSG_DONTROUTE =  0x4,
        MSG_NOSIGNAL =   0x0, /// not supported on win32, would be 0x4000 if it was
}


enum: int
{
        SD_RECEIVE =  0,
        SD_SEND =     1,
        SD_BOTH =     2,
}


enum: uint
{
        INADDR_ANY =        0,
        INADDR_LOOPBACK =   0x7F000001,
        INADDR_BROADCAST =  0xFFFFFFFF,
        INADDR_NONE =       0xFFFFFFFF,
        ADDR_ANY =          INADDR_ANY,
}


enum: int
{
        AI_PASSIVE = 0x1,
        AI_CANONNAME = 0x2,
        AI_NUMERICHOST = 0x4,
}


struct timeval
{
        int32_t tv_sec;
        int32_t tv_usec;
}


union in_addr
{
        private union _S_un_t
        {
                private struct _S_un_b_t
                {
                        uint8_t s_b1, s_b2, s_b3, s_b4;
                }
                _S_un_b_t S_un_b;

                private struct _S_un_w_t
                {
                        uint16_t s_w1, s_w2;
                }
                _S_un_w_t S_un_w;

                uint32_t S_addr;
        }
        _S_un_t S_un;

        uint32_t s_addr;

        struct
        {
                uint8_t s_net, s_host;

                union
                {
                        uint16_t s_imp;

                        struct
                        {
                                uint8_t s_lh, s_impno;
                        }
                }
        }
}


union in6_addr
{
        private union _in6_u_t
        {
                uint8_t[16] u6_addr8;
                uint16_t[8] u6_addr16;
                uint32_t[4] u6_addr32;
        }
        _in6_u_t in6_u;

        uint8_t[16] s6_addr8;
        uint16_t[8] s6_addr16;
        uint32_t[4] s6_addr32;

        alias s6_addr8 s6_addr;
}


const in6_addr IN6ADDR_ANY = { s6_addr8: [0] };
const in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] };
//alias IN6ADDR_ANY IN6ADDR_ANY_INIT;
//alias IN6ADDR_LOOPBACK IN6ADDR_LOOPBACK_INIT;

const uint INET_ADDRSTRLEN = 16;
const uint INET6_ADDRSTRLEN = 46;


struct sockaddr
{
        int16_t sa_family;
        ubyte[14] sa_data;
}


struct sockaddr_in
{
        int16_t sin_family = AF_INET;
        uint16_t sin_port;
        in_addr sin_addr;
        ubyte[8] sin_zero;
}


struct sockaddr_in6
{
        int16_t sin6_family = AF_INET6;
        uint16_t sin6_port;
        uint32_t sin6_flowinfo;
        in6_addr sin6_addr;
        uint32_t sin6_scope_id;
}


struct addrinfo
{
        int32_t ai_flags;
        int32_t ai_family;
        int32_t ai_socktype;
        int32_t ai_protocol;
        size_t ai_addrlen;
        char* ai_canonname;
        sockaddr* ai_addr;
        addrinfo* ai_next;
}


struct hostent
{
        char* h_name;
        char** h_aliases;
        int16_t h_addrtype;
        int16_t h_length;
        char** h_addr_list;


        char* h_addr()
        {
                return h_addr_list[0];
        }
}

