#include "gameres.h"
#include "aconfig.h"
#include "filesystem.h"
#include "anetwork.h"

#include <QApplication>
#include <QString>
#include <QDir>
#include <QFile>
#include <QTime>
#include <QProcess>

#include <string>

GameRes::GameRes() {}

// Public functions

void GameRes::openDatFile(int id) {
    QStringList dat_files;
    dat_files << AConfig::getInstance()->getValue("Local", "file")
              << "client_general.dat"
              << "client_sound.dat"
              << "client_surface.dat"
              << "client_highres.dat";

    if (dat_files[id] == nullptr) {
        emit openDatFileFinished(-100); // -100 = incorrect id
        return;
    }

    std::string filename = (AConfig::getInstance()->getValue("Local", "folder") + "/" + dat_files[id]).toStdString();

    qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Инициализация dat-файла. Открываем файл ", filename.c_str());
    int dat_state = GameRes::getInstance()->datfiles_[id].InitDatFile(filename, 0);
    qDebug("%s:%i: %s%d", __FILE__, __LINE__, "Состояние dat-файла: ", dat_state);

    emit openDatFileFinished(dat_state);
}

void GameRes::closeDatFile(int id) {
    int result = datfiles_[id].CloseDatFile();
    emit closeDatFileFinished(result);
    return;
}

void GameRes::applyPatch(QString name) {
    emit startedPatching(name);

    qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Начинаем применение патча ", name.toStdString().c_str());

    QDir dir(QApplication::applicationDirPath() + "/data");

    if (!dir.exists()) {
        emit applyAllFinished(-100); // -100 - not found data folder
        return;
    }

    QStringList paths = dir.entryList(QStringList(name + "*"));
    qDebug() << ("data/" + paths.first());

    if(!paths.empty()) {
        db_.InitDatabase(("data/" + paths.first()).toStdString());
        int indb = db_.CountRows();
        qInfo("%s:%i: %s%d", __FILE__, __LINE__, "Файлов в обновлении: ", indb);
        int percent = -1;

        for (int i = 0; i <= indb; i++) {
            if (i * 100 / indb > percent) {
                percent = i * 100 / indb;
                qInfo("%s:%i: %s%d%s", __FILE__, __LINE__, "Применение обновления: ", percent, "%");
                emit updatedPatchPercent(percent);
            }
            processFile();
         }

        AConfig::getInstance()->setValue("Applied", name, paths.first());
        db_.CloseDatabase();
    }

    emit applyPatchFinished(0);
}

void GameRes::setGameLocale(QString locale) {
    openDatFile(0);
    locale_ = datfiles_[0].current_locale();

    if(locale_ == PATCHED) qDebug() << "Starting " + locale + " version. Current locale is PATCHED";
    if(locale_ == ORIGINAL) qDebug() << "Starting " + locale + " version. Current locale is ORIGINAL";
    if(locale_ != ORIGINAL && locale_ != PATCHED) qDebug() << "Starting " + locale + " version. Current locale is UNKNOWN";

    if(locale == "RU" && locale_ != PATCHED) {qDebug()<< "Current locale Original"; datfiles_[0].SetLocale(PATCHED); locale_ = PATCHED;}
    if(locale == "Original" && locale_ != ORIGINAL) {qDebug()<< "Current locale RU"; datfiles_[0].SetLocale(ORIGINAL); locale_ = ORIGINAL;}
    closeDatFile(0);
    emit setGameLocaleFinished(0);
}

void GameRes::saveAllDatFiles(){
    for(int i = 0; i < 5; i++){
        datfiles_[i].CloseDatFile();
    }
}

void GameRes::startGame() {
    saveAllDatFiles();

    QStringList args;
    args << "-skiprawdownload" << "-nosplash";
    if (locale_ == PATCHED)
        args << "-disablePatch";

    QFile f(AConfig::getInstance()->getValue("Local", "folder") + "/TurbineLauncher.exe");
    QProcess process;
    if(FileSystem::fileExists(f.fileName())){
        if (f.fileName().contains(" "))
            f.setFileName("\"" + f.fileName() + "\"");

        process.startDetached(f.fileName(), args);
        process.waitForFinished(-1);
        process.deleteLater();
        emit startGameFinished(0);
        QApplication::quit();
    } else {
        //QString text = "Запуск не удался";
        //QString info = "Не удалось запустить игру. Во время запуска произшла непредвиденная ошибка (возможно, не найден файл TurbineLauncher.exe в папке с игрой. Проверьте в 'Настройках', что у вас указан верный путь к игре, и повторите попытку запуска).";
        //app->helper->myDialogBox(text, info, "OK", "Отмена", "gandalf.png", "", "", 400, 160, true, false);
        emit startGameFinished(-100);
    }
}

int GameRes::dateToTimestamp(QString customDateString, QString format) {
    int timestamp = QDateTime::fromString(customDateString, format).toTime_t();
    return timestamp;
}

void GameRes::installMicroPatch() {
    QString date = AConfig::getInstance()->getValue("Updates", "update");

    if(date == "false")
        date = QDate::currentDate().toString("yyyy-MM-dd");

    QTime time = QTime::currentTime();
    int timestamp = dateToTimestamp(date + " " + time.toString("hh:mm:ss"), "yyyy-MM-dd hh:mm:ss") - 2592000;

    qInfo("%s:%i: %s", __FILE__, __LINE__, "Опция активна. Начинаем загрузку обновлений");
    ANetwork::getInstance()->getMicroPatch(timestamp);
    connect(ANetwork::getInstance(), SIGNAL(ANetwork::getMicroPatchFinished(QString)), this, SLOT(applyMicroPatch(QString)));
}

void GameRes::applyAll() {
    // Применяем патч с заставочными экранами
    if(AConfig::getInstance()->getValue("Editor", "screens") == "true") {
        applyLoadscreens();
    }

    // Применяем остальные патчи
    QStringList names;
    names << "fonts" << "sounds" << "texts" << "images" << "videos" << "textures";

    QDir dir(QApplication::applicationDirPath() + "/data");
    QStringList need;

    foreach(QString name, names){
        QStringList list = dir.entryList(QStringList(name + "*"));
        if(list.size() > 0 && list.first() != ""){
            QString isset = AConfig::getInstance()->getValue("Applied", name);
            if(list.first() != isset && AConfig::getInstance()->getValue("Editor", name) == "true") {
                need.append(name);
            }
        }
    }

    if(need.size() > 0){
        openDatFile(0);
        foreach(QString name, need) {
            applyPatch(name);
        }
    }

    if(AConfig::getInstance()->getValue("Updates", "micro") == "true")
        installMicroPatch();

    emit applyAllFinished(0);
}

void GameRes::checkDatFile() {
    openDatFile(0);
    if(datfiles_[0].CheckIfUpdatedByGame()){
        emit checkDatFileFinished(1);
        return;
    }

    if(datfiles_[0].CheckIfNotPatched() && AConfig::getInstance()->getValue("Local", "runfirst") == "1"){
        emit checkDatFileFinished(2);
        return;
    }

    if(datfiles_[0].CheckIfPatchedByOldLauncher()){
        emit checkDatFileFinished(3);
        return;
    }
}

QStringList GameRes::getLotroPath(){
    QStringList paths;

    //#if defined(Q_WS_WIN)

    // Windows 7
    QSettings m("HKEY_CLASSES_ROOT\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache", QSettings::NativeFormat);
    foreach (QString key, m.allKeys()) {
      if(key.contains("TurbineLauncher.exe") && FileSystem::fileExists(key)){
          paths.append(key.replace("/TurbineLauncher.exe", ""));
      }
    }

    // Windows 8, 10
    QSettings n("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\12bbe590-c890-11d9-9669-0800200c9a66_is1", QSettings::NativeFormat);
    foreach (QString key, n.allKeys()) {
        qDebug() << key;
        if(key.contains("InstallLocation") || key.contains("installlocation")){
            QString folder = n.value(key).toString().replace("\\", "/").replace("/TurbineLauncher.exe", "").replace("\"", "");
            if(FileSystem::fileExists(folder + "/TurbineLauncher.exe")) paths.append(folder);
        }
    }

    //#else
        // Реализация под Linux
    //#endif


    return paths;
}

// Private functions

void GameRes::processFile() {
    SubfileData subfile;
    subfile = db_.GetNextFile();
    if (subfile.Empty()){
        qInfo("%s:%i: %s", __FILE__, __LINE__, "Достигнут конец файла.");
    } else {
        int dat_id = subfile.options["did"].as<int>();
        int dat_state = datfiles_[dat_id].InitDatFile(getDatPath(dat_id), dat_id);
        if(dat_state > 0)
            datfiles_[dat_id].PatchFile(subfile);
    }
}

void GameRes::applyMicroPatch(QString filename) {
    emit startedPatching(" новые переводы");

    db_.InitDatabase(filename.toStdString());

    int indb = db_.CountRows();
    qInfo("%s:%i: %s%d", __FILE__, __LINE__, "Файлов в обновлении: ", indb);
    int percent = -1;

    for(int i = 0; i < indb; i++) {
        if (i * 100 / indb > percent) {
            percent = i * 100 / indb;
            qInfo("%s:%i: %s%d%s", __FILE__, __LINE__, "Применение обновления: ", percent, "%");
            emit updatedPatchPercent(percent);
        }
        processFile();
    }

    db_.CloseDatabase();

    QDir dir("/data/micro");
    FileSystem::clearFolder(dir);
    emit installMicroPatchFinished(0);
}

void GameRes::applyLoadscreens() {
    QString datafolder = QApplication::applicationDirPath() + "/data";
    QDir dir(datafolder);
    if (!dir.exists())
        return;

    QStringList paths = dir.entryList(QStringList("loadscreens*"));

    if(!paths.empty()){
        if(paths.first() == AConfig::getInstance()->getValue("Applied", "screens")){
            return;
        }

        QString lang = AConfig::getInstance()->getValue("Local", "lang");
        QString folder = AConfig::getInstance()->getValue("Local", "folder") + "/raw/" + lang + "/logo/";
        SubfileData subfile;
        QStringList filenames;
        QString mainscreen = (lang == "en" ? "lotro_ad_pregame.jpg" : "lotro_ad_pregame_" + lang + ".jpg");
        filenames << mainscreen << "lotro_generic_teleport_screen_01.jpg" << "lotro_generic_teleport_screen_02.jpg"
                  << "lotro_generic_teleport_screen_03.jpg" << "lotro_generic_teleport_screen_04.jpg" << "lotro_generic_teleport_screen_05.jpg"
                  << "lotro_generic_teleport_screen_06.jpg" << "lotro_generic_teleport_screen_07.jpg" << "lotro_generic_teleport_screen_08.jpg"
                  << "lotro_generic_teleport_screen_09.jpg" << "lotro_generic_teleport_screen_10.jpg";

        QString basename = datafolder + "/" + paths.first();
        db_.InitDatabase(basename.toStdString());
        int indb = db_.CountRows();
        qInfo("%s:%i: %s%d", __FILE__, __LINE__, "Файлы загрузочных экранов: ", indb);

        for (int i = 0; i < indb; i++) {
            subfile = db_.GetNextFile();
            qDebug() << i;
            if (!subfile.Empty()){
                qInfo("%s:%i: %s", __FILE__, __LINE__, (folder + filenames[i]).toStdString().c_str());
                QFile::remove(folder + filenames[i]);
                subfile.binary_data.WriteToFile((folder + filenames[i]).toStdString());
            }
        }
        db_.CloseDatabase();
    }
    qInfo("%s:%i: %s", __FILE__, __LINE__, "Выполнено.");
}

bool GameRes::isDatReady() { // Checks if file is ready to write data
    bool free = false;
    QString dir = AConfig::getInstance()->getValue("Local", "folder");
    QString file = AConfig::getInstance()->getValue("Local", "file");
    QFile fl(dir+"/" + file);
    qDebug() << fl.fileName();

    if(FileSystem::fileExists(fl.fileName())){
        QDir game_dir(dir);
        free = game_dir.rename(fl.fileName(), fl.fileName() + "99");
        game_dir.rename(fl.fileName() + "99", fl.fileName());
    } else {
        free = false;
    }

    if (free == true)
        qDebug() << "Dat is FREE"; else qDebug() << "Dat is BUSY";

    return free;
}

std::string GameRes::getDatPath(int id){
    QStringList dats;
    dats << AConfig::getInstance()->getValue("Local", "file") << "client_general.dat" << "client_sound.dat" << "client_surface.dat" << "client_highres.dat";
    //std::string file = (app->config->getValue("Local", "folder") + "/" + dats.at(id)).toStdString();
    return (AConfig::getInstance()->getValue("Local", "folder") + "/" + dats.at(id)).toStdString();
}
