#include "tcpsocket.h"

TcpSocket::TcpSocket(QObject *parent) : QTcpSocket{parent} {
    connect(this, &QTcpSocket::errorOccurred, this, [this]() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            hasErr = true;
        }
        cv.notify_all();
    });
    connect(this, &QTcpSocket::connected, this, [this]() {
        {
            std::lock_guard<std::mutex> lock(mtx);
        }
        cv.notify_all();
    });
    connect(this, &QTcpSocket::disconnected, this, [this]() {
        {
            std::lock_guard<std::mutex> lock(mtx);
        }
        cv.notify_all();
    });
    connect(this, &QTcpSocket::bytesWritten, this, [this]() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            isWritten = true;
        }
        cv.notify_all();
    });
    connect(this, &QTcpSocket::readyRead, this, [this]() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            isReadyRead = true;
        }
        cv.notify_all();
    });
}

bool TcpSocket::waitForConnected(int msecs) {
    if(hasErr) return false;
    if(state() == ConnectedState) return true;
    {
        std::unique_lock<std::mutex> lock(mtx);
        if(hasErr) return false;
        if(state() == ConnectedState) return true;
        auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(msecs);
        auto check = until - std::chrono::milliseconds(10);
        do {
            cv.wait_until(lock, until);
            if(hasErr) return false;
            if(state() == ConnectedState) return true;
        } while(std::chrono::steady_clock::now() < check);
    }
    hasErr = true;
    setSocketError(SocketTimeoutError);
    return false;
}
bool TcpSocket::waitForDisconnected(int msecs) {
    if(hasErr) return false;
    if(state() == UnconnectedState) return true;
    {
        std::unique_lock<std::mutex> lock(mtx);
        if(hasErr) return false;
        if(state() == UnconnectedState) return true;
        auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(msecs);
        auto check = until - std::chrono::milliseconds(10);
        do {
            cv.wait_until(lock, until);
            if(hasErr) return false;
            if(state() == UnconnectedState) return true;
        } while(std::chrono::steady_clock::now() < check);
    }
    hasErr = true;
    setSocketError(SocketTimeoutError);
    return false;
}
bool TcpSocket::waitForBytesWritten(int msecs) {
    if(hasErr) return false;
    if(isWritten) return true;
    {
        std::unique_lock<std::mutex> lock(mtx);
        if(hasErr) return false;
        if(isWritten) return true;
        auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(msecs);
        auto check = until - std::chrono::milliseconds(10);
        do {
            cv.wait_until(lock, until);
            if(hasErr) return false;
            if(isWritten) return true;
        } while(std::chrono::steady_clock::now() < check);
    }
    hasErr = true;
    setSocketError(SocketTimeoutError);
    return false;
}
bool TcpSocket::waitForReadyRead(int msecs) {
    if(hasErr) return false;
    if(isReadyRead) return true;
    {
        std::unique_lock<std::mutex> lock(mtx);
        if(hasErr) return false;
        if(isReadyRead) return true;
        auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(msecs);
        auto check = until - std::chrono::milliseconds(10);
        do {
            cv.wait_until(lock, until);
            if(hasErr) return false;
            if(isReadyRead) return true;
        } while(std::chrono::steady_clock::now() < check);
    }
    hasErr = true;
    setSocketError(SocketTimeoutError);
    return false;
}
qint64 TcpSocket::readData(char *data, qint64 maxlen) {
    auto rtn = QTcpSocket::readData(data, maxlen);
    isReadyRead = false;
    return rtn;
}
qint64 TcpSocket::readLineData(char *data, qint64 maxlen) {
    auto rtn = QTcpSocket::readLineData(data, maxlen);
    isReadyRead = false;
    return rtn;
}
qint64 TcpSocket::writeData(const char *data, qint64 len) {
    isWritten = false;
    return QTcpSocket::writeData(data, len);
}
