// Squawk messenger. 
// Copyright (C) 2019  Yury Gubich <blue@macaw.me>
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "root.h"
#include <QDebug>
#include <QThread>
#include <QLibraryInfo>
#include <QStandardPaths>

#include <string>

#include <shared/pathcheck.h>

const std::vector<unsigned int> Root::appIconSizes({
    16, 24, 32, 48, 64, 96, 128, 256, 512
});

Root::Root(int& argc, char *argv[]) :
    QApplication(argc, argv),
    signalCatcher(this),
    defaultTranslator(),
    currentTranslator(),
    appIcon(),
    settings(),
    componentsInitialized(false),
    global(nullptr),
    coreThread(nullptr),
    core(nullptr),
    gui(nullptr)
{
    setApplicationName("squawk");
    setOrganizationName("macaw.me");
    setApplicationDisplayName("Squawk");
    setApplicationVersion("0.2.3");
    setDesktopFileName("squawk");

    initializeTranslation();
    initializeAppIcon();

    global = new Shared::Global();  //important to instantiate after initialization of translations;
}

Root::~Root() {
    if (componentsInitialized) {
        delete gui;
        if (core != nullptr)
            delete core;
        delete coreThread;
    }
    delete global;
}


void Root::initializeTranslation() {
    defaultTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
    installTranslator(&defaultTranslator);

    QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
    bool found = false;
    for (QString share : shares) {
        found = currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
        if (found) {
            break;
        }
    }
    if (!found) {
        currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
    }

    installTranslator(&currentTranslator);
}

void Root::initializeAppIcon() {
    for (std::vector<unsigned int>::size_type i = 0; i < appIconSizes.size(); ++i)
        appIcon.addFile(":images/logo.svg", QSize(appIconSizes[i], appIconSizes[i]));

    Root::setWindowIcon(appIcon);
}

bool Root::initializeSettings() {
    QVariant vs = settings.value("style");
    if (vs.isValid()) {
        QString style = vs.toString().toLower();
        if (style != "system") {
            Shared::Global::setStyle(style);
        }
    }

    if (Shared::Global::supported("colorSchemeTools")) {
        QVariant vt = settings.value("theme");
        if (vt.isValid()) {
            QString theme = vt.toString();
            if (theme.toLower() != "system") {
                Shared::Global::setTheme(theme);
            }
        }
    }

    QString path = Shared::downloadsPathCheck();
    if (path.size() > 0) {
        settings.setValue("downloadsPath", path);
    } else {
        qDebug() << "couldn't initialize directory for downloads, quitting";
        return false;
    }

    return true;
}

int Root::run() {
    if (!componentsInitialized)
        initializeComponents();

    coreThread->start();
    int result = exec();
    qDebug("Event loop stopped");

    if (result == 0) {
        processEvents();                    //I dont like all of this mess
        if (coreThread->isRunning()) {      //but it's the best solution for now
            if (core != nullptr) {          //Ideally, following line should never appear in the log
                qDebug() << "Core is still seems to be running, killing manually";
                core->deleteLater();
                coreThread->quit();
                processEvents();
                core = nullptr;
            }
            coreThread->wait();
        }
    }

    return result;
}

void Root::initializeComponents() {
    core = new Core::Squawk();
    coreThread = new QThread();
    core->moveToThread(coreThread);
    gui = new Application(core);

    QObject::connect(&signalCatcher, &SignalCatcher::interrupt, gui, &Application::quit);

    QObject::connect(coreThread, &QThread::started, core, &Core::Squawk::start);
    QObject::connect(gui, &Application::quitting, core, &Core::Squawk::stop);

    QObject::connect(core, &Core::Squawk::quit, core, &Core::Squawk::deleteLater);
    QObject::connect(core, &Core::Squawk::destroyed, this, &Root::onCoreDestroyed);
    QObject::connect(core, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
    QObject::connect(coreThread, &QThread::finished, this, &Root::quit, Qt::QueuedConnection);

    componentsInitialized = true;
}

bool Root::notify(QObject* receiver, QEvent* e) {
    try {
        return QApplication::notify(receiver, e);
    } catch(const std::runtime_error& e) {
        qDebug() << "std::runtime_error in thread:" << QThread::currentThreadId();
        qDebug() << "error message:" << e.what();
    } catch(const std::exception& e) {
        qDebug() << "std::exception in thread:" << QThread::currentThreadId();
        qDebug() << "error message:" << e.what();
    } catch(const int& e) {
        qDebug() << "integer exception in thread:" << QThread::currentThreadId();
        qDebug() << "thrown integer:" << std::to_string(e).c_str();
    } catch(...) {
        qDebug() << "unhandled exception thread:" << QThread::currentThreadId();
    }

    qDebug() << "Squawk is crashing...";
    exit(1);
    return false;
}

void Root::onCoreDestroyed() {
    core = nullptr;
}