#include "GUI.h"
#include "Logging/Logger.h"
#include "Logging/LoggerInterface.h"
#include "Translation/GetTextTranslator.h"
#include "Utils/ConfigurationLoader.h"
#include "Utils/ConfigurationResolver.h"
#include "Utils/DRIQuery.h"
#include "Utils/DRMDeviceFactory.h"
#include "Utils/EGLDisplayFactory.h"
#include "Utils/GBMDeviceFactory.h"
#include "Utils/PCIDatabaseQuery.h"
#include "Utils/WaylandQuery.h"
#include "Utils/Writer.h"

#include <fstream>
#include <gtkmm.h>
#include <gtkmm/messagedialog.h>
#include <memory>

void setupLocale(TranslatorInterface* translator, LoggerInterface* logger);
void onWindowActivated(const Glib::RefPtr<Gtk::Application>& app, GUI*);

int main(int argc, char* argv[]) {
    char* verbosity = std::getenv("VERBOSITY");
    auto logger     = std::make_shared<Logger>();
    logger->setLevel(LoggerLevel::INFO);
    auto translator = std::make_shared<GetTextTranslator>();

    if (verbosity != nullptr) {
        Glib::ustring verbosityStr(verbosity);
        if (verbosityStr == "DEBUG") {
            logger->setLevel(LoggerLevel::DEBUG);
        }

        if (verbosityStr == "INFO") {
            logger->setLevel(LoggerLevel::INFO);
        }

        if (verbosityStr == "WARNING") {
            logger->setLevel(LoggerLevel::WARNING);
        }

        if (verbosityStr == "ERROR") {
            logger->setLevel(LoggerLevel::ERROR);
        }
    }

    try {
        setupLocale(translator.get(), logger.get());

        logger->debug(translator->trns("Creating the GTK application object"));
        /* Start the GUI work */
        auto app = Gtk::Application::create("org.freedesktop.adriconf");
        char* waylandDisplay = std::getenv("WAYLAND_DISPLAY");
        bool isWayland       = waylandDisplay != nullptr;

        Parser parser(logger.get(), translator.get());
        PCIDatabaseQuery pciQuery;
        GBMDeviceFactory gbmDeviceFactory(translator.get());
        DRMDeviceFactory drmDeviceFactory;
        EGLDisplayFactory eglDisplayFactory(translator.get());
        DRIQuery check(logger.get(),
                       translator.get(),
                       &parser,
                       &pciQuery,
                       &drmDeviceFactory,
                       &gbmDeviceFactory,
                       &eglDisplayFactory);
        std::shared_ptr<DisplayServerQueryInterface> displayQuery = std::make_shared<WaylandQuery>(logger.get(),
                                                                                                   translator.get(),
                                                                                                   &parser,
                                                                                                   &eglDisplayFactory);
        if (isWayland) {
            logger->info(translator->trns("adriconf running on Wayland"));
        } else {
            logger->info(translator->trns("adriconf running on X11"));
        }

        logger->debug(translator->trns("Checking if the system is supported"));
        if (!displayQuery->checkNecessaryExtensions()) {
            return 1;
        }

        Writer writer;
        ConfigurationResolver resolver(logger.get(), translator.get());
        ConfigurationLoader loader(check,
                                   displayQuery.get(),
                                   logger.get(),
                                   translator.get(),
                                   &parser,
                                   &resolver);
        GUI gui(logger.get(), translator.get(), &loader, &resolver, &writer);

        app->signal_activate().connect(
            sigc::bind(sigc::ptr_fun(&onWindowActivated), app, &gui));

        return app->run(argc, argv);
    } catch (const Glib::FileError& ex) {
        logger->error(Glib::ustring::compose("FileError: %1", ex.what()));
        return 1;
    } catch (const Glib::MarkupError& ex) {
        logger->error(Glib::ustring::compose("MarkupError: %1", ex.what()));
        return 1;
    } catch (const Gtk::BuilderError& ex) {
        logger->error(Glib::ustring::compose("BuilderError: %1", ex.what()));
        return 1;
    } catch (const std::exception& ex) {
        logger->error(Glib::ustring::compose("Generic Error: %1", ex.what()));
        return 1;
    }
}

void onWindowActivated(const Glib::RefPtr<Gtk::Application>& app, GUI* gui) {
    /* No need to worry about the window pointer as the gui object owns it */
    Gtk::Window* pWindow = gui->getWindowPointer();
    pWindow->show();

    app->add_window(*pWindow);
}

void setupLocale(TranslatorInterface* translator, LoggerInterface* logger) {
    std::locale l("");
    std::locale::global(l);
    std::cout.imbue(l);
    textdomain("adriconf");

    std::ifstream flatpakInfo("/.flatpak-info");

    if (flatpakInfo.good()) {
        bindtextdomain("adriconf", "/app/share/locale");
    }

    logger->debug(
        Glib::ustring::compose(translator->trns("Current language code is %1"),
                               l.name()));
}
