#include "SocketModule.h"
#include <stdio.h>

TCPSocket::TCPSocket(): m_sockfd(-1){}
TCPSocket::~TCPSocket()
{
    if ( m_sockfd != -1 ){
#ifdef WIN32
        closesocket(m_sockfd);
#else
        ::close(m_sockfd);
#endif
    }

}

bool TCPSocket::create()
{
    close();
    if ((m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
        return false;
    return true;
}

bool TCPSocket::bind(unsigned short int port, const char *ip) const
{
    if ( m_sockfd == -1 )
        return false;

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if (ip == NULL)
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
    else
        addr.sin_addr.s_addr = inet_addr(ip);
    if ( ::bind(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1 )
        return false;
    return true;
}
bool TCPSocket::listen(int backlog) const
{
    if ( m_sockfd == -1 )
        return false;

    if ( ::listen(m_sockfd, backlog) == -1)
        return false;
    return true;
}
bool TCPSocket::accept(TCPSocket &clientSocket) const
{
    if ( m_sockfd == -1 )
        return false;

    clientSocket.m_sockfd =
        ::accept(this->m_sockfd, NULL, NULL);
    if (clientSocket.m_sockfd == -1)
        return false;
    return true;
}

bool TCPSocket::connect(unsigned short int port, const char *ip) const
{
    if ( m_sockfd == -1 )
        return false;

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip);
    if ( ::connect(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
        return false;
    return true;
}

bool TCPSocket::setNonBlocking(bool flag) const
{
    if ( m_sockfd == -1 )
        return false;
#ifdef WIN32
#else
    int opt = fcntl(m_sockfd, F_GETFL, 0);
    if (opt == -1)
        return false;
    if (flag)
        opt |= O_NONBLOCK;
    else
        opt &= ~O_NONBLOCK;
    if (fcntl(m_sockfd, F_SETFL, opt) == -1)
        return false;
#endif
    return true;
}
bool TCPSocket::reuseaddr() const
{
    if ( m_sockfd == -1 )
        return false;

    int on = 1;
    if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) == -1)
        return false;
    return true;
}
bool TCPSocket::close()
{
    if ( m_sockfd == -1 )
        return false;
#ifdef WIN32
        closesocket(m_sockfd);
#else
        ::close(m_sockfd);
#endif
    m_sockfd = -1;
    return true;
}

/** Server TCP Socket**/
bool TCPServer::doListen(unsigned short int port, const char *ip, int backlog)
{
    if ( create() )
        if ( reuseaddr() )
            if ( bind(port, ip) )
                if ( listen(backlog) )
                {
                    return true;
                }

    return false;
}

bool TCPServer::accept(TCPSocket &clientSocket) const
{
    return TCPSocket::accept(clientSocket);
}

/** client端特有的send/receive **/
static ssize_t readn(int fd, void *buf, size_t count);
static ssize_t writen(int fd, const void *buf, size_t count);

/** client TCP Socket **/
bool TCPClient::doConnect(unsigned short port, const char *ip)
{
    if( create() )
    {
        if( connect(port, ip) )
        {
            m_bValid = true;
            return true;
        }
    }
    return false;
}

void TCPClient::setSocketTimeout(int timeout)
{
#ifdef WIN32
    int iTimeOut = timeout * 1000;
    setsockopt(m_sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&iTimeOut, sizeof(iTimeOut));
#else
    struct timeval vtime;
    vtime.tv_sec = timeout;
    vtime.tv_usec = 0;
    setsockopt(m_sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&vtime, sizeof(struct timeval));
#endif // WIN32
}

//send
bool TCPClient::send(const std::string& message)
{
    Packet buf;
    buf.msgLen = htonl(message.length());
    strcpy(buf.text, message.c_str());
    if (writen(m_sockfd, &buf, sizeof(buf.msgLen)+message.length()) == -1)
    {
        m_bValid = false;
        return false;
    }
    return true;
}
bool TCPClient::receive(std::string &message)
{
    //首先读取头部
    Packet buf = {0, 0};
    size_t readBytes = readn(m_sockfd, &buf.msgLen, sizeof(buf.msgLen));
    if (readBytes == (size_t)-1)
    {
        m_bValid = false;
        return false;
    }
    else if (readBytes != sizeof(buf.msgLen))
    {
        m_bValid = false;
        return false;
    }

    //然后读取数据部分
    unsigned int lenHost = ntohl(buf.msgLen);
    readBytes = readn(m_sockfd, buf.text, lenHost);
    if (readBytes == (size_t)-1)
    {
        m_bValid = false;
        return false;
    }
    else if (readBytes != lenHost)
    {
        m_bValid = false;
        return false;
    }
    message = buf.text;
    return true;
}

bool TCPClient::read(void *buf, size_t count)
{
#ifdef WIN32
    ssize_t readBytes= ::recv(m_sockfd, (char*)buf, count,0);
#else
    ssize_t readBytes = ::read(m_sockfd, buf, count);
#endif

    if (readBytes == -1)
    {
        m_bValid = false;
        return false;
    }
    return true;
}

bool TCPClient::write(const char *msg)
{
#ifdef WIN32
    ssize_t writeBytes= ::send(m_sockfd, msg, strlen(msg),0);
#else
    ssize_t writeBytes= ::write(m_sockfd, msg, strlen(msg));
#endif
    if( writeBytes == -1 )
    {
        m_bValid = false;
        return false;
    }
    return true;
}


/** readn/writen实现部分 **/
static ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nLeft = count;
    ssize_t nRead = 0;
    char *pBuf = (char *)buf;
    while (nLeft > 0)
    {
#ifdef WIN32
        nRead= ::recv(fd, (char*)pBuf, nLeft,0);
#else
        nRead = ::read(fd, pBuf, nLeft);
#endif
        if (nRead < 0)
        {
            //如果读取操作是被信号打断了, 则说明还可以继续读
            if (errno == EINTR)
                continue;
            //否则就是其他错误
            else
                return -1;
        }
        //读取到末尾
        else if (nRead == 0)
            return count-nLeft;

        //正常读取
        nLeft -= nRead;
        pBuf += nRead;
    }
    return count;
}
static ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nLeft = count;
    ssize_t nWritten = 0;
    char *pBuf = (char *)buf;
    while (nLeft > 0)
    {
#ifdef WIN32
        nWritten= ::send(fd, pBuf, nLeft,0);
#else
        nWritten= ::write(fd, pBuf, nLeft);
#endif
        if (nWritten < 0)
        {
            //如果写入操作是被信号打断了, 则说明还可以继续写入
            if (errno == EINTR)
                continue;
            //否则就是其他错误
            else
                return -1;
        }
        //如果 ==0则说明是什么也没写入, 可以继续写
        else if (nWritten == 0)
            continue;

        //正常写入
        nLeft -= nWritten;
        pBuf += nWritten;
    }
    return count;
}
