#include <assert.h>
#include <socket.h>

int atoport(char* service, char* proto)
{
    int port;
    long int lport;
    struct servent *serv;
    char *errpos;

    // First try to read it from services
    serv = getservbyname(service, proto);
    if (serv != NULL)
        port = serv->s_port;
    else 
    { // Not in services, maybe a number?
        lport = strtol(service,&errpos,0);
        if (errpos[0] != 0 || lport < 1 || lport > 65535)
            return -1; // Invalid port address
        port = lport; // htons call removed !!!
    }
    
    return port;
}

/* Converts ascii text to in_addr struct.    NULL is returned if the address
     can not be found. */
struct in_addr *atoaddr(char* address)
{
    struct hostent *host;
    static struct in_addr saddr;

    /* First try it as aaa.bbb.ccc.ddd. */
    saddr.s_addr = inet_addr(address);
    if (saddr.s_addr != INADDR_NONE) {
        return &saddr;
    }
    host = gethostbyname(address);
    if (host != NULL) {
        return (struct in_addr*)*host->h_addr_list;
    }
    return NULL;
}

// ------------------------------------------------------------------------------------------------

Socket::Socket()
    : s(0)
{
}

Socket::~Socket()
{
    disconnect();
}

int Socket::create(int type, short port)
{
    SOCKADDR_IN sain;
    struct hostent HostEntry;
    struct hostent* pHostEntry;
    char hostname[256];

    if (type != SOCK_DGRAM && type != SOCK_STREAM)
        return -1;
        
    s = socket(AF_INET, type, 0);
    if (s == INVALID_SOCKET)
        return sock_getlasterror();

    if (type == SOCK_DGRAM)
    {
        if (SOCKET_ERROR == gethostname(hostname, sizeof(hostname)))
            return sock_getlasterror();

        pHostEntry = gethostbyname(hostname);
        if (pHostEntry == NULL)
            return sock_getlasterror();

        HostEntry = *pHostEntry;
        sain.sin_family = AF_INET;
        memcpy(&sain.sin_addr.s_addr, HostEntry.h_addr_list[0], 4);
        sain.sin_port = htons(port);
        if (SOCKET_ERROR == ::bind(s, (const struct sockaddr*) &sain, sizeof(sain)))
            return sock_getlasterror();
    }

    return 0;
}

int Socket::bind(const char* targetAddr, short port)
{
    struct sockaddr_in address;
    
    assert(s != 0);

    struct in_addr *addr = atoaddr((char*)targetAddr);
    if (!addr) 
        return -1;

    memset((char*)&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    address.sin_addr.s_addr = addr->s_addr;

    if (::bind(s, (struct sockaddr*)&address, sizeof(address)) == SOCKET_ERROR)
        return sock_getlasterror();

    return 0;
}

int Socket::connect(const char* targetAddr, short port)
{
    struct sockaddr_in address;
    
    assert(s != 0);

    struct in_addr *addr = atoaddr((char*)targetAddr);
    if (!addr) 
        return -1;

    memset((char*)&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    address.sin_addr.s_addr = addr->s_addr;

    if (::connect(s, (struct sockaddr*)&address, sizeof(address)) == SOCKET_ERROR) 
        return sock_getlasterror();

    return 0;
}

void Socket::disconnect()
{
    //::shutdown(s, SD_BOTH);
    //StopEvents();
    if (s)
        ::closesocket(s);
    s = 0;
}

int Socket::read(char* buff, unsigned size, int flags)
{
    assert(s != 0);
    
    int bread = ::recv(s, buff, size, flags);
    return bread;
}

int Socket::write(const char *buff, unsigned size) const
{
    int written = 0;
    
    assert(s != 0);

    while (written < size) 
    {
        int r = send(s, &buff[written], size - written, 0);
        if (r == SOCKET_ERROR) {
            return sock_getlasterror();
        } else {
            written += r;
        }
    }
    
    return 0;
}

int Socket::puts(const char *str) const
{
    int r = write(str, strlen(str)) + write("\n", strlen("\n"));
    
    return r;
}

int Socket::startup()
{
#ifdef _WIN32
    WORD wVersionRequested = MAKEWORD(2, 0);
    WSADATA WSAData;
    int error = WSAStartup(wVersionRequested, &WSAData);
    return error;
#else
    return 0;
#endif
}

int Socket::shutdown()
{
#ifdef _WIN32
    return WSACleanup();
#else
    return 0;
#endif
}


