#include "connection.hh"
#include "server_widget.hh"

namespace {

constexpr char timeFormat[] = "dd.MM.yyyy HH:mm";

} //namespace

Connection::Connection(QLocalSocket* connection, ServerWidget* server):
    connection_(connection),
    server_(server),
    clientId_(-1),
    created_(QDateTime::currentDateTime()),
    receivedCount_(0),
    sentCount_(0),
    dataView_(nullptr)
{  
    connect(
        connection, &QLocalSocket::readyRead,
        this, &Connection::readMessage);

    connect(
        connection, &QLocalSocket::disconnected,
        this, &Connection::onSocketDisconnected);
}

Connection::~Connection()
{
    delete connection_;
    connection_ = nullptr;
}

bool Connection::isRegistered() const
{
    return clientId_ >= 0;
}

int Connection::clientId() const
{
    return clientId_;
}

void Connection::writeMessage(const Message& message)
{
    QByteArray block;
    QDataStream stream(&block, QIODevice::WriteOnly);
    stream.setVersion(QDataStream::Qt_5_0);
    QString messageText = serializeMessage(message);

    stream << (quint16)0;
    stream << messageText.toLatin1();
    stream.device()->seek(0);
    stream << (quint16)(block.size() - sizeof(quint16));

    connection_->write(block);
    connection_->flush();

    ++sentCount_;

    if (dataView_ && dataView_->isVisible()) {
        QString dateTime = QDateTime::currentDateTime().toString(timeFormat);
        QString prefix = "[" + dateTime + "] " + tr("Sent message:\n");
        dataView_->append(prefix + messageText);
    }
}

bool Connection::hasSocket()
{
    return connection_ != nullptr;
}

void Connection::disconnectSocket()
{
    connection_->abort();
}

void Connection::readMessage()
{
    connection_->blockSignals(true);

    quint16 bytesToRead = 0;
    {
        QDataStream stream(connection_);
        stream.setVersion(QDataStream::Qt_5_0);
        stream >> bytesToRead;
    }

    QByteArray block;
    block.append(connection_->readAll());

    while(connection_->waitForReadyRead(0))
        block.append(connection_->readAll());

    QString messageText;
    {
        QDataStream stream(&block, QIODevice::ReadOnly);
        stream.setVersion(QDataStream::Qt_5_0);
        QByteArray data;
        stream >> data;
        messageText = QString::fromLatin1(data);
    }

    connection_->blockSignals(false);
    ++receivedCount_;

    if (dataView_ && dataView_->isVisible()) {
        QString dateTime = QDateTime::currentDateTime().toString(timeFormat);
        QString prefix = "[" + dateTime + "] " + tr("Received message:\n");
        dataView_->append(prefix + messageText);
    }

    Message message = parseMessage(messageText);

    if (message.type == MessageType::Invalid) {
        writeMessage(errorStatusReport(
            message.clientId,MessageType::RegistrationRequest,
            "Invalid message received"));
        return;
    }

    if (message.type == MessageType::RegistrationRequest) {
        if (server_->isRegistered(message.clientId)) {
            writeMessage(errorStatusReport(
                message.clientId, MessageType::RegistrationRequest,
                "Connection already registered"));
        } else {
            clientId_ = message.clientId;
            emit connectionRegistered(message.clientId, message.registration);
        }
        return;
    }

    emit messageReceived(message);
}

void Connection::onSocketDisconnected()
{
    connection_->deleteLater();
    connection_ = nullptr;
    emit socketDisconnected();
}

QDateTime Connection::created() const
{
    return created_;
}

int Connection::receivedMessages() const
{
    return receivedCount_;
}

int Connection::sentMessages() const
{
    return sentCount_;
}

void Connection::showData()
{
    if (!dataView_) {
        dataView_.reset(new TextViewDialog());
        dataView_->setWindowIcon(QIcon(":app24"));
        dataView_->setWindowTitle(tr("Connection data view"));
    }

    dataView_->clear();
    dataView_->show();
}
