#include "database.hh"
#include "../TradeCore/chart_candle.hh"
#include <QSqlError>
#include <QSqlQuery>
#include <QVariant>
#include <QMessageBox>
#include <QDebug>

Database::Database(const QString& connectionName) :
    sql_(QSqlDatabase::addDatabase("QMYSQL", connectionName))
{}

Database::~Database()
{
    QString connectionName = sql_.connectionName();
    sql_.close();

    // reference to database still held by sql_, reset
    // done by assigning a default constructed value
    sql_ = QSqlDatabase();

    QSqlDatabase::removeDatabase(connectionName);
}

bool Database::connect(
    const QString& hostname, const QString& database,
    const QString& username, const QString& password)
{
    sql_.setHostName(hostname);
    sql_.setDatabaseName(database);
    sql_.setUserName(username);
    sql_.setPassword(password);

    return sql_.open();
}

void Database::disconnect()
{
    sql_.close();
}

bool Database::hasError() const
{
    return sql_.lastError().type() != QSqlError::NoError;
}

QString Database::lastError() const
{
    return sql_.lastError().text();
}

QMap<int, QString> Database::enumeration(const QString& enumName)
{
    QMap<int, QString> enumVals;

    if (!sql_.isOpen())
        return enumVals;

    QSqlQuery query(sql_);

    query.prepare(
        "SELECT name, value FROM enum "
        "WHERE enum=:enumName ");

    query.bindValue(":enumName", enumName);

    if (!query.exec())
        return enumVals;

    while (query.next())
        enumVals.insert(query.value(1).toInt(), query.value(0).toString());

    return enumVals;
}

QStringList Database::strategyList()
{
    QStringList strategies;

    if (!sql_.isOpen())
        return strategies;

    QSqlQuery query(sql_);

    query.prepare(
        "SELECT DISTINCT(strategy) AS strategy "
        "FROM trade ORDER BY strategy ASC");

    if (!query.exec())
        return strategies;

    while (query.next())
        strategies.append(query.value(0).toString());

    return strategies;
}

QList<OrderRecord> Database::searchOrder(
    const QString& strategy, int currency, int timeFrame,
    const QDateTime& from, const QDateTime& to)
{
    QList<OrderRecord> trades;

    if (!sql_.isOpen())
        return trades;

    QString queryStr =
        "SELECT * FROM trade WHERE "
        "created >= :from AND created <= :to";

    if (!strategy.isEmpty()) queryStr += " AND strategy = :strategy";
    if (currency > 0) queryStr += " AND currency = :currency";
    if (timeFrame > 0) queryStr += " AND timeframe = :timeframe";

    queryStr += " ORDER BY created DESC";

    QSqlQuery query(sql_);
    query.prepare(queryStr);

    query.bindValue(":from", from);
    query.bindValue(":to", to);

    if (!strategy.isEmpty()) query.bindValue(":strategy", strategy);
    if (currency > 0) query.bindValue(":currency", currency);
    if (timeFrame > 0) query.bindValue(":timeframe", timeFrame);

    if (!query.exec())
        return trades;

    while (query.next()) {
        OrderRecord record;
        record.id = query.value("id").toLongLong();
        record.client = query.value("client").toInt();
        record.strategy = query.value("strategy").toString();
        record.currency = query.value("currency").toInt();
        record.timeFrame = query.value("timeframe").toInt();
        record.digits = query.value("digits").toInt();
        record.created = query.value("created").toDateTime();
        record.profit = orderProfit(record.id);
        trades.append(record);
    }

    return trades;
}

QList<EventRecord> Database::eventList(qlonglong tradeId)
{
    QList<EventRecord> events;

    if (!sql_.isOpen())
        return events;

    QSqlQuery query(sql_);

    query.prepare(
        "SELECT * FROM event "
        "WHERE trade = :tradeId "
        "ORDER BY time, ticket ASC");

    query.bindValue(":tradeId", tradeId);

    if (!query.exec())
        return events;

    while (query.next()) {
        EventRecord record;
        record.id = query.value("id").toLongLong();
        record.trade = query.value("trade").toLongLong();
        record.event = query.value("event").toInt();
        record.ticket = query.value("ticket").toInt();
        record.openPrice = query.value("openPrice").toDouble();
        record.closePrice = query.value("closePrice").toDouble();
        record.lots = query.value("lots").toDouble();
        record.stopLoss = query.value("stopLoss").toDouble();
        record.takeProfit = query.value("takeProfit").toDouble();
        record.profit = query.value("profit").toDouble();
        record.time = query.value("time").toDateTime();
        events.append(record);
    }

    return events;
}

QList<ChartCandle> Database::priceData(qlonglong tradeId)
{
    QList<ChartCandle> data;

    if (!sql_.isOpen())
        return data;

    QSqlQuery query(sql_);

    query.prepare(
        "SELECT time, open, high, low, close, volume "
        "FROM price WHERE trade = :tradeId "
        "ORDER BY time ASC");

    query.bindValue(":tradeId", tradeId);

    if (!query.exec())
        return data;

    while (query.next()) {
        ChartCandle candle;
        candle.dateTime(query.value(0).toDateTime());
        candle.open(query.value(1).toDouble());
        candle.high(query.value(2).toDouble());
        candle.low(query.value(3).toDouble());
        candle.close(query.value(4).toDouble());
        candle.volume(query.value(5).toLongLong());
        data.append(candle);
    }

    return data;
}

double Database::orderProfit(qlonglong tradeId)
{
    if (!sql_.isOpen())
        return 0.0;

    QSqlQuery query(sql_);

    query.prepare(
        "SELECT SUM(profit) FROM event "
        "WHERE trade = :tradeId AND event = "
        "(SELECT value FROM enum "
        "WHERE enum='EventType' AND name='Close')");

    query.bindValue(":tradeId", tradeId);

    if (!query.exec())
        return 0.0;

    if (!query.next())
        return 0;

    return query.value(0).toDouble();
}
