/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "plNet.h"

////////////////////////////////////////////////////

plNet   plNet::_;

////////////////////////////////////////////////////
// Windows socket interface
#if HS_BUILD_FOR_WIN32

plNet::plNet()
{        
    static struct WSAData wsa;
    WSAStartup(0x0101, &wsa);
}

plNet::~plNet()
{
    WSACleanup();
}

SOCKET plNet::NewUDP()
{
    return ::socket(AF_INET, SOCK_DGRAM, 0);
}

SOCKET plNet::NewTCP()
{
    SOCKET x = ::socket(AF_INET, SOCK_STREAM, 0);
    unsigned int timeoutval;
    timeoutval = kDefaultSocketTimeout;
    setsockopt(x, SOL_SOCKET, (int)SO_RCVTIMEO,(const char*)&timeoutval,sizeof(timeoutval));
    timeoutval = kDefaultSocketTimeout;
    setsockopt(x, SOL_SOCKET, (int)SO_SNDTIMEO,(const char*)&timeoutval,sizeof(timeoutval));
    return x;
}

int plNet::GetError()
{
    return WSAGetLastError();
}

int plNet::Read(const SOCKET sck, char * buf, const int size)
{
    return ::recv(sck,buf,size,0);
}

int plNet::Write(const SOCKET sck, const char * buf, const int len)
{
    return ::send(sck,buf,len,0);
}

int plNet::ReadFrom(const SOCKET sck, char * buf, int len, sockaddr_in * addr)
{
    int addrlen = sizeof(sockaddr);
    return ::recvfrom(sck,buf,len,0,reinterpret_cast<sockaddr*>(addr),&addrlen);
}

int plNet::WriteTo(const SOCKET sck, const char * buf, const int len, sockaddr_in * addr)
{
    return ::sendto(sck,buf,len,0,reinterpret_cast<const sockaddr*>(addr),sizeof(sockaddr));
}

int plNet::Connect(const SOCKET sck, const sockaddr_in * addr)
{
    return ::connect(sck, reinterpret_cast<const sockaddr*>(addr), sizeof(sockaddr));
}

int plNet::Close(const SOCKET sck)
{
    return ::closesocket(sck);
}

int plNet::Bind(const SOCKET sck, const sockaddr_in * addr)
{
    return ::bind(sck,reinterpret_cast<const sockaddr*>(addr),sizeof(sockaddr));
}

int plNet::Listen(const SOCKET sck, const int qsize)
{
    return ::listen(sck,qsize);
}

int plNet::Accept(const SOCKET sck, sockaddr_in * addr)
{
    int addrlen = sizeof(sockaddr);
    return ::accept(sck,reinterpret_cast<sockaddr*>(addr),&addrlen);
}

int plNet::Ioctl(const SOCKET sck, const long flags, unsigned long * val)
{
    return ::ioctlsocket(sck,flags,val);
}

// static
const char * plNet::GetErrorMsg(int error)
{
    switch(error)
    {
        case WSAEINTR:              return "Interrupted system call"; break;
        case WSAEBADF:              return "Bad file number"; break;
        case WSAEACCES:             return "Permission denied"; break;
        case WSAEFAULT:             return "Bad address"; break;
        case WSAEINVAL:             return "Invalid argument"; break;
        case WSAEMFILE:             return "Too many open sockets"; break;
        case WSAEWOULDBLOCK:        return "Operation would block"; break;
        case WSAEINPROGRESS:        return "Operation now in progress"; break;
        case WSAEALREADY:           return "Operation already in progress"; break;
        case WSAENOTSOCK:           return "Socket operation on non-socket"; break;
        case WSAEDESTADDRREQ:       return "Destination address required"; break;
        case WSAEMSGSIZE:           return "Message too long"; break;
        case WSAEPROTOTYPE:         return "Protocol wrong type for socket"; break;
        case WSAENOPROTOOPT:        return "Bad protocol option"; break;
        case WSAEPROTONOSUPPORT:    return "Protocol not supported"; break;
        case WSAESOCKTNOSUPPORT:    return "Socket type not supported"; break;
        case WSAEOPNOTSUPP:         return "Operation not supported on socket"; break;
        case WSAEPFNOSUPPORT:       return "Protocol family not supported"; break;
        case WSAEAFNOSUPPORT:       return "Address family not supported"; break;
        case WSAEADDRINUSE:         return "Address already in use"; break;
        case WSAEADDRNOTAVAIL:      return "Can't assign requested address"; break;
        case WSAENETDOWN:           return "Network is down"; break;
        case WSAENETUNREACH:        return "Network is unreachable"; break;
        case WSAENETRESET:          return "Net connection reset"; break;
        case WSAECONNABORTED:       return "Software caused connection abort"; break;
        case WSAECONNRESET:         return "Connection reset by peer"; break;
        case WSAENOBUFS:            return "No buffer space available"; break;
        case WSAEISCONN:            return "Socket is already connected"; break;
        case WSAENOTCONN:           return "Socket is not connected"; break;
        case WSAESHUTDOWN:          return "Can't send after socket shutdown"; break;
        case WSAETOOMANYREFS:       return "Too many references, can't splice"; break;
        case WSAETIMEDOUT:          return "Connection timed out"; break;
        case WSAECONNREFUSED:       return "Connection refused"; break;
        case WSAELOOP:              return "Too many levels of symbolic links"; break;
        case WSAENAMETOOLONG:       return "File name too long"; break;
        case WSAEHOSTDOWN:          return "Host is down"; break;
        case WSAEHOSTUNREACH:       return "No route to host"; break;
        case WSAENOTEMPTY:          return "Directory not empty"; break;
        case WSAEPROCLIM:           return "Too many processes"; break;
        case WSAEUSERS:             return "Too many users"; break;
        case WSAEDQUOT:             return "Disc quota exceeded"; break;
        case WSAESTALE:             return "Stale NFS file handle"; break;
        case WSAEREMOTE:            return "Too many levels of remote in path"; break;
        case WSASYSNOTREADY:        return "Network subsystem is unavailable"; break;
        case WSAVERNOTSUPPORTED:    return "Winsock version not supported"; break;
        case WSANOTINITIALISED:     return "Winsock not yet initialized"; break;
        case WSAHOST_NOT_FOUND:     return "Host not found"; break;
        case WSATRY_AGAIN:          return "Non-authoritative host not found"; break;
        case WSANO_RECOVERY:        return "Non-recoverable errors"; break;
        case WSANO_DATA:            return "Valid name, no data record of requested type"; break;
        case WSAEDISCON:            return "Graceful disconnect in progress"; break;
        case WSASYSCALLFAILURE:     return "System call failure"; break;
        case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available"; break;
        case WSA_OPERATION_ABORTED: return "Overlapped operation aborted"; break;
        case WSA_IO_INCOMPLETE:      return "Overlapped I/O object not signalled"; break;
        case WSA_IO_PENDING:        return "Overlapped I/O will complete later"; break;
        //case WSAINVALIDPROCTABLE:   return "Invalid proc. table from service provider"; break;
        //case WSAINVALIDPROVIDER:    return "Invalid service provider version number"; break;
        //case WSAPROVIDERFAILEDINIT, return "Unable to init service provider"; break;
        case WSA_INVALID_PARAMETER: return "One or more parameters are invalid"; break;
        case WSA_INVALID_HANDLE:    return "Event object handle not valid"; break;
    };
    return "\0";
}

////////////////////////////////////////////////////
// UNIX socket interface
#elif HS_BUILD_FOR_UNIX

#include <unistd.h>

plNet::plNet()
{ }

plNet::~plNet()
{ }

SOCKET plNet::NewUDP()
{
    return ::socket(AF_INET, SOCK_DGRAM, 0);
}

SOCKET plNet::NewTCP()
{
    SOCKET x = ::socket(AF_INET, SOCK_STREAM, 0);
    unsigned int timeoutval;
    timeoutval = kDefaultSocketTimeout;
    setsockopt(x, SOL_SOCKET, (int)SO_RCVTIMEO,(const char*)&timeoutval,sizeof(timeoutval));
    timeoutval = kDefaultSocketTimeout;
    setsockopt(x, SOL_SOCKET, (int)SO_SNDTIMEO,(const char*)&timeoutval,sizeof(timeoutval));
    return x;
}

int plNet::GetError()
{
    return errno;
}

int plNet::Read(const SOCKET sck, char * buf, const int size)
{
    return ::recv(sck,buf,size,0);
}

int plNet::Write(const SOCKET sck, const char * buf, const int len)
{
    return ::send(sck,buf,len,0);
}

int plNet::ReadFrom(const SOCKET sck, char * buf, int len, sockaddr_in * addr)
{
    unsigned addrlen = sizeof(sockaddr);
    return ::recvfrom(sck,buf,len,0,reinterpret_cast<sockaddr*>(addr),&addrlen);
}

int plNet::WriteTo(const SOCKET sck, const char * buf, const int len, sockaddr_in * addr)
{
    return ::sendto(sck,buf,len,0,reinterpret_cast<const sockaddr*>(addr),sizeof(sockaddr));
}

int plNet::Connect(const SOCKET sck, const sockaddr_in * addr)
{
    return ::connect(sck, reinterpret_cast<const sockaddr*>(addr), sizeof(sockaddr));
}

int plNet::Close(const SOCKET sck)
{
    return ::close(sck);
}

int plNet::Bind(const SOCKET sck, const sockaddr_in * addr)
{
    return ::bind(sck,reinterpret_cast<const sockaddr*>(addr),sizeof(sockaddr));
}

int plNet::Listen(const SOCKET sck, const int qsize)
{
    return ::listen(sck,qsize);
}

int plNet::Accept(const SOCKET sck, sockaddr_in * addr)
{
    unsigned addrlen = sizeof(sockaddr);
    return ::accept(sck,reinterpret_cast<sockaddr*>(addr),&addrlen);
}

int plNet::Ioctl(const SOCKET sck, const long flags, unsigned long * val)
{
    return ::ioctl(sck,flags,val);
}

#else
#error "plNet: Sockets not ported"
#endif

