
#include "CutterApplication.h"
#include "core/MainWindow.h"
#include "common/UpdateWorker.h"
#include "CutterConfig.h"
#include "common/SettingsUpgrade.h"

#include <QJsonObject>
#include <QJsonArray>
#include <iostream>

/**
 * @brief Attempt to connect to a parent console and configure outputs.
 */
#ifdef Q_OS_WIN
#    include <windows.h>

static void connectToConsole()
{
    BOOL attached = AttachConsole(ATTACH_PARENT_PROCESS);

    // Avoid reconfiguring stdin/stderr/stdout if one of them is already connected to a stream.
    // This can happen when running with stdout/stderr redirected to a file.
    if (0 > fileno(stdin)) {
        // Overwrite FD 0, FD 1 and 2 for the benefit of any code that uses the FDs
        // directly.  This is safe because the CRT allocates FDs 0, 1 and
        // 2 at startup even if they don't have valid underlying Windows
        // handles.  This means we won't be overwriting an FD created by
        // _open() after startup.
        _close(0);

        freopen(attached ? "CONIN$" : "NUL", "r+", stdin);
    }
    if (0 > fileno(stdout)) {
        _close(1);

        if (freopen(attached ? "CONOUT$" : "NUL", "a+", stdout)) {
            // Avoid buffering stdout/stderr since IOLBF is replaced by IOFBF in Win32.
            setvbuf(stdout, nullptr, _IONBF, 0);
        }
    }
    if (0 > fileno(stderr)) {
        _close(2);

        if (freopen(attached ? "CONOUT$" : "NUL", "a+", stderr)) {
            setvbuf(stderr, nullptr, _IONBF, 0);
        }
    }

    // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
    std::ios::sync_with_stdio();
}
#endif

int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
    connectToConsole();
#endif

    qRegisterMetaType<QList<StringDescription>>();
    qRegisterMetaType<QList<FunctionDescription>>();

    QCoreApplication::setOrganizationName("rizin");
#ifndef Q_OS_MACOS // don't set on macOS so that it doesn't affect config path there
    QCoreApplication::setOrganizationDomain("rizin.re");
#endif
    QCoreApplication::setApplicationName("cutter");

    // Importing settings after setting rename, needs separate handling in addition to regular
    // version to version upgrade.
    if (Cutter::shouldOfferSettingImport()) {
        Cutter::showSettingImportDialog(argc, argv);
    }

    Cutter::initializeSettings();

    QCoreApplication::setAttribute(
            Qt::AA_ShareOpenGLContexts); // needed for QtWebEngine inside Plugins
#ifdef Q_OS_WIN
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#    if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
    QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
            Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#    endif
#endif

    CutterApplication a(argc, argv);

    Cutter::migrateThemes();

    if (Config()->getAutoUpdateEnabled()) {
#if CUTTER_UPDATE_WORKER_AVAILABLE
        UpdateWorker *updateWorker = new UpdateWorker;
        QObject::connect(updateWorker, &UpdateWorker::checkComplete,
                         [=](const QVersionNumber &version, const QString &error) {
                             if (error.isEmpty()
                                 && version > UpdateWorker::currentVersionNumber()) {
                                 updateWorker->showUpdateDialog(true);
                             }
                             updateWorker->deleteLater();
                         });
        updateWorker->checkCurrentVersion(7000);
#endif
    }

    int ret = a.exec();

    return ret;
}
