#include "logger_cache.hh"
#include <utility>
#include <cassert>

namespace {

/// Number of candles logged before order was sent (if available)
constexpr int logBefore = 5;

int findOrder(const TradeData& data, int magic)
{
    for (int i = 0; i < data.orderCount(); i++) {
        if (magic == data.order(i).magic)
            return i;
    }
    return -1;
}

void logOrder(std::map<int, OrderVector>& logged, const Order& order)
{
    auto it = logged.find(order.magic);

    if (it == logged.end()) {
        OrderVector vector;
        vector.push_back(order);
        logged.insert(std::make_pair(order.magic, vector));
        return;
    }

    (*it).second.push_back(order);
}

} //namespace

void LoggerCache::setClient(int clientId, const Registration& registration)
{
    client_.id = clientId;
    client_.strategy = registration.strategy.toStdString();
    client_.currency = registration.currency.toStdString();
    client_.timeframe = registration.timeframe.toStdString();
    client_.digits = registration.digits;
}

void LoggerCache::setDatabase(std::shared_ptr<Database> database)
{
    database_ = database;
}

void LoggerCache::update(const TradeData& data)
{
    cachePrice(data);
    lookForFinished(data);
    lookForChanges(data);
    previousData_ = data;
}

void LoggerCache::cachePrice(const TradeData& data)
{
    if (data.candleCount() < 2)
        return;

    QDateTime last;
    int i = data.candleCount()-1;

    if (loggedPrice_.empty()) {
        loggedPrice_.push_back(data.candle(i));
        last = data.candle(i).dateTime();
        i--;
    } else {
        last = loggedPrice_.back().dateTime();
    }

    for (; i >= 1; i--) {
        if (data.candle(i).dateTime() > last)
            loggedPrice_.push_back(data.candle(i));
    }
}

void LoggerCache::lookForFinished(const TradeData& data)
{
    auto it = loggedOrders_.begin();
    while (it != loggedOrders_.end()) {
        if (findOrder(data, (*it).first) == -1) {
            orderFinished((*it).second, (*it).first);
            it = loggedOrders_.erase(it);
        } else {
            it++;
        }
    }
}

void LoggerCache::lookForChanges(const TradeData& data)
{
    for (int i = 0; i < data.orderCount(); i++) {
        auto& order = data.order(i);
        if (!order.event.empty())
            logOrder(loggedOrders_, order);
    }
}

void LoggerCache::orderFinished(OrderVector& orderEvents, int magic)
{
    if (!database_)
        return;

    QDateTime from = orderEvents.front().time;

    auto itFrom = std::find_if(loggedPrice_.begin(), loggedPrice_.end(),
        [&from](auto& candle){return candle.dateTime() >= from;});

    if (std::distance(loggedPrice_.begin(), itFrom) > logBefore)
        itFrom -= logBefore;
    else
        itFrom = loggedPrice_.begin();

    ChartCandle last =
        previousData_.hasCandle() ? previousData_.candle(0) : ChartCandle();

    database_->logTrade(orderEvents, loggedPrice_, itFrom, last, client_);
}
