/*!
  \file
  \brief TCP/IP 通信

  \author Satofumi KAMIMURA

  $Id: TcpipSocket.cpp 1988 2012-05-15 07:04:56Z satofumi $

  \todo テストを実装して動作確認を行う
*/

#include "TcpipSocket.h"
#include <QTcpSocket>

using namespace qrk;


struct TcpipSocket::pImpl
{
    QTcpSocket* socket_;
    long baudrate_;


    pImpl(QTcpSocket* socket) : socket_(socket), baudrate_(0)
    {
    }


    ~pImpl(void)
    {
        //delete socket_;
    }
};


TcpipSocket::TcpipSocket(void) : pimpl(new pImpl(new QTcpSocket))
{
}


TcpipSocket::TcpipSocket(void* socket)
    : pimpl(new pImpl(static_cast<QTcpSocket*>(socket)))
{
}



TcpipSocket::~TcpipSocket(void)
{
    disconnect();
}


const char* TcpipSocket::what(void) const
{
    return pimpl->socket_->errorString().toStdString().c_str();
}


bool TcpipSocket::connect(const char* host, long port)
{
    pimpl->socket_->connectToHost(host, port);

    enum { ConnectionTimeout = 3000 };
    bool ret = pimpl->socket_->waitForConnected(ConnectionTimeout);

    return ret;
}


void TcpipSocket::disconnect(void)
{
    if (pimpl->socket_->isValid()) {
        pimpl->socket_->disconnectFromHost();
        pimpl->socket_->waitForDisconnected();
    }
}


bool TcpipSocket::setBaudrate(long baudrate)
{
    // 実装しない
    pimpl->baudrate_ = baudrate;
    return true;
}


long TcpipSocket::baudrate(void) const
{
    // 実装しない
    return pimpl->baudrate_;
}


bool TcpipSocket::isConnected(void) const
{
    return pimpl->socket_->isValid();
}


int TcpipSocket::send(const char* data, size_t count)
{
    return pimpl->socket_->write(data, count);
}


int TcpipSocket::receive(char* data, size_t count, int timeout)
{
    if (count == 0) {
        return 0;
    }

    // !!! 制御構造を見直す
    size_t filled = 0;
    do {
        int n = pimpl->socket_->read(&data[filled], count - filled);
        if (n < 0) {
            return n;
        }
        if (n > 0) {
            filled += n;
        }

        if ((filled < count) && (! pimpl->socket_->waitForReadyRead(timeout))) {
            break;
        }
    } while (filled < count);

    return filled;
}


size_t TcpipSocket::size(void) const
{
    return pimpl->socket_->bytesAvailable();
}


void TcpipSocket::flush(void)
{
    pimpl->socket_->flush();
}


void TcpipSocket::clear(void)
{
    // 受信バッファの内容を読み捨てる
    enum { BufferSize = 256 };
    char buffer[BufferSize];

    while (receive(buffer, BufferSize, 0) == BufferSize) {
        ;
    }
}


void TcpipSocket::ungetc(const char ch)
{
    pimpl->socket_->ungetChar(ch);
}
