/*
 * 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: git://neueland.iserlohn-fortress.net/smolbote.git
 *
 * SPDX-License-Identifier: GPL-3.0
 */

#include "browser.h"
#include "mainwindow.h"
#include "version.h"
#include <QCommandLineParser>
#include <QDir>
#include <QStandardPaths>
#include <iostream>

// startup time measuring
#ifdef QT_DEBUG
#include <QElapsedTimer>
#endif

// read config into std::string, supports qrc
inline std::string readConfig(const QString &path)
{
    QFile conf(path);
    std::string ret;
    if(conf.open(QIODevice::ReadOnly)) {
        ret = conf.readAll().toStdString();
        conf.close();
    }
    return ret;
}

void bootstrapUserConfig(const std::string &path, Configuration &config)
{
    // set firstRun to false so we don't re-init on every run
    config.setValue<bool>("browser.firstRun", false);

    // The .path's need to be overriden because ~ doesn't translate to home
    const QString &home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);

    // filter.path
    std::string filterPath = config.value<std::string>("filter.path").value();
    config.setValue<std::string>("filter.path", patchHome(filterPath, home.toStdString()));

    // profile.path
    std::string profilePath = config.value<std::string>("profile.path").value();
    config.setValue<std::string>("profile.path", patchHome(profilePath, home.toStdString()));

    // bookmarks.path
    std::string bookmarksPath = config.value<std::string>("bookmarks.path").value();
    config.setValue<std::string>("bookmarks.path", patchHome(bookmarksPath, home.toStdString()));

    // downloads.path
    std::string downloadsPath = config.value<std::string>("downloads.path").value();
    config.setValue<std::string>("downloads.path", patchHome(downloadsPath, home.toStdString()));
}

int main(int argc, char *argv[])
{
    // Create application object
    Browser instance(argc, argv);
#ifdef GIT_VERSION
    instance.setApplicationVersion(GIT_VERSION);
#else
    instance.setApplicationVersion("1.0.0");
#endif

#ifdef QT_DEBUG
    QElapsedTimer timer;
    timer.start();
#endif

    QCommandLineParser parser;
    parser.setApplicationDescription("yet another no-frills browser");
    parser.addHelpOption();
    parser.addVersionOption();

    // user config, ~/.config/smolbote/smolbote.cfg or empty if there is none
    QCommandLineOption configOption({ "c", "config" }, "Set configuration file.", "path");
    configOption.setDefaultValue(QStandardPaths::locate(QStandardPaths::AppConfigLocation, "smolbote.cfg"));
    parser.addOption(configOption);

    // default config, :/poi.cfg
    QCommandLineOption defaultConfigOption("default-config", "Set the default configuration file.", "path");
    defaultConfigOption.setDefaultValue(":/poi.cfg");
    parser.addOption(defaultConfigOption);

    // print default config, so users can easily create their overrides
    QCommandLineOption printDefaultConfigOption("print-default-config", "Print default configuration.");
    parser.addOption(printDefaultConfigOption);

    // generate user config
    QCommandLineOption generateUserConfigOption("generate-user-config", "Generate user configuration and exit.");
    parser.addOption(generateUserConfigOption);

    QCommandLineOption profileOption({ "p", "profile" }, "Use this profile.", "PROFILE");
    profileOption.setDefaultValue("");
    parser.addOption(profileOption);

    QCommandLineOption socketOption("socket", "Set socket to use for IPC, leave blank for default, 'none' to disable.", "name");
    socketOption.setDefaultValue("");
    parser.addOption(socketOption);

    QCommandLineOption newWindowOption("in-new-window", "Open URL in new window");
    parser.addOption(newWindowOption);

    parser.addPositionalArgument("URL", "URL(s) to open");

    parser.process(instance);

#ifdef QT_DEBUG
    qDebug("config=%s", qUtf8Printable(parser.value(configOption)));
    qDebug("default-config=%s", qUtf8Printable(parser.value(defaultConfigOption)));
    qDebug("socket=%s", qUtf8Printable(parser.value(socketOption)));
    qDebug("profile=%s", qUtf8Printable(parser.value(profileOption)));
#endif

    if(parser.isSet(printDefaultConfigOption)) {
        std::cout << readConfig(parser.value(defaultConfigOption));
        std::cout.flush();
        return 0;
    }

    std::shared_ptr<Configuration> config = std::make_shared<Configuration>();
    config->readUserConfiguration(parser.value(configOption).toStdString());
    config->parseDefaultConfiguration(readConfig(parser.value(defaultConfigOption)));

    // check if first run
    if(config->value<bool>("browser.firstRun").value_or(true) || parser.isSet(generateUserConfigOption)) {
        // create a user config file

        QString path = parser.value(configOption);
        // make sure we have a path
        if(path.isEmpty()) {
            path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/smolbote.cfg";
        }
        // make sure the directory exists
        QDir configDir = QFileInfo(path).absoluteDir();
        if(!configDir.exists()) {
            configDir.mkpath(".");
        }

        // remove any existing config
        if(QFile::exists(path)) {
            QFile::remove(path);
        }

        config->parse(readConfig(parser.value(defaultConfigOption)));

        // patch paths
        bootstrapUserConfig(path.toStdString(), *config);

        std::cout << "Generating config to: " << qUtf8Printable(path) << (config->writeUserConfiguration(path.toStdString()) ? " ok" : " failed") << std::endl;

        // exit if this is the only thing we needed to do
        if(parser.isSet(generateUserConfigOption)) {
            return 0;
        }
    }

    // check for other instances
    // if we socket hasn't been disabled (socket is not none)
    if(parser.value(socketOption) != "none") {
        bool bindOk = instance.bindLocalSocket(parser.value(socketOption));
        if(bindOk) {
            qDebug("Connected to local socket: %s", qUtf8Printable(instance.serverName()));
        } else {
            // pass arguments to new instance
            return instance.sendMessage(parser.value(profileOption), parser.isSet(newWindowOption), parser.positionalArguments());
        }
    }

    instance.setConfiguration(config);

    instance.createSession(parser.value(profileOption), parser.isSet(newWindowOption), parser.positionalArguments());

#ifdef QT_DEBUG
    qDebug("Startup complete in %lldms", timer.elapsed());
#endif
    return instance.exec();
}