#include "server_widget.hh"
#include "connection.hh"
#include <QDebug>
#include <cassert>

namespace {

std::vector<ConnectionPtr>::iterator findConnection(
   std::vector<ConnectionPtr>& connections, int clientId)
{
    return std::find_if(connections.begin(), connections.end(),
        [&clientId](const ConnectionPtr& connection){
            return connection->clientId() == clientId;
        });
}

void addRow(
    QTableWidget& table, const QDateTime& created)
{
    int row = table.rowCount();
    table.insertRow(row);

    QString dtStr = created.toString("dd.MM.yyyy HH:mm:ss");

    table.setItem(row, 0, new QTableWidgetItem(QTableWidget::tr("None")));
    table.setItem(row, 1, new QTableWidgetItem(dtStr));
    table.setItem(row, 2, new QTableWidgetItem("0"));
    table.setItem(row, 3, new QTableWidgetItem("0"));
}

void updateCell(QTableWidget& table, int row, int column, int value)
{
    auto idItem = table.item(row, column);
    assert(idItem);
    idItem->setText(QString::number(value));
}

} // namespace

ServerWidget::ServerWidget(QWidget* parent):
    QTableWidget(parent), server_(this)
{
    connect(
        &server_, &QLocalServer::newConnection,
        this, &ServerWidget::handleNewConnection);

    connect(
        this, &ServerWidget::cellDoubleClicked,
        this, &ServerWidget::onCellDoubleClicked);

    connect(
        &timer_, &QTimer::timeout,
        this, &ServerWidget::onTimerTimeout);

    timer_.start(1000);
}

bool ServerWidget::listen()
{
    return server_.listen(serverName);
}

void ServerWidget::disconnectAll()
{
    connections_.clear();
    clear();
}

void ServerWidget::sendMessage(const Message& message)
{
    auto it = findConnection(connections_, message.clientId);

    if (it != connections_.end())
        (*it)->writeMessage(message);
}

bool ServerWidget::closeConnection(int clientId)
{
    auto it = findConnection(connections_, clientId);

    if (it == connections_.end())
        return false;

    (*it)->disconnectSocket();
    return true;
}

bool ServerWidget::isRegistered(int clientId)
{
    auto it = findConnection(connections_, clientId);
    return it != connections_.end();
}

void ServerWidget::handleNewConnection()
{
    ConnectionPtr connection = std::make_shared<Connection>(
        server_.nextPendingConnection(), this);

    connect(
        connection.get(), &Connection::connectionRegistered,
        this, &ServerWidget::onConnectionRegistered);

    connect(
        connection.get(), &Connection::messageReceived,
        [this](const Message& message){emit messageReceived(message);});

    connect(
        connection.get(), &Connection::socketDisconnected,
        this, &ServerWidget::onDisconect);

    connections_.push_back(connection);

    addRow(*this, connection->created());
    emit newConnectionCreated();
}

void ServerWidget::onDisconect()
{
    auto it = connections_.begin();
    while (it != connections_.end()) {
        if (!(*it)->hasSocket()) {
            int client = (*it)->clientId();
            bool registered = (*it)->isRegistered();
            removeRow(std::distance(connections_.begin(), it));
            it = connections_.erase(it);
            if (registered)
                emit connectionClosed(client);
        } else {
            it++;
        }
    }
}

void ServerWidget::onConnectionRegistered(
    int clientId, const Registration& registration)
{
    auto it = findConnection(connections_, clientId);

    if (it != connections_.end()) {
        int row = std::distance(connections_.begin(), it);
        updateCell(*this, row, 0, clientId);
    }

    emit connectionRegistered(clientId, registration);
}

void ServerWidget::onCellDoubleClicked(int row, int)
{
    connections_[row]->showData();
}

void ServerWidget::onTimerTimeout()
{
    for (unsigned int r = 0; r < connections_.size(); r++) {
        updateCell(*this, r, 2, connections_[r]->receivedMessages());
        updateCell(*this, r, 3, connections_[r]->sentMessages());
    }
}
