#include "trade_data.hh"

namespace {

template <typename Map, typename Type>
void addToMap(
    Map& map, const std::string& name,
    const QDateTime& datetime, const Type& value)
{
    if (map.find(name) == map.end())
        map.emplace(name, std::vector<std::pair<QDateTime, Type>>());

    auto pair = std::make_pair(datetime, value);
    map[name].push_back(pair);
}

template <typename Map>
int count(const Map& map, const std::string& name)
{
    auto it = map.find(name);

    if (it == map.end())
        return 0;

    return (*it).second.size();
}

} //namespace

Order::Order() :
    command(std::string()), event(std::string()), error(std::string()),
    magic(-1), ticket(-1), type(std::string()), openPrice(0),
    closePrice(0), lots(0), slippage(0), stopLoss(0), takeProfit(0),
    stopLossPt(0), takeProfitPt(0), profit(0), time(QDateTime()),
    openTime(QDateTime())
{}

Order Order::send(
    const std::string& type, double lots, int slippage,
    int stopLossPt, int takeProfitPt)
{
    Order order;
    order.command = "SEND";
    order.type = type;
    order.lots = lots;
    order.slippage = slippage;
    order.stopLossPt = stopLossPt;
    order.takeProfitPt = takeProfitPt;
    return order;
}

Order Order::modify(int ticket, int stopLossPt, int takeProfitPt)
{
    Order order;
    order.command = "MODIFY";
    order.ticket = ticket;
    order.stopLossPt = stopLossPt;
    order.takeProfitPt = takeProfitPt;
    return order;
}

Order Order::close(int ticket, double lots, int slippage)
{
    Order order;
    order.command = "CLOSE";
    order.ticket = ticket;
    order.lots = lots;
    order.slippage = slippage;
    return order;
}

void DataRequest::append(const std::string& name, int count)
{
    if (count < 2) count = 2;
    data_.push_back(std::make_pair(name, count));
}

StringIntVector::const_iterator DataRequest::begin() const
{
    return data_.begin();
}

StringIntVector::const_iterator DataRequest::end() const
{
    return data_.end();
}

void DataRequest::clear()
{
    data_.clear();
}

int DataRequest::count() const
{
    return data_.size();
}

const std::pair<std::string, int>& DataRequest::operator[](int index)
{
    return data_.at(index);
}

void TradeData::addOrder(const Order& order)
{
    orders_.push_back(order);
}

void TradeData::addCandle(const ChartCandle& candle)
{
    candles_.push_back(candle);
}

void TradeData::addValue(
    const std::string& name, const QDateTime& datetime, double value)
{
    addToMap(values_, name, datetime, value);
}

void TradeData::addVector(
    const std::string& name, const QDateTime& datetime, const Vector& vector)
{
    addToMap(vectors_, name, datetime, vector);
}

int TradeData::orderCount() const
{
    return orders_.size();
}

int TradeData::candleCount() const
{
    return candles_.size();
}

int TradeData::valueCount(const std::string& name) const
{
    return count(values_, name);
}

int TradeData::vectorCount(const std::string& name) const
{
    return count(vectors_, name);
}

DoubleVectorMap::const_iterator TradeData::valuesBegin() const
{
    return values_.cbegin();
}

DoubleVectorMap::const_iterator TradeData::valuesEnd() const
{
    return values_.cend();
}

DoubleTableMap::const_iterator TradeData::vectorsBegin() const
{
    return vectors_.cbegin();
}

DoubleTableMap::const_iterator TradeData::vectorsEnd() const
{
    return vectors_.cend();
}

bool TradeData::hasOrder() const
{
    return orders_.size() != 0;
}

bool TradeData::hasCandle() const
{
    return candles_.size() != 0;
}

bool TradeData::hasValue(const std::string& name) const
{
    return count(values_, name) != 0;
}

bool TradeData::hasVector(const std::string& name) const
{
    return count(vectors_, name) != 0;
}

const Order& TradeData::order(int index) const
{
    return orders_[index];
}

const ChartCandle& TradeData::candle(int index) const
{
    return candles_[index];
}

const TimeDouble& TradeData::value(const std::string& name, int index) const
{
    return values_.at(name)[index];
}

const TimeVector& TradeData::vector(const std::string& name, int index) const
{
    return vectors_.at(name)[index];
}

bool TradeData::validate(const DataRequest& request)
{
    for (auto& pair : request) {
        std::string name = pair.first;
        int count = pair.second;
        if (name == "PRICE") {
            if (candleCount() != count)
                return false;
        } else {
            if ((!hasValue(name) || valueCount(name) != count) &&
                (!hasVector(name) || vectorCount(name) != count))
            {
                return false;
            }
        }
    }
    return true;
}

void TradeData::clear()
{
    orders_.clear();
    candles_.clear();
    values_.clear();
    vectors_.clear();
}
