/*
 * This file is part of smolbote. It's copyrighted by the contributors recorded
 * in the version control history of the file, available from its original
 * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote
 *
 * SPDX-License-Identifier: GPL-3.0
 */

#include "browser.h"
#include "aboutdialog.h"
#include "aboutplugin.h"
#include "applicationmenu.h"
#include "bookmarks/bookmarkswidget.h"
#include "configuration.h"
#include "downloadswidget.h"
#include "mainwindow/addressbar.h"
#include "mainwindow/mainwindow.h"
#include "mainwindow/menubar.h"
#include "smolbote/plugininterface.hpp"
#include "subwindow/subwindow.h"
#include "util.h"
#include "webengine/webprofile.h"
#include "webengine/webprofilemanager.h"
#include "webengine/webview.h"
#include <QAction>
#include <QLibraryInfo>
#include <QPluginLoader>
#include <QTimer>
#include <QTranslator>
#include <QVersionNumber>
#include <pluginloader.h>
#include <spdlog/spdlog.h>
#include <version.h>

Browser::Browser(int &argc, char *argv[], bool allowSecondary)
    : SingleApplication(argc, argv, allowSecondary, SingleApplication::User | SingleApplication::SecondaryNotification | SingleApplication::ExcludeAppVersion)
{
    Configuration conf;

    setApplicationName(conf.value<QString>("poi.name").value());
    setWindowIcon(QIcon(conf.value<QString>("poi.icon").value()));
    setApplicationVersion(QVersionNumber::fromString(QLatin1String(poi_Version)).toString());

    if(const auto _translation = conf.value<QString>("browser.translation")) {
        auto *translator = new QTranslator(this);
        if(translator->load(_translation.value()))
            installTranslator(translator);
        else
            delete translator;
    }

    if(const auto _locale = conf.value<QString>("browser.locale")) {
        auto *locale = new QTranslator(this);
        if(locale->load("qt_" + _locale.value(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
            installTranslator(locale);
        else
            delete locale;
    }

    if(auto iconTheme = conf.value<QString>("browser.iconTheme")) {
        QIcon::setThemeName(iconTheme.value());
    }

    if(auto stylesheet = conf.value<QString>("browser.stylesheet")) {
        QFile f(stylesheet.value());
        if(f.open(QIODevice::ReadOnly)) {
            setStyleSheet(f.readAll());
            f.close();
        }
    }

    // content filter - register format plugins
    if(const auto hostlist_plugin = conf.value<QString>("smolblok.plugins.hostlist")) {
        content_filter.registerFormatPlugin("hostlist", hostlist_plugin.value());
    }
    if(const auto adblock_plugin = conf.value<QString>("smolblok.plugins.adblock")) {
        content_filter.registerFormatPlugin("adblock", adblock_plugin.value());
    }

    // load profiles
    {
        const auto profiles = Util::files(conf.value<QString>("profile.path").value(), { "*.profile" });
        const auto search = conf.value<QString>("profile.search").value();
        const auto homepage = QUrl::fromUserInput(conf.value<QString>("profile.homepage").value());
        const auto newtab = QUrl::fromUserInput(conf.value<QString>("profile.newtab").value());
        const auto default_id = conf.value<QString>("profile.default").value();
        m_profileManager = std::make_unique<WebProfileManager<false>>(profiles, default_id, search, homepage, newtab);
        m_profileManager->make_global();

        for(auto &id : m_profileManager->idList()) {
            spdlog::info("Added profile\t{}", qUtf8Printable(id));
        }

        // set default profile
        auto *profile = m_profileManager->profile(default_id);
        spdlog::info("Default profile\t{}{}\t{}", qUtf8Printable(default_id), profile->isOffTheRecord() ? "*" : "", qUtf8Printable(profile->name()));
    }

    // downloads
    m_downloads = std::make_unique<DownloadsWidget>(conf.value<QString>("downloads.path").value());
    m_profileManager->walk([this](const QString &, WebProfile *profile, QSettings *) {
        profile->setUrlRequestInterceptor(content_filter.interceptor());
        connect(profile, &QWebEngineProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload);
    });

    // bookmarks
    m_bookmarks = std::make_shared<BookmarksWidget>(QString::fromStdString(conf.value<std::string>("bookmarks.path").value()));
    connect(m_bookmarks.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) {
        m_windows.last()->createTab(url);
    });

    auto *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, m_bookmarks.get(), &BookmarksWidget::save);
    timer->start(5 * 60 * 1000); // 5min * 60sec * 1000ms
}

Browser::~Browser()
{
    if(m_bookmarks)
        m_bookmarks->save();

    for(auto *info : qAsConst(m_plugins))
        delete info;

    qDeleteAll(m_windows);
    m_windows.clear();
}

void Browser::about()
{
    auto *dlg = new AboutDialog;
    dlg->exec();
}

void Browser::aboutPlugins()
{
    auto *dlg = new AboutPluginDialog;
    for(auto *info : qAsConst(m_plugins)) {
        dlg->add(info->loader);
    }
    dlg->exec();
}

bool Browser::loadPlugin(const QString &path)
{
    if(path.isEmpty()) {
        return false;
    }

    Configuration conf;
    const auto state = PluginLoader::signature_state(
        conf.value<bool>("plugins.signature.ignored").value(),
        conf.value<bool>("plugins.signature.checked").value(),
        conf.value<bool>("plugins.signature.enforced").value());

    auto *loader = new PluginLoader(path, state, this);
    const bool loaded = loader->load();

    if(!loaded) {
        delete loader;
        return false;
    }
    auto *info = new PluginInfo(loader);
    m_plugins.append(info);

    emit pluginAdded(loader);
    return true;
}

void Browser::showWidget(QWidget *widget, MainWindow *where) const
{
    bool wasVisible = widget->isVisible();
    for(MainWindow *w : qAsConst(m_windows))
        w->removeDockWidget(widget);

    if(!wasVisible)
        where->addDockWidget(Qt::RightDockWidgetArea, widget);
}

void Browser::open(const QVector<Session::MainWindow> &data, bool merge)
{
    if(data.count() == 0 && merge) {
        m_windows.at(0)->createTab(QUrl());
    }

    if(data.count() == 1 && m_windows.count() >= 1 && merge) {
        const auto windowData = data.at(0);
        auto *window = m_windows.at(0);

        if(windowData.subwindows.count() == 1) {
            const auto subwindowData = windowData.subwindows.at(0);
            for(const auto &tab : subwindowData.tabs) {
                window->createTab(tab);
            }
        } else {
            for(const auto &window_data : windowData.subwindows) {
                window->createSubWindow(window_data);
            }
        }

        return;
    }

    for(const auto &windowData : data) {
        auto *menu = new ApplicationMenu(this);
        connect(this, &Browser::pluginAdded, menu, &ApplicationMenu::addPlugin);
        for(auto *info : qAsConst(m_plugins)) {
            menu->addPlugin(info->loader);
        }

        auto *window = new MainWindow(windowData, menu);
        connect(window->addressBar, &AddressBar::complete, m_bookmarks.get(), &BookmarksWidget::search);

        m_windows.append(window);
        // the window will delete itself when it closes, so we don't need to delete it
        connect(window, &MainWindow::destroyed, this, [this, window]() {
            m_windows.removeOne(window);
        });
    }
}