#include "command_processor.hh"
#include <cassert>
#include <QItemDelegate>

namespace {

std::vector<Client>::iterator findClient(
   std::vector<Client>& clients, int clientId)
{
    return std::find_if(clients.begin(), clients.end(),
        [&clientId](const Client& client){
            return client.clientId == clientId;
        });
}

void addRow(
    QTableWidget& table, int clientId, const QString& strategy,
    const QString& currency, const QString& timeframe)
{
    int row = table.rowCount();
    table.insertRow(row);

    table.setItem(row, 0, new QTableWidgetItem(QString::number(clientId)));
    table.setItem(row, 1, new QTableWidgetItem(strategy));
    table.setItem(row, 2, new QTableWidgetItem(currency));
    table.setItem(row, 3, new QTableWidgetItem(timeframe));
}

} //namespace

CommandProcessor::CommandProcessor(QWidget* parent)
    : QTableWidget(parent)
{
    connect(
        this, &CommandProcessor::cellDoubleClicked,
        this, &CommandProcessor::onCellDoubleClicked);
}

void CommandProcessor::registerClient(
    int clientId, const Registration& registration, StrategyPtr strategy)
{
    Client client;
    client.clientId = clientId;

    client.strategy = std::move(strategy);
    client.strategy->registration(registration);

    client.cache.setDatabase(db_);
    client.cache.setClient(clientId, registration);
    clients_.push_back(std::move(client));

    addRow(
        *this, clientId, registration.strategy,
        registration.currency, registration.timeframe);
}

void CommandProcessor::removeClient(int identifier)
{
    auto it = findClient(clients_, identifier);
    if (it != clients_.end()) {
        removeRow(std::distance(clients_.begin(), it));
        clients_.erase(it);
    }
}

void CommandProcessor::setDatabase(std::shared_ptr<Database> db)
{
    db_ = db;
}

void CommandProcessor::handleMessage(const Message& message)
{
    auto it = findClient(clients_, message.clientId);

    if (it == clients_.end())
        return;

    if (message.type == MessageType::UpdateRequest) {
        Message response;
        response.type = MessageType::UpdateDataRequest;
        response.clientId = (*it).clientId;
        response.dataRequest = (*it).strategy->requestedData();
        emit sendMessage(response);
    }
    else if (message.type == MessageType::UpdateDataResponse) {
        (*it).cache.update(message.dataResponse);
        if ((*it).strategy->tradeData(message.dataResponse)) {
            emit sendMessage(successStatusReport(
                (*it).clientId, MessageType::UpdateDataResponse));
        } else {
            emit sendMessage(errorStatusReport(
                (*it).clientId, MessageType::UpdateDataResponse,
                tr("Incomplete or invalid data received.")));
        }
    }
    else if (message.type == MessageType::DecisionRequest) {
        Message response;
        response.type = MessageType::DecisionResponse;
        response.clientId = (*it).clientId;
        response.tradeDecision = (*it).strategy->makeDecision();
        emit sendMessage(response);
    }
    else if(message.type == MessageType::DecisionStatus) {
        (*it).strategy->decisionStatus(message.decisionStatus);
        emit sendMessage(
            successStatusReport((*it).clientId, MessageType::DecisionStatus));
    }
}

void CommandProcessor::onCellDoubleClicked(int row, int)
{
    clients_[row].strategy->onClientDoubleClicked();
}
