#include <pqc_migrateshortcuts.h>
#include <pqc_configfiles.h>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QFile>
#include <QSettings>

void PQCMigrateShortcuts::migrate(const QString &oldVersion, const QStringList &allVersions) {

    qDebug() << "args: oldVersion =" << oldVersion;

    QSqlDatabase db = QSqlDatabase::database("shortcuts");

    // in this case we stop
    if(oldVersion.startsWith("2") || oldVersion.startsWith("1")) {

        qDebug() << "Old version number if" << oldVersion << " - too old to use migrations. Setting up fresh database.";

        db.close();

        // backup current database
        QFile::remove(QString("%1.bak").arg(PQCConfigFiles::get().SHORTCUTS_DB()));
        QFile::copy(PQCConfigFiles::get().SHORTCUTS_DB(), QString("%1.bak").arg(PQCConfigFiles::get().SHORTCUTS_DB()));
        QFile::remove(PQCConfigFiles::get().SHORTCUTS_DB());

        // create new default database
        if(!QFile::copy(":/shortcuts.db", PQCConfigFiles::get().SHORTCUTS_DB()))
            qWarning() << "Unable to create shortcuts database";
        else {
            QFile file(PQCConfigFiles::get().SHORTCUTS_DB());
            file.setPermissions(file.permissions()|QFileDevice::WriteOwner);
        }

        return;
    }

    db.transaction();

    // this is a safety check to make sure we don't forget the above check
    if(oldVersion != "dev" && allVersions.indexOf(oldVersion) == -1) {
        qCritical() << "WARNING: The current version number needs to be added to the migrate() functions:" << oldVersion;
    }

    int iVersion = 0;
    if(oldVersion == "dev")
        iVersion = allVersions.length()-1;
    else if(oldVersion != "" && allVersions.contains(oldVersion))
        // we do a +1 as we are on the found version and don't need to migrate to it
        iVersion = allVersions.indexOf(oldVersion)+1;

    for(int iV = iVersion; iV < allVersions.length(); ++iV) {

        QString curVer = allVersions[iV];

        if(curVer == "4.0")
            migrate400();

        else if(curVer == "4.4")
            migrate440();

        else if(curVer == "4.6")
            migrate460();

        else if(curVer == "4.9.1")
            migrate491();

        else if(curVer == "5.0")
            migrate500();

        else if(curVer == "5.3")
            migrate530();

    }

    db.commit();

}

/******************************************************/
/******************************************************/

void PQCMigrateShortcuts::migrate530() {

    if(!helperCheckIfComboPresent("Shift+0"))
        helperInsertShortcut("Shift+0", "__setRating0", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+)"))
        helperInsertShortcut("Shift+)", "__setRating0", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+="))
        helperInsertShortcut("Shift+=", "__setRating0", 1, 0, 0);

    if(!helperCheckIfComboPresent("Shift+1"))
        helperInsertShortcut("Shift+1", "__setRating1", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+!"))
        helperInsertShortcut("Shift+!", "__setRating1", 1, 0, 0);

    if(!helperCheckIfComboPresent("Shift+2"))
        helperInsertShortcut("Shift+2", "__setRating2", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+@"))
        helperInsertShortcut("Shift+@", "__setRating2", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+\""))
        helperInsertShortcut("Shift+\"", "__setRating2", 1, 0, 0);

    if(!helperCheckIfComboPresent("Shift+3"))
        helperInsertShortcut("Shift+3", "__setRating3", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+#"))
        helperInsertShortcut("Shift+#", "__setRating3", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+§"))
        helperInsertShortcut("Shift+§", "__setRating3", 1, 0, 0);

    if(!helperCheckIfComboPresent("Shift+4"))
        helperInsertShortcut("Shift+4", "__setRating4", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+$"))
        helperInsertShortcut("Shift+$", "__setRating4", 1, 0, 0);

    if(!helperCheckIfComboPresent("Shift+5"))
        helperInsertShortcut("Shift+5", "__setRating5", 1, 0, 0);
    if(!helperCheckIfComboPresent("Shift+%"))
        helperInsertShortcut("Shift+%", "__setRating5", 1, 0, 0);

}

/******************************************************/
/******************************************************/

void PQCMigrateShortcuts::migrate500() {

    // check for extensions that were removed now

    migrate500_helperMoveShortcut({"__crop"}, "CropImage");
    migrate500_helperMoveShortcut({"__export", "__save", "__saveAs"}, "ExportImage");
    migrate500_helperMoveShortcut({"__navigationFloating"}, "FloatingNavigation");
    migrate500_helperMoveShortcut({"__histogram"}, "Histogram");
    migrate500_helperMoveShortcut({"__imgurAnonym", "__imgur"}, "ImgurCom");
    migrate500_helperMoveShortcut({"__showMapCurrent"}, "MapCurrent");
    migrate500_helperMoveShortcut({"__quickActions"}, "QuickActions");
    migrate500_helperMoveShortcut({"__scale"}, "ScaleImage");
    migrate500_helperMoveShortcut({"__wallpaper"}, "Wallpaper");


}

void PQCMigrateShortcuts::migrate500_helperMoveShortcut(const QStringList &cmds, const QString &extId) {

    // there might be multiple shortcuts, we take the first one and discard the rest
    bool foundOne = false;

    QSettings set(QString("%1/%2").arg(PQCConfigFiles::get().EXTENSION_CONFIG_DIR(), extId), QSettings::IniFormat);

    for(const QString &c : cmds) {
        QSqlQuery query(QSqlDatabase::database("shortcuts"));
        query.exec(QString("SELECT `combo` FROM `shortcuts` WHERE `commands` LIKE '%1'").arg(c));
        if(query.lastError().text().trimmed().length()) {
            qWarning() << "Unable to query for" << c << "shortcut:" << query.lastError().text();
            return;
        }
        bool thisOne = false;
        if(query.next()) {
            thisOne = true;
            if(!foundOne)
                set.setValue("ExtShortcut", query.value(0).toString());
            foundOne = true;
        }

        query.clear();
        if(thisOne) {
            query.exec(QString("DELETE FROM `shortcuts` WHERE `commands` LIKE '%1'").arg(c));
            if(query.lastError().text().trimmed().length()) {
                qWarning() << "Unable to remove old shortcut for" << c << ":" << query.lastError().text();
            }
        }
    }

}

/******************************************************/
/******************************************************/

void PQCMigrateShortcuts::migrate491() {

    // These two checks were located in the validation function for qutie some time
    // After 4.9.1 they were moved here.

    QSqlQuery query1(QSqlDatabase::database("shortcuts"));
    if(!query1.exec("Update `shortcuts` SET `combo` = REPLACE(`combo`, 'Escape', 'Esc')"))
        qWarning() << "Error renaming Escape to Esc:" << query1.lastError().text();
    query1.clear();

    QSqlQuery query2(QSqlDatabase::database("shortcuts"));
    if(!query2.exec("Update `shortcuts` SET `combo` = REPLACE(`combo`, 'Delete', 'Del')"))
        qWarning() << "Error renaming Delete to Del:" << query2.lastError().text();

    query2.clear();

}

/******************************************************/
/******************************************************/

void PQCMigrateShortcuts::migrate460() {

    // Ctrl+Z is to be added for __undoTrash. If Ctrl+Z is already used, we need to fix this

    QSqlQuery query(QSqlDatabase::database("shortcuts"));

    query.exec("SELECT `combo` FROM `shortcuts` WHERE `combo` LIKE '%Ctrl%Z' AND `commands` NOT LIKE '%__undoTrash%'");
    if(query.lastError().text().trimmed().length()) {
        qWarning() << "Unable to query for shortcuts with Ctrl+Z:" << query.lastError().text();
        return;
    }

    bool CtrlZ = true;
    bool CtrlShiftZ = true;
    bool CtrlAltShiftZ = true;

    while(query.next()) {

        QString combo = query.value(0).toString();

        if(combo == "Ctrl+Z")
            CtrlZ = false;
        else if(combo == "Ctrl+Shift+Z")
            CtrlShiftZ = false;
        else if(combo == "Ctrl+Alt+Shift+Z")
            CtrlAltShiftZ = false;

    }

    query.clear();

    QString newcombo = "";
    if(CtrlZ)
        newcombo = "Ctrl+Z";
    else if(CtrlShiftZ)
        newcombo = "Ctrl+Shift+Z";
    else if(CtrlAltShiftZ)
        newcombo = "Ctrl+Alt+Shift+Z";

    if(newcombo != "") {

        QSqlQuery queryNew(QSqlDatabase::database("shortcuts"));

        queryNew.prepare("INSERT INTO shortcuts (`combo`,`commands`,`cycle`,`cycletimeout`,`simultaneous`) VALUES(:com,'__undoTrash',1,0,0)");
        queryNew.bindValue(":com", newcombo);
        if(!queryNew.exec())
            qWarning() << "Unable to insert __undoTrash shortcut";

        queryNew.clear();

    }

}

/******************************************************/
/******************************************************/

void PQCMigrateShortcuts::migrate440() {

    // Update 'Del' to 'Delete'

    // first update shortcut set to exactly 'Del'
    QSqlQuery query(QSqlDatabase::database("shortcuts"));
    if(!query.exec("Update `shortcuts` SET `combo`='Delete' WHERE `combo`='Del'"))
        qWarning() << "Unable to change 'Del' shortcut to 'Delete':" << query.lastError().text();
    query.clear();

    // next we update the ones with 'Del' no at the end
    QSqlQuery query2(QSqlDatabase::database("shortcuts"));
    if(query2.exec("SELECT `combo` FROM `shortcuts` WHERE combo LIKE 'Del+%'")) {

        while(query2.next()) {

            QString combo = query2.value(0).toString();
            QString comboNEW = query2.value(0).toString().replace("Del+", "Delete+");

            QSqlQuery queryUpd(QSqlDatabase::database("shortcuts"));
            queryUpd.prepare("UPDATE `shortcuts` SET `combo`=:combonew WHERE `combo`=:combo");
            queryUpd.bindValue(":combonew", comboNEW);
            queryUpd.bindValue(":combo", combo);
            if(!queryUpd.exec())
                qWarning() << "Unable to update 'Del' to 'Delete' in shortcut" << combo << "::" << queryUpd.lastError().text();
            queryUpd.clear();

        }

    }

    query2.clear();

    // next we update the ones with 'Del' at the end
    if(query.exec("SELECT `combo` FROM `shortcuts` WHERE combo LIKE '%+Del'")) {

        while(query.next()) {

            QString combo = query.value(0).toString();
            QString comboNEW = combo.replace("+Del", "+Delete");

            QSqlQuery queryUpd(QSqlDatabase::database("shortcuts"));
            queryUpd.prepare("UPDATE `shortcuts` SET `combo`=:combonew WHERE `combo`=:combo");
            queryUpd.bindValue(":combonew", comboNEW);
            queryUpd.bindValue(":combo", combo);
            queryUpd.exec();
            if(queryUpd.lastError().text().trimmed().length())
                qWarning() << "Unable to update 'Del' to 'Delete' in shortcut" << combo << "::" << queryUpd.lastError().text();
            queryUpd.clear();

        }

    }

    query.clear();

}

/******************************************************/
/******************************************************/

void PQCMigrateShortcuts::migrate400() {

    // make sure new table name exists and if not create it and populate it with default data

    QSqlQuery query(QSqlDatabase::database("shortcuts"));

    query.exec("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='shortcuts'");
    if(query.lastError().text().trimmed().length()) {
        qWarning() << "Unable to check if shortcuts table exists already:" << query.lastError().text();
        return;
    }
    query.next();

    // table does not exist yet
    if(query.value(0).toInt() == 0) {

        QSqlQuery queryCreate(QSqlDatabase::database("shortcuts"));
        queryCreate.exec("CREATE TABLE 'shortcuts' ('combo' TEXT UNIQUE, 'commands' TEXT, 'cycle' INTEGER, 'cycletimeout' INTEGER, 'simultaneous' INTEGER)");
        queryCreate.clear();

        if(!QFile::copy(":/shortcuts.db", PQCConfigFiles::get().CACHE_DIR() + "/shortcutstmp.db")) {
            qWarning() << "Unable to create shortcuts database";
            return;
        }

        QFile file(PQCConfigFiles::get().CACHE_DIR() + "/shortcutstmp.db");
        file.setPermissions(file.permissions()|QFileDevice::WriteOwner);

        QSqlQuery queryAttach(QSqlDatabase::database("shortcuts"));
        queryAttach.exec(QString("ATTACH DATABASE '%1' AS defaultdb").arg(PQCConfigFiles::get().CACHE_DIR() + "/shortcutstmp.db"));
        if(queryAttach.lastError().text().trimmed().length()) {
            qWarning() << "Unable to attach default database:" << queryAttach.lastError().text();
            return;
        }

        QSqlQuery queryInsert(QSqlDatabase::database("shortcuts"));
        queryInsert.exec("INSERT INTO shortcuts SELECT * FROM defaultdb.shortcuts;");
        if(queryInsert.lastError().text().trimmed().length()) {
            qWarning() << "Failed to insert default shortcuts:" << queryInsert.lastError().text();
        }
        queryInsert.clear();

        queryAttach.clear();

    }

    query.clear();

}

/******************************************************/

bool PQCMigrateShortcuts::helperCheckIfComboPresent(const QString combo) {

    QSqlQuery query(QSqlDatabase::database("shortcuts"));

    query.prepare("SELECT COUNT(combo) FROM shortcuts WHERE combo=':combo'");
    query.bindValue(":combo", combo);
    if(!query.exec()) {
        qWarning() << "Failed to check if combo" << combo << "is present:" << query.lastError().text();
        return false;
    }

    query.next();

    return query.value(0).toInt()>0;

}

void PQCMigrateShortcuts::helperInsertShortcut(const QString combo, const QString command,
                                               const int cycle, const int cycletimeout, const int simultaneous) {

    QSqlQuery query(QSqlDatabase::database("shortcuts"));

    query.prepare("INSERT INTO shortcuts (combo, commands, cycle, cycletimeout, simultaneous) VALUES (:com, :cmd, :cyc, :tim, :sim)");
    query.bindValue(":com", combo);
    query.bindValue(":cmd", command);
    query.bindValue(":cyc", cycle);
    query.bindValue(":tim", cycletimeout);
    query.bindValue(":sim", simultaneous);

    if(!query.exec())
        qWarning() << "Failed to enter new shortcut:" << combo << ":" << query.lastError().text();

}
