/*
 * Copyright (c) 2015, Technische Universität Kaiserslautern
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors:
 *    Janik Schlemminger
 *    Robert Gernhardt
 *    Matthias Jung
 *    Derek Christ
 *    Iron Prando da Silva
 */

#include <QString>
#include <QFile>
#include <QStringList>
#include <QTextStream>
#include <QSqlError>
#include <iostream>
#include <QFileInfo>
#include <QDebug>
#include "data/tracedb.h"
#include "businessObjects/phases/phasefactory.h"


//define symbol printqueries if all queries should be printed to the console
//#define printqueries

TraceDB::TraceDB(const QString& path, bool openExisting)
{
    this->pathToDB = path;

    database = QSqlDatabase::database(path);
    if (database.isValid() && database.isOpen())
    {
        // Close the database connection if it exists and was not closed yet.
        database.close();
        QSqlDatabase::removeDatabase(path);
    }

    database = QSqlDatabase::addDatabase("QSQLITE", path);
    database.setDatabaseName(path);
    if (!database.open())
    {
        qDebug() << database.lastError().text();
    }
    if (!openExisting)
        dropAndCreateTables();
    prepareQueries();
    generalInfo = getGeneralInfoFromDB();
    commandLengths = getCommandLengthsFromDB();
}

void TraceDB::prepareQueries()
{
    selectTransactionsByTimespan = QSqlQuery(database);
    if (!selectTransactionsByTimespan.prepare(queryTexts.selectTransactionsByTimespan))
        qDebug() << database.lastError().text();

    selectTransactionById = QSqlQuery(database);
    if (!selectTransactionById.prepare(queryTexts.selectTransactionById))
        qDebug() << database.lastError().text();

    selectDebugMessagesByTimespan = QSqlQuery(database);
    if (!selectDebugMessagesByTimespan.prepare("SELECT time, Message FROM DebugMessages WHERE :begin <= time AND time <= :end "))
        qDebug() << database.lastError().text();

    selectDebugMessagesByTimespanWithLimit = QSqlQuery(database);
    if (!selectDebugMessagesByTimespanWithLimit.prepare("SELECT time, Message FROM DebugMessages WHERE :begin <= time AND time <= :end LIMIT :limit"))
        qDebug() << database.lastError().text();

    checkDependenciesExist = QSqlQuery(database);
    if (!checkDependenciesExist.prepare(queryTexts.checkDependenciesExist))
        qDebug() << database.lastError().text();

    selectDependenciesByTimespan = QSqlQuery(database);
    if (!selectDependenciesByTimespan.prepare(queryTexts.selectDependenciesByTimespan))
        qDebug() << database.lastError().text();

}

void TraceDB::updateComments(const std::vector<CommentModel::Comment> &comments)
{
    QSqlQuery query(database);
    query.prepare("DELETE FROM Comments");
    executeQuery(query);
    query.prepare("insert into Comments values(:time,:text)");

    for (const auto &comment : comments)
    {
        query.bindValue(":time", comment.time);
        query.bindValue(":text", comment.text);
        executeQuery(query);
    }
}

void TraceDB::updateFileDescription(const QString &description)
{
    QSqlQuery query(database);
    query.prepare("UPDATE GeneralInfo SET Description=:description");
    query.bindValue(":description", description);
    executeQuery(query);
}

void TraceDB::refreshData()
{
    prepareQueries();
    generalInfo = getGeneralInfoFromDB();
}

//QueryText must select the fields
//TransactionID, Ranges.begin, Ranges.end, Address, TThread, TChannel, TBank, TRow, TColumn, Phases.ID AS PhaseID, PhaseName, PhaseBegin, PhaseEnd
std::vector<std::shared_ptr<Transaction>> TraceDB::getTransactionsWithCustomQuery(const QString& queryText)
{
    QSqlQuery query(database);
    query.prepare(queryText);
    executeQuery(query);
    return parseTransactionsFromQuery(query);
}

std::vector<std::shared_ptr<Transaction>> TraceDB::getTransactionsInTimespan(const Timespan &span, bool updateVisiblePhases)
{
    selectTransactionsByTimespan.bindValue(":begin", span.Begin());
    selectTransactionsByTimespan.bindValue(":end", span.End());
    executeQuery(selectTransactionsByTimespan);
    return parseTransactionsFromQuery(selectTransactionsByTimespan, updateVisiblePhases);
}

bool TraceDB::checkDependencyTableExists()
{
    executeQuery(checkDependenciesExist);

    bool exists = checkDependenciesExist.next() && checkDependenciesExist.value(0).toInt() == 1;

    checkDependenciesExist.finish();

    return exists;
}

void TraceDB::updateDependenciesInTimespan(const Timespan &span)
{
    if (checkDependencyTableExists())
    {
        selectDependenciesByTimespan.bindValue(":begin", span.Begin());
        selectDependenciesByTimespan.bindValue(":end", span.End());
        executeQuery(selectDependenciesByTimespan);
        mUpdateDependenciesFromQuery(selectDependenciesByTimespan);
    }
}

//TODO Remove exception
std::shared_ptr<Transaction> TraceDB::getTransactionByID(ID id)
{
    selectTransactionById.bindValue(":id", id);
    executeQuery(selectTransactionById);
    auto result = parseTransactionsFromQuery(selectTransactionById);
    if (!result.empty())
        return result[0];
    else
        throw sqlException(("Transaction with ID " + QString::number(
                                id) + " not in DB").toStdString(), this->pathToDB.toStdString());
}


std::shared_ptr<Transaction> TraceDB::getNextActivate(traceTime time)
{
    QSqlQuery query(database);
    QString queryText = queryTexts.queryHead +
                        "WHERE PhaseBegin > :traceTime AND PhaseName = 'ACT' ORDER BY PhaseBegin ASC LIMIT 1";

    query.prepare(queryText);
    query.bindValue(":traceTime", time);
    executeQuery(query);
    return parseTransactionFromQuery(query);
}

std::shared_ptr<Transaction> TraceDB::getNextPrecharge(traceTime time)
{
    QSqlQuery query(database);
    QString queryText = queryTexts.queryHead +
                        "WHERE PhaseBegin > :traceTime AND PhaseName "
                        "IN ('PRE','PREPB','PREA','PREAB','PRESB') ORDER BY PhaseBegin ASC LIMIT 1";

    query.prepare(queryText);
    query.bindValue(":traceTime", time);
    executeQuery(query);
    return parseTransactionFromQuery(query);
}

// shared_ptr<Transaction> TraceDB::getNextActb(ID currentTransactionId)
// {
//     QSqlQuery query(database);
//     QString queryText = queryTexts.queryHead +
//                         "WHERE TransactionID > :currentID AND PhaseName = 'ACTB' LIMIT 1";
//
//     query.prepare(queryText);
//     query.bindValue(":currentID", currentTransactionId);
//     executeQuery(query);
//     return parseTransactionFromQuery(query);
// }

// shared_ptr<Transaction> TraceDB::getNextPreb(ID currentTransactionId)
// {
//     QSqlQuery query(database);
//     QString queryText = queryTexts.queryHead +
//                         "WHERE TransactionID > :currentID AND PhaseName = 'PREB' LIMIT 1";
//
//     query.prepare(queryText);
//     query.bindValue(":currentID", currentTransactionId);
//     executeQuery(query);
//     return parseTransactionFromQuery(query);
// }

std::shared_ptr<Transaction> TraceDB::getNextRefresh(traceTime time)
{
    QSqlQuery query(database);
    QString queryText = queryTexts.queryHead +
                        "WHERE PhaseBegin > :traceTime AND PhaseName "
                        "IN ('REFAB','REFA','REFB','REFPB','REFP2B','REFSB','SREF','SREFB') ORDER BY PhaseBegin ASC LIMIT 1";
    query.prepare(queryText);
    query.bindValue(":traceTime", time);
    executeQuery(query);
    return parseTransactionFromQuery(query);
}

std::shared_ptr<Transaction> TraceDB::getNextCommand(traceTime time)
{
    QSqlQuery query(database);
    QString queryText = queryTexts.queryHead +
                        "WHERE PhaseBegin > :traceTime ORDER BY PhaseBegin ASC LIMIT 1";
    query.prepare(queryText);
    query.bindValue(":traceTime", time);
    executeQuery(query);
    return parseTransactionFromQuery(query);
}

// shared_ptr<Transaction> TraceDB::getNextRefb(ID currentTransactionId)
// {
//     QSqlQuery query(database);
//     QString queryText = queryTexts.queryHead +
//                         "WHERE TransactionID > :currentID AND PhaseName = 'REFB' LIMIT 1";
//
//     query.prepare(queryText);
//     query.bindValue(":currentID", currentTransactionId);
//     executeQuery(query);
//     return parseTransactionFromQuery(query);
// }

ID TraceDB::getTransactionIDFromPhaseID(ID phaseID)
{
    QSqlQuery query(database);
    query.prepare("SELECT Transact FROM Phases WHERE ID=:id");
    query.bindValue(":id", phaseID);
    executeQuery(query);

    if (query.next()) {
        return query.value(0).toInt();
    } else {
        throw sqlException("Phase with ID " + std::to_string(phaseID) + " not in db",
                           this->pathToDB.toStdString());
    }
}

GeneralInfo TraceDB::getGeneralInfoFromDB()
{
    QVariant parameter;
    parameter = getParameterFromTable("NumberOfTransactions", "GeneralInfo");
    uint64_t numberOfTransactions = parameter.isValid() ? parameter.toULongLong() : 0;
    parameter = getParameterFromTable("TraceEnd", "GeneralInfo");
    traceTime traceEnd = parameter.isValid() ? static_cast<traceTime>(parameter.toULongLong()) : 0;
    parameter = getParameterFromTable("NumberOfRanks", "GeneralInfo");
    unsigned numberOfRanks = parameter.isValid() ? parameter.toUInt() : 1;
    parameter = getParameterFromTable("NumberOfBankgroups", "GeneralInfo");
    unsigned numberOfBankGroups = parameter.isValid() ? parameter.toUInt() : numberOfRanks;
    parameter = getParameterFromTable("NumberOfBanks", "GeneralInfo");
    unsigned numberOfBanks = parameter.isValid() ? parameter.toUInt() : numberOfBankGroups;
    parameter = getParameterFromTable("Clk", "GeneralInfo");
    uint64_t clkPeriod = parameter.isValid() ? parameter.toULongLong() : 1000;
    parameter = getParameterFromTable("UnitOfTime", "GeneralInfo");
    QString unitOfTime = parameter.isValid() ? parameter.toString() : "PS";
    parameter = getParameterFromTable("Traces", "GeneralInfo");
    QString traces  = parameter.isValid() ? "Traces: " + parameter.toString() : "Traces: empty";
    parameter = getParameterFromTable("Memspec", "GeneralInfo");
    QString memspec  = parameter.isValid() ? "Memspec: " + parameter.toString() : "Memspec: empty";
    parameter = getParameterFromTable("MCconfig", "GeneralInfo");
    QString mcconfig  = parameter.isValid() ? "MCconfig: " + parameter.toString() : "MCconfig: empty";
    parameter = getParameterFromTable("WindowSize", "GeneralInfo");
    uint64_t windowSize = parameter.isValid() ? parameter.toULongLong() : 0;
    parameter = getParameterFromTable("RefreshMaxPostponed", "GeneralInfo");
    unsigned refreshMaxPostponed = parameter.isValid() ? parameter.toUInt() : 0;
    parameter = getParameterFromTable("RefreshMaxPulledin", "GeneralInfo");
    unsigned refreshMaxPulledin = parameter.isValid() ? parameter.toUInt() : 0;
    parameter = getParameterFromTable("ControllerThread", "GeneralInfo");
    unsigned controllerThread = parameter.isValid() ? parameter.toUInt() : UINT_MAX;
    parameter = getParameterFromTable("MaxBufferDepth", "GeneralInfo");
    unsigned maxBufferDepth = parameter.isValid() ? parameter.toUInt() : 8;
    parameter = getParameterFromTable("Per2BankOffset", "GeneralInfo");
    unsigned per2BankOffset = parameter.isValid() ? parameter.toUInt() : 1;
    parameter = getParameterFromTable("RowColumnCommandBus", "GeneralInfo");
    bool rowColumnCommandBus = parameter.isValid() && parameter.toBool();
    parameter = getParameterFromTable("PseudoChannelMode", "GeneralInfo");
    bool pseudoChannelMode = parameter.isValid() && parameter.toBool();

    uint64_t numberOfPhases = getNumberOfPhases();

    QString description = (traces + "\n");
    description += mcconfig + "\n";
    description += memspec + "\n";
    description += "Number of Transactions: " + QString::number(numberOfTransactions) + "\n";
    description += "Clock period: " + QString::number(clkPeriod) + " " + unitOfTime + "\n";
    description += "Length of trace: " + prettyFormatTime(traceEnd) + "\n";
    description += "Window size: " + QString::number(windowSize) + "\n";

    return {numberOfTransactions, numberOfPhases,      Timespan(0, traceEnd),
            numberOfRanks,        numberOfBankGroups,  numberOfBanks,
            description,          unitOfTime,          clkPeriod,
            windowSize,           refreshMaxPostponed, refreshMaxPulledin,
            controllerThread,     maxBufferDepth,      per2BankOffset,
            rowColumnCommandBus, pseudoChannelMode};
}

CommandLengths TraceDB::getCommandLengthsFromDB()
{
    const std::string table = "CommandLengths";

    auto getLengthFromDb = [=, &table](const std::string &command) -> QVariant
    {
        QSqlQuery query(("SELECT Length FROM " + table + " WHERE Command = \"" + command + "\"").c_str(), database);

        if (query.first())
            return query.value(0);
        else
            return {};
    };

    auto getCommandLength = [=, &table](const std::string &command) -> double
    {
        QVariant length = getLengthFromDb(command);

        if (length.isValid())
            return length.toDouble();
        else
        {
            qDebug() << "CommandLength for" << command.c_str() << "not present in table" << table.c_str()
                     << ". Defaulting to 1.";
            return 1;
        }
    };

    double NOP = getCommandLength("NOP");
    double RD = getCommandLength("RD");
    double WR = getCommandLength("RD");
    double RDA = getCommandLength("RDA");
    double WRA = getCommandLength("WRA");
    double ACT = getCommandLength("ACT");

    double PREPB = getCommandLength("PREPB");
    double REFPB = getCommandLength("REFPB");

    double RFMPB = getCommandLength("RFMPB");
    double REFP2B = getCommandLength("REFP2B");
    double RFMP2B = getCommandLength("RFMP2B");
    double PRESB = getCommandLength("PRESB");
    double REFSB = getCommandLength("REFSB");
    double RFMSB = getCommandLength("RFMSB");

    double PREAB = getCommandLength("PREAB");
    double REFAB = getCommandLength("REFAB");

    double RFMAB = getCommandLength("RFMAB");
    double PDEA = getCommandLength("PDEA");
    double PDXA = getCommandLength("PDXA");
    double PDEP = getCommandLength("PDEP");
    double PDXP = getCommandLength("PDXP");
    double SREFEN = getCommandLength("SREFEN");
    double SREFEX = getCommandLength("SREFEX");

    return {NOP, RD, WR, RDA, WRA, ACT, PREPB, REFPB, RFMPB, REFP2B, RFMP2B, PRESB, REFSB, RFMSB,
            PREAB, REFAB, RFMAB, PDEA, PDXA, PDEP, PDXP, SREFEN, SREFEX};
}

QVariant TraceDB::getParameterFromTable(const std::string& parameter, const std::string& table)
{
    QSqlQuery query(("SELECT " + parameter + " FROM " + table).c_str(), database);
    if (query.first())
        return query.value(0);
    else
    {
        qDebug() << "Parameter " << parameter.c_str() << " not present in table " << table.c_str();
        return {};
    }
}

uint64_t TraceDB::getNumberOfPhases()
{
    QSqlQuery query(database);
    query.prepare("SELECT COUNT(ID) FROM Phases");
    executeQuery(query);

    query.next();
    return query.value(0).toULongLong();
}

std::vector<CommentModel::Comment> TraceDB::getComments()
{
    QSqlQuery query(database);
    query.prepare("SELECT Time,Text From Comments");
    executeQuery(query);
    return parseCommentsFromQuery(query);
}


std::vector<CommentModel::Comment> TraceDB::getDebugMessagesInTimespan(const Timespan &span)
{
    selectDebugMessagesByTimespan.bindValue(":begin", span.Begin());
    selectDebugMessagesByTimespan.bindValue(":end", span.End());
    executeQuery(selectDebugMessagesByTimespan);

    return parseCommentsFromQuery(selectDebugMessagesByTimespan);
}

std::vector<CommentModel::Comment> TraceDB::getDebugMessagesInTimespan(const Timespan &span,
                                                    unsigned int limit = 50)
{
    selectDebugMessagesByTimespanWithLimit.bindValue(":begin", span.Begin());
    selectDebugMessagesByTimespanWithLimit.bindValue(":end", span.End());
    selectDebugMessagesByTimespanWithLimit.bindValue(":limit", limit);
    executeQuery(selectDebugMessagesByTimespanWithLimit);
    return parseCommentsFromQuery(selectDebugMessagesByTimespanWithLimit);
}

DependencyInfos TraceDB::getDependencyInfos(DependencyInfos::Type infoType)
{
    DependencyInfos dummy;
    if (!checkDependencyTableExists())
    {
        return dummy;
    }

    switch (infoType)
    {
        case DependencyInfos::Type::DependencyType:
        {
            selectDependencyTypePercentages = QSqlQuery(database);
            if (!selectDependencyTypePercentages.prepare(queryTexts.selectDependencyTypePercentages))
                qDebug() << database.lastError().text();

            executeQuery(selectDependencyTypePercentages);
            return parseDependencyInfos(selectDependencyTypePercentages, infoType);
        }

        case DependencyInfos::Type::TimeDependency:
        {
            selectTimeDependencyPercentages = QSqlQuery(database);
            if (!selectTimeDependencyPercentages.prepare(queryTexts.selectTimeDependencyPercentages))
                qDebug() << database.lastError().text();
                
            executeQuery(selectTimeDependencyPercentages);
            return parseDependencyInfos(selectTimeDependencyPercentages, infoType);
        }

        case DependencyInfos::Type::DelayedPhase:
        {
            selectDelayedPhasePercentages = QSqlQuery(database);
            if (!selectDelayedPhasePercentages.prepare(queryTexts.selectDelayedPhasePercentages))
                qDebug() << database.lastError().text();

            executeQuery(selectDelayedPhasePercentages);
            return parseDependencyInfos(selectDelayedPhasePercentages, infoType);
        }

        case DependencyInfos::Type::DependencyPhase:
        {
            selectDependencyPhasePercentages = QSqlQuery(database);
            if (!selectDependencyPhasePercentages.prepare(queryTexts.selectDependencyPhasePercentages))
                qDebug() << database.lastError().text();

            executeQuery(selectDependencyPhasePercentages);
            return parseDependencyInfos(selectDependencyPhasePercentages, infoType);
        }

    }

    return dummy;
}

QSqlDatabase TraceDB::getDatabase() const
{
    return database;
}

/*  Helpers
 *
 *
 *
 */


std::shared_ptr<Transaction> TraceDB::parseTransactionFromQuery(QSqlQuery &query)
{
    auto result = parseTransactionsFromQuery(query);
    if (!result.empty())
        return result[0];
    else
        return {};
}

std::vector<std::shared_ptr<Transaction>> TraceDB::parseTransactionsFromQuery(QSqlQuery &query, bool updateVisiblePhases)
{
    if (updateVisiblePhases)
    {
        _visiblePhases.clear();
    }

    std::vector<std::shared_ptr<Transaction>> result;

    bool firstIteration = true;
    ID currentID = 0;
    int i = -1;

    while (query.next())
    {
        ID id = query.value(0).toInt();

        if (currentID != id || firstIteration)
        {
            ++i;
            firstIteration = false;
            currentID = id;
            Timespan span(query.value(1).toLongLong(), query.value(2).toLongLong());
            uint64_t address = query.value(3).toULongLong();
            unsigned int dataLength = query.value(4).toUInt();
            unsigned int thread = query.value(5).toUInt();
            unsigned int channel = query.value(6).toUInt();
            QString command = query.value(7).toString();
            result.push_back(std::make_shared<Transaction>(id, std::move(command), address, dataLength, thread, channel,
                                                           span, generalInfo.clkPeriod));
        }

        unsigned int phaseID = query.value(8).toInt();
        QString phaseName = query.value(9).toString();
        Timespan span(query.value(10).toLongLong(), query.value(11).toLongLong());
        Timespan spanOnDataStrobe(query.value(12).toLongLong(), query.value(13).toLongLong());
        unsigned int rank = query.value(14).toUInt();
        unsigned int bankGroup = query.value(15).toUInt();
        unsigned int bank = query.value(16).toUInt();
        unsigned int row = query.value(17).toUInt();
        unsigned int column = query.value(18).toUInt();
        unsigned int burstLength = query.value(19).toUInt();
        auto phase = PhaseFactory::createPhase(phaseID, phaseName, span, spanOnDataStrobe, rank, bankGroup, bank,
                                               row, column, burstLength, result.at(result.size() - 1), *this);
        result.at(result.size() - 1)->addPhase(phase);

        if (updateVisiblePhases)
        {
            _visiblePhases[phaseID] = phase;
        }
    }
    return result;
}

void TraceDB::mUpdateDependenciesFromQuery(QSqlQuery &query)
{
    DependencyType type;
    while (query.next())
    {
        ID delayedID = query.value(0).toInt();
        ID dependencyID = query.value(4).toInt();

        QString dependencyTypeStr = query.value(2).toString();
        if (dependencyTypeStr == "bank")
        {
            type = DependencyType::IntraBank;
        }
        else if (dependencyTypeStr == "rank")
        {
            type = DependencyType::IntraRank;
        }
        else if (dependencyTypeStr == "interRank")
        {
            type = DependencyType::InterRank;
        }

        QString timeDependencyStr = query.value(3).toString();

        if (_visiblePhases.count(delayedID) > 0)
        {

            if (_visiblePhases.count(dependencyID) > 0)
            {

                _visiblePhases[delayedID]->addDependency(std::make_shared<PhaseDependency>(
                        PhaseDependency(type, timeDependencyStr, _visiblePhases[dependencyID])));
            }
            else
            {

                _visiblePhases[delayedID]->addDependency(
                        std::make_shared<PhaseDependency>(PhaseDependency(type, timeDependencyStr)));
            }
        }
        else
        {
            // TODO delayed phase not visible?
        }
    }
}

std::vector<CommentModel::Comment> TraceDB::parseCommentsFromQuery(QSqlQuery &query)
{
    std::vector<CommentModel::Comment> result;
    while (query.next())
    {
        result.push_back(CommentModel::Comment{query.value(0).toLongLong(),
                                               query.value(1).toString()});
    }
    return result;
}

DependencyInfos TraceDB::parseDependencyInfos(QSqlQuery &query, const DependencyInfos::Type infoType)
{
    DependencyInfos infos(infoType);

    while (query.next())
    {
        infos.addInfo({query.value(0).toString(), query.value(1).toFloat()});
    }

    query.finish();

    return infos;
}

void TraceDB::executeQuery(QSqlQuery query)
{

    //query.exec returns bool indicating if the query was sucessfull
    if (query.exec()) {
#ifdef printqueries
        cout << queryToString(query).toStdString() << endl;
#endif
    }

    else {
        query.finish();
        throw sqlException( ("Query:\n " + queryToString(query) + "\n failed. Error: \n"
                             +
                             query.lastError().text()).toStdString(), this->pathToDB.toStdString());
    }
}

QString TraceDB::queryToString(const QSqlQuery& query)
{
    QString str = query.lastQuery();
    QMapIterator<QString, QVariant> it(query.boundValues());
    while (it.hasNext()) {
        it.next();
        str.replace(it.key(), it.value().toString());
    }
    return str;
}


void TraceDB::dropAndCreateTables()
{
    executeScriptFile("common/static/createTraceDB.sql");
}

void TraceDB::executeScriptFile(const QString& fileName)
{
    QSqlQuery query(database);
    QFile scriptFile(fileName);

    if (scriptFile.open(QIODevice::ReadOnly)) {
        // The SQLite driver executes only a single (the first) query in the QSqlQuery
        // if the script contains more queries, it needs to be splitted.
        QStringList scriptQueries = QTextStream(&scriptFile).readAll().split(';');

        for (QString &queryTxt : scriptQueries) {
            if (queryTxt.trimmed().isEmpty()) {
                continue;
            }
            if (!query.exec(queryTxt)) {
                throw sqlException("Querry failed:" + query.lastError().text().toStdString(),
                                   this->pathToDB.toStdString());
            }
            query.finish();
        }
    }
}

