#ifndef DEBUG
#include "CrashHandler.h"
#endif

#include "MegaApplication.h"
#include "MegaProxyStyle.h"
#include "Platform.h"
#include "PowerOptions.h"
#include "ProxyStatsEventHandler.h"
#include "qtlockedfile/qtlockedfile.h"

#include <QFontDatabase>

#include <cassert>
#include <iostream>

#ifdef Q_OS_LINUX
#include <condition_variable>
#include <signal.h>
#endif

#ifndef WIN32
#include <unistd.h>
#else
#include <Psapi.h>
#include <Shellapi.h>
#include <Strsafe.h>
#include <Windows.h>

#include <winrt/base.h>
#endif

#if defined(WIN32) || defined(Q_OS_LINUX)
#include "ScaleFactorManager.h"

#include <QScreen>
#endif

using namespace mega;
using namespace std;

struct LogMessage
{
    int logLevel;
    QString message;

    LogMessage(int logLevel, QString message):logLevel{logLevel}, message{message}{};
};
std::vector<LogMessage> logMessages;

void msgHandler(QtMsgType type, const char *msg)
{
    switch (type)
    {
        case QtInfoMsg:
            MegaApi::log(MegaApi::LOG_LEVEL_INFO, QString::fromUtf8("Qt Info: %1").arg(QString::fromUtf8(msg)).toUtf8().constData());
            break;
        case QtDebugMsg:
            MegaApi::log(MegaApi::LOG_LEVEL_DEBUG, QString::fromUtf8("Qt Debug: %1").arg(QString::fromUtf8(msg)).toUtf8().constData());
            break;
        case QtWarningMsg:
            MegaApi::log(MegaApi::LOG_LEVEL_WARNING, QString::fromUtf8("Qt Warning: %1").arg(QString::fromUtf8(msg)).toUtf8().constData());
            break;
        case QtCriticalMsg:
            MegaApi::log(MegaApi::LOG_LEVEL_ERROR, QString::fromUtf8("Qt Critical: %1").arg(QString::fromUtf8(msg)).toUtf8().constData());
            break;
        case QtFatalMsg:
            MegaApi::log(MegaApi::LOG_LEVEL_FATAL, QString::fromUtf8("Qt FATAL: %1").arg(QString::fromUtf8(msg)).toUtf8().constData());
            break;
        default:
            MegaApi::log(MegaApi::LOG_LEVEL_MAX, QString::fromUtf8("Qt MSG: %1").arg(QString::fromUtf8(msg)).toUtf8().constData());
            break;
    }
}

#ifdef Q_OS_LINUX
MegaApplication* theapp = NULL;
std::mutex mtxcondvar;
std::condition_variable condVarRestart;
QString appExecPath;
QStringList appArgs;
const QString waitForSignalArg = QLatin1String("--waitforsignal");

void LinuxSignalHandler(int signum)
{
    if (signum == SIGUSR2)
    {
        std::unique_lock<std::mutex> lock(mtxcondvar);
        condVarRestart.notify_one();
    }
    else if (signum == SIGUSR1)
    {
        appArgs << waitForSignalArg;
        bool success = QProcess::startDetached(appExecPath, appArgs);
        cout << "Started detached MEGAsync to wait for restart signal: "
             << appExecPath.toUtf8().constData() << " "
             << appArgs.join(QLatin1Char(' ')).toUtf8().constData() << " "
             << (success ? "OK" : "FAILED!") << endl;

        if (theapp)
        {
            theapp->tryExitApplication(true);
        }
    }
}

#endif

    void messageHandler(QtMsgType type,const QMessageLogContext &context, const QString &msg)
    {
        switch (type)
        {
            case QtInfoMsg:
                MegaApi::log(MegaApi::LOG_LEVEL_INFO, QString::fromUtf8("Qt Info: %1").arg(msg).toUtf8().constData());
                // deliberately not showing context for Info level
                break;
            case QtDebugMsg:
                MegaApi::log(MegaApi::LOG_LEVEL_DEBUG, QString::fromUtf8("Qt Debug: %1").arg(msg).toUtf8().constData());
                MegaApi::log(MegaApi::LOG_LEVEL_DEBUG,
                             QString::fromUtf8("Qt Context: %1 %2 %3 %4 %5")
                                 .arg(QString::fromUtf8(context.category),
                                      QString::fromUtf8(context.file),
                                      QString::fromUtf8(context.function),
                                      QString::fromUtf8(context.file))
                                 .arg(context.version)
                                 .toUtf8()
                                 .constData());
                break;
            case QtWarningMsg:
                MegaApi::log(MegaApi::LOG_LEVEL_WARNING, QString::fromUtf8("Qt Warning: %1").arg(msg).toUtf8().constData());
                MegaApi::log(MegaApi::LOG_LEVEL_WARNING,
                             QString::fromUtf8("Qt Context: %1 %2 %3 %4 %5")
                                 .arg(QString::fromUtf8(context.category),
                                      QString::fromUtf8(context.file),
                                      QString::fromUtf8(context.function),
                                      QString::fromUtf8(context.file))
                                 .arg(context.version)
                                 .toUtf8()
                                 .constData());
                break;
            case QtCriticalMsg:
                MegaApi::log(MegaApi::LOG_LEVEL_ERROR, QString::fromUtf8("Qt Critical: %1").arg(msg).toUtf8().constData());
                MegaApi::log(MegaApi::LOG_LEVEL_ERROR,
                             QString::fromUtf8("Qt Context: %1 %2 %3 %4 %5")
                                 .arg(QString::fromUtf8(context.category),
                                      QString::fromUtf8(context.file),
                                      QString::fromUtf8(context.function),
                                      QString::fromUtf8(context.file))
                                 .arg(context.version)
                                 .toUtf8()
                                 .constData());
                break;
            case QtFatalMsg:
                MegaApi::log(MegaApi::LOG_LEVEL_FATAL, QString::fromUtf8("Qt FATAL: %1").arg(msg).toUtf8().constData());
                MegaApi::log(MegaApi::LOG_LEVEL_FATAL,
                             QString::fromUtf8("Qt Context: %1 %2 %3 %4 %5")
                                 .arg(QString::fromUtf8(context.category),
                                      QString::fromUtf8(context.file),
                                      QString::fromUtf8(context.function),
                                      QString::fromUtf8(context.file))
                                 .arg(context.version)
                                 .toUtf8()
                                 .constData());
                break;
            default:
                MegaApi::log(MegaApi::LOG_LEVEL_MAX, QString::fromUtf8("Qt MSG: %1").arg(msg).toUtf8().constData());
                MegaApi::log(MegaApi::LOG_LEVEL_MAX,
                             QString::fromUtf8("Qt Context: %1 %2 %3 %4 %5")
                                 .arg(QString::fromUtf8(context.category),
                                      QString::fromUtf8(context.file),
                                      QString::fromUtf8(context.function),
                                      QString::fromUtf8(context.file))
                                 .arg(context.version)
                                 .toUtf8()
                                 .constData());
                break;
        }
    }

void removeSyncData(const QString &localFolder, const QString & name, const QString &syncID)
{
    Platform::getInstance()->syncFolderRemoved(localFolder, name, syncID);

    #ifdef WIN32
    // unhide debris folder
    QString debrisPath = QDir::toNativeSeparators(localFolder +
            QDir::separator() + QString::fromUtf8(MEGA_DEBRIS_FOLDER));

    WIN32_FILE_ATTRIBUTE_DATA fad;
    if (GetFileAttributesExW((LPCWSTR)debrisPath.utf16(), GetFileExInfoStandard, &fad))
    {
        SetFileAttributesW((LPCWSTR)debrisPath.utf16(), fad.dwFileAttributes & ~FILE_ATTRIBUTE_HIDDEN);
    }

    QDir dir(debrisPath);
    QFileInfoList fList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden);
    for (int j = 0; j < fList.size(); j++)
    {
        QString folderPath = QDir::toNativeSeparators(fList[j].absoluteFilePath());
        WIN32_FILE_ATTRIBUTE_DATA fa;
        if (GetFileAttributesExW((LPCWSTR)folderPath.utf16(), GetFileExInfoStandard, &fa))
        {
            SetFileAttributesW((LPCWSTR)folderPath.utf16(), fa.dwFileAttributes & ~FILE_ATTRIBUTE_HIDDEN);
        }
    }
    #endif
}

void freeStaticResources()
{
    PowerOptions::appShutdown();
    Platform::destroy();
}

#if defined(WIN32) || defined(Q_OS_LINUX)
void sendUninstallEvent(std::shared_ptr<Preferences> preferences)
{
    if (preferences->installationTime() != -1)
    {
        auto megaApi = std::make_unique<MegaApi>(Preferences::CLIENT_KEY,
                                                 (char *)NULL,
                                                 Preferences::USER_AGENT.toUtf8().constData());
        auto statsEventHandler = std::make_unique<ProxyStatsEventHandler>(megaApi.get());
        statsEventHandler->sendEvent(AppStatsEvents::EventType::UNINSTALL,
                                     { QString::number(preferences->installationTime()),
                                       QString::number(preferences->accountCreationTime()),
                                       QString::number(preferences->hasLoggedIn()) },
                                     true);
        QThread::msleep(500);
    }
}
#endif

void uninstall()
{
    auto preferences = Preferences::instance();
    preferences->initialize(MegaApplication::applicationDataPath());
    if (!preferences->error())
    {
        if (preferences->logged())
        {
            preferences->unlink();
        }

        const auto numUsers = preferences->getNumUsers();
        for (auto i = 0; i < numUsers; ++i)
        {
            preferences->enterUser(static_cast<int>(i));

            // now for the new syncs cached configurations
            auto loadedSyncs = preferences->getLoadedSyncsMap();
            for (auto it = loadedSyncs.begin(); it != loadedSyncs.end(); it++)
            {
                removeSyncData(it.value()->getLocalFolder(), it.value()->name(true), it.value()->getSyncID());
            }

            preferences->leaveUser();
        }
    }

    // Close preferences to unlock cfg file
    preferences->sync();

    QDir dir(MegaApplication::applicationDataPath());
    dir.removeRecursively();
    Platform::getInstance()->uninstall();

#ifdef WIN32
    sendUninstallEvent(preferences);
#endif

    freeStaticResources();
}

int main(int argc, char *argv[])
{
    QCoreApplication::setOrganizationName(QString::fromUtf8("Mega Limited"));
    QCoreApplication::setOrganizationDomain(QString::fromUtf8("mega.co.nz"));
    QCoreApplication::setApplicationName(QString::fromUtf8("MEGAsync")); //Do not change app name, keep MEGAsync because Linux rely on that for app paths.
    QCoreApplication::setApplicationVersion(QString::number(Preferences::VERSION_CODE));

    Platform::create();

    // This call is responsible for rebuilding the Qt symlinks in platforms where it applies.
    // This needs to be done when starting the first time after an update.
    // For this to work, the call needs to know the version of the app BEFORE updating,
    // so needs to be made before the file megasync.version is updated.
    Platform::getInstance()->processSymLinks();

    if ((argc == 2) && !strcmp("/uninstall", argv[1]))
    {
        uninstall();
        return 0;
    }
#ifdef Q_OS_LINUX
    else if((argc == 2) && !strcmp("--send-uninstall-event", argv[1]))
    {
        auto preferences = Preferences::instance();
        preferences->initialize(MegaApplication::applicationDataPath());
        sendUninstallEvent(preferences);
        return 0;
    }
#endif

#ifdef Q_OS_LINUX

    // Ensure interesting signals are unblocked.
    sigset_t signalstounblock;
    sigemptyset (&signalstounblock);
    sigaddset(&signalstounblock, SIGUSR1);
    sigaddset(&signalstounblock, SIGUSR2);
    sigprocmask(SIG_UNBLOCK, &signalstounblock, NULL);

    if (signal(SIGUSR1, LinuxSignalHandler))
    {
        cerr << " Failed to register signal SIGUSR1 " << endl;
    }

    // Get app parameters
    for (int i = 0; i < argc; ++i)
    {
        appArgs << QString::fromUtf8(argv[i]);
    }
    appExecPath = appArgs.takeFirst();

    // Process "waitforsignal" if passed
    if (appArgs.contains(waitForSignalArg))
    {
        std::unique_lock<std::mutex> lock(mtxcondvar);
        if (signal(SIGUSR2, LinuxSignalHandler))
        {
            cerr << " Failed to register signal SIGUSR2 " << endl;
        }

        cout << "Waiting for signal to restart MEGAsync ... " << endl;
        if (condVarRestart.wait_for(lock, std::chrono::minutes(30)) == std::cv_status::no_timeout)
        {
            appArgs.removeAll(waitForSignalArg);
            bool success = QProcess::startDetached(appExecPath, appArgs);
            cout << "Restarting MEGAsync: " << appExecPath.toUtf8().constData() << " "
                 << appArgs.join(QLatin1Char(' ')).toUtf8().constData() << " "
                 << (success ? "OK" : "FAILED!") << endl;
            freeStaticResources();
            exit(!success);
        }

        cout << "Timed out waiting for restart signal" << endl;
        freeStaticResources();
        exit(2);
    }

    // Block SIGUSR2 for normal execution: we don't want it to kill the process, in case there's a rogue update going on.
    sigset_t signalstoblock;
    sigemptyset (&signalstoblock);
    sigaddset(&signalstoblock, SIGUSR2);
    sigprocmask(SIG_BLOCK, &signalstoblock, NULL);
#endif

    // adds thread-safety to OpenSSL
    QSslSocket::supportsSsl();

    Platform::getInstance()->setRenderingBackend();

#ifndef Q_OS_MACOS
    const QString autoScreenScaleFactor = qEnvironmentVariable("QT_AUTO_SCREEN_SCALE_FACTOR");
    if (autoScreenScaleFactor == QString::fromUtf8("0"))
    {
        logMessages.emplace_back(MegaApi::LOG_LEVEL_DEBUG,
                                 QStringLiteral("auto screen scale factor disabled because of "
                                                "QT_AUTO_SCREEN_SCALE_FACTOR set to 0"));
    }
    else
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
    }
#endif

#ifdef Q_OS_MACOS

    bool harfbuzzEnabled = qputenv("QT_HARFBUZZ","old");

    // From QT (5.9) documentation:
    // Secure Transport SSL backend on macOS may update the default keychain (the default is probably your login keychain) by importing your local certificates and keys.
    // This can also result in system dialogs showing up and asking for permission when your application is using these private keys.
    // If such behavior is undesired, set the QT_SSL_USE_TEMPORARY_KEYCHAIN environment variable to a non-zero value this will prompt QSslSocket to use its own temporary keychain.
    bool useSSLtemporaryKeychain = qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN","1");

    qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));

#endif

#if defined(Q_OS_LINUX)
    if (!qEnvironmentVariableIsSet("DO_NOT_SET_QT_PLUGIN_PATH"))
    {
        if (QDir(QString::fromUtf8("/opt/mega/plugins")).exists())
        {
            qputenv("QT_PLUGIN_PATH","/opt/mega/plugins");
        }
    }
#endif

#if defined(Q_OS_LINUX)
    if (!qEnvironmentVariableIsSet("DO_NOT_UNSET_XDG_SESSION_TYPE"))
    {
        QString sessionType = qEnvironmentVariable("XDG_SESSION_TYPE");
        if (!sessionType.isEmpty() && sessionType == QString::fromUtf8("wayland"))
        {
            std::cerr << "Unsetting XDG session type" << std::endl;
            qunsetenv("XDG_SESSION_TYPE");
        }
    }

    // Qt5-ONLY Wayland workaround: when the app ends up on the native Wayland
    // QPA (via "-platform wayland", QT_QPA_PLATFORM=wayland, or
    // DO_NOT_UNSET_XDG_SESSION_TYPE keeping a wayland session above), two
    // top-level QQuickWindows (a parent window and a modal QML dialog) fail GL
    // context activation under Qt5's *threaded* scene-graph render loop with
    //   "QWaylandGLContext::makeCurrent: eglError: 3002" (EGL_BAD_ACCESS)
    // and the dialog never renders a frame, so it appears not to open at all.
    // EGL_BAD_ACCESS means the EGL context is already current on another
    // thread: this is a threaded-render-loop + multi-window bug, NOT a driver
    // bug — forcing software GL (LIBGL_ALWAYS_SOFTWARE/llvmpipe) does NOT help
    // because the loop is still threaded. The single-threaded "basic" render
    // loop serialises GL-context use across windows and resolves it while
    // keeping the OpenGL backend (so ShaderEffect/layers still render, unlike
    // QT_QUICK_BACKEND=software). Evaluated AFTER the unset above so it reflects
    // the platform the app will actually start on; never overrides a value the
    // user/distro already set. TODO Qt6: re-evaluate — the Qt6 Wayland backend
    // does not exhibit this multi-window makeCurrent contention.
    if (!qEnvironmentVariableIsSet("DO_NOT_SET_QSG_RENDER_LOOP") &&
        !qEnvironmentVariableIsSet("QSG_RENDER_LOOP"))
    {
        // Resolve the QPA platform the app will actually start on, following
        // Qt's own precedence: the "-platform <name>" command-line argument
        // wins over the QT_QPA_PLATFORM environment variable, which wins over
        // session auto-detection (XDG_SESSION_TYPE, possibly already cleared
        // above to steer toward XWayland). argv is parsed here because Qt has
        // not consumed it yet (QApplication is constructed further down).
        QString platform;
        for (int i = 1; i < argc; ++i)
        {
            QString arg = QString::fromLocal8Bit(argv[i]);
            // Qt strips one leading '-', so both -platform and --platform work.
            if (arg.startsWith(QString::fromUtf8("--")))
            {
                arg.remove(0, 1);
            }
            if (arg == QString::fromUtf8("-platform") && (i + 1) < argc)
            {
                // Name may carry ":options", e.g. "wayland:no-window-decorations".
                platform = QString::fromLocal8Bit(argv[i + 1]);
                break;
            }
        }
        if (platform.isEmpty())
        {
            platform = qEnvironmentVariable("QT_QPA_PLATFORM");
        }

        const bool willUseWayland =
            platform.contains(QString::fromUtf8("wayland")) ||
            (platform.isEmpty() &&
             qEnvironmentVariable("XDG_SESSION_TYPE") == QString::fromUtf8("wayland"));
        if (willUseWayland)
        {
            logMessages.emplace_back(
                MegaApi::LOG_LEVEL_INFO,
                QStringLiteral("Native Wayland detected: forcing QSG_RENDER_LOOP=basic "
                               "to avoid EGL_BAD_ACCESS on multi-window QML dialogs"));
            qputenv("QSG_RENDER_LOOP", "basic");
        }
    }
#endif

#ifndef Q_OS_MACOS
#if defined(WIN32)
    ScaleFactorManager scaleFactorManager(OsType::WIN);
#endif

#if defined(Q_OS_LINUX)
    ScaleFactorManager scaleFactorManager(OsType::LINUX);
#endif

    try
    {
        scaleFactorManager.setScaleFactorEnvironmentVariable();
    }
    catch (const std::exception& exception)
    {
        const QString errorMessage{
            QString::fromStdString("Error while setting scale factor environment variable: " +
                                   std::string(exception.what()))};
        logMessages.emplace_back(MegaApi::LOG_LEVEL_DEBUG, errorMessage);
    }
#endif

#if defined(Q_OS_LINUX)
    if (!qEnvironmentVariableIsSet("DO_NOT_UNSET_QT_QPA_PLATFORMTHEME") &&
        qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME"))
    {
        if (qunsetenv("QT_QPA_PLATFORMTHEME") !=
            0) // open folder dialog & similar crashes is fixed with this
        {
            std::cerr << "Error unsetting QT_QPA_PLATFORMTHEME vble. errno=" << errno << std::endl;
        }
    }

    if (!qEnvironmentVariableIsSet("DO_NOT_UNSET_SHLVL") && qEnvironmentVariableIsSet("SHLVL"))
    {
        if (qunsetenv("SHLVL") != 0) // reported failure in mint
        {
            // std::cerr <<  "Error unsetting SHLVL vble" << std::endl; // Fedora fails to unset
            // this env var ... too verbose error
        }
    }

    if (!qEnvironmentVariableIsSet("DO_NOT_SET_DESKTOP_SETTINGS_UNAWARE"))
    {
        QApplication::setDesktopSettingsAware(false);
    }
#endif

    MegaApplication app(argc, argv);
#if defined(Q_OS_LINUX)
    theapp = &app;
#endif

    for(const auto &message : logMessages)
    {
        MegaApi::log(message.logLevel, message.message.toUtf8().constData());
    }

#ifndef Q_OS_MACOS
    const QVector<QString> scaleFactorLogMessages = scaleFactorManager.getLogMessages();
    for (const QString& message: scaleFactorLogMessages)
    {
        MegaApi::log(MegaApi::LOG_LEVEL_DEBUG, message.toUtf8().constData());
    }
#endif

#if defined(Q_OS_LINUX)
    for (const auto& screen : app.screens())
    {
        QString msg = QString::fromUtf8("Device pixel ratio on '") + screen->name() +
                      QString::fromUtf8("': ") + QString::number(screen->devicePixelRatio());
        MegaApi::log(MegaApi::LOG_LEVEL_INFO, msg.toUtf8().constData());
    }
#endif

    qInstallMessageHandler(messageHandler);

#ifdef Q_OS_MACOS

    auto current = QOperatingSystemVersion::current();
    MegaApi::log(MegaApi::LOG_LEVEL_INFO, QString::fromUtf8("Running on macOS version: %1.%2.%3")
                 .arg(current.majorVersion()).arg(current.minorVersion()).arg(current.microVersion())
                 .toUtf8().constData());

    if (!harfbuzzEnabled)
    {
       MegaApi::log(MegaApi::LOG_LEVEL_WARNING, "Error setting QT_HARFBUZZ vble");
    }

    if (!useSSLtemporaryKeychain)
    {
        MegaApi::log(MegaApi::LOG_LEVEL_WARNING, "Error setting QT_SSL_USE_TEMPORARY_KEYCHAIN vble");
    }

    app.setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif

    QDir dataDir(app.applicationDataPath());
    QString crashPath = dataDir.filePath(QString::fromUtf8("crashDumps"));
    QString avatarPath = dataDir.filePath(QString::fromUtf8("avatars"));
    QString appLockPath = dataDir.filePath(QString::fromUtf8("megasync.lock"));
    QString appShowPath = dataDir.filePath(QString::fromUtf8("megasync.show"));
    QDir crashDir(crashPath);
    if (!crashDir.exists())
    {
        crashDir.mkpath(QString::fromUtf8("."));
    }

    QDir avatarsDir(avatarPath);
    if (!avatarsDir.exists())
    {
        avatarsDir.mkpath(QString::fromUtf8("."));
    }
#ifdef USE_BREAKPAD
#ifndef DEBUG
    CrashHandler::instance()->init(QDir::toNativeSeparators(crashPath));
#endif
#endif
    QtLockedFile singleInstanceChecker(appLockPath);
    bool alreadyStarted = true;
    for (int i = 0; i < 10; i++)
    {
        if (i > 0)
        {
            if (dataDir.exists(appShowPath))
            {
                QFile appShowFile(appShowPath);
                if (appShowFile.open(QIODevice::ReadOnly))
                {
                    if (appShowFile.size() == 0)
                    {
                        // the file has been emptied; so the infoDialog was shown in the primary MEGAsync instance.  We can exit.
                        alreadyStarted = true;
                        break;
                    }
                }
            }
        }
        singleInstanceChecker.open(QtLockedFile::ReadWrite);
        if (singleInstanceChecker.lock(QtLockedFile::WriteLock, false))
        {
            alreadyStarted = false;
            break;
        }
        else if (i == 0)
        {
             QFile appShowFile(appShowPath);
             if (appShowFile.open(QIODevice::WriteOnly))
             {
                 appShowFile.write("open");
                 appShowFile.close();
             }
        }
#ifdef __APPLE__
        else if (i == 5)
        {
            QString appVersionPath = dataDir.filePath(QString::fromUtf8("megasync.version"));
            QFile fappVersionPath(appVersionPath);
            if (!fappVersionPath.exists())
            {
                QProcess::startDetached(QString::fromUtf8("/bin/bash -c \"lsof ~/Library/Application\\ Support/Mega\\ Limited/MEGAsync/megasync.lock 2>/dev/null | grep MEGAclien | cut -d' ' -f2 | xargs kill\""), {});
            }
        }
#endif

        #ifdef WIN32
            Sleep(1000);
        #else
            sleep(1);
        #endif
    }

    // The megasync.version file update needs to be done AFTER Platform::getInstance()->processSymLinks(),
    // because it needs the old version number.
    QString appVersionPath = dataDir.filePath(QString::fromUtf8("megasync.version"));
    QFile fappVersionPath(appVersionPath);
    if (fappVersionPath.open(QIODevice::WriteOnly))
    {
        fappVersionPath.write(QString::number(Preferences::VERSION_CODE).toUtf8());
        fappVersionPath.close();
    }

    if (alreadyStarted)
    {
        MegaApi::log(MegaApi::LOG_LEVEL_WARNING, "MEGAsync is already started");
        freeStaticResources();
        return 0;
    }
    Platform::getInstance()->initialize(argc, argv);

    app.setWindowIcon(QIcon(QString::fromUtf8(":/images/app_ico.ico")));
    app.initialize();
    app.start();

    int toret = app.exec();

    freeStaticResources();
#ifdef WIN32
    extern bool WindowsPlatform_exiting;
    WindowsPlatform_exiting = true;
#endif

#ifdef Q_OS_LINUX
    theapp = nullptr;
#endif
    return toret;

#if 0 //Strings for the translation system. These lines don't need to be built
    QT_TRANSLATE_NOOP("QDialogButtonBox", "&Yes");
    QT_TRANSLATE_NOOP("QDialogButtonBox", "&No");
    QT_TRANSLATE_NOOP("QDialogButtonBox", "&OK");
    QT_TRANSLATE_NOOP("QDialogButtonBox", "&Cancel");
    QT_TRANSLATE_NOOP("QPlatformTheme", "&Yes");
    QT_TRANSLATE_NOOP("QPlatformTheme", "&No");
    QT_TRANSLATE_NOOP("QPlatformTheme", "OK");
    QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel");

    QT_TRANSLATE_NOOP("QFileDialog", "Look in:");
    QT_TRANSLATE_NOOP("QFileDialog", "Back");
    QT_TRANSLATE_NOOP("QFileDialog", "Go back");
    QT_TRANSLATE_NOOP("QFileDialog", "Alt+Left");
    QT_TRANSLATE_NOOP("QFileDialog", "Forward");
    QT_TRANSLATE_NOOP("QFileDialog", "Go forward");
    QT_TRANSLATE_NOOP("QFileDialog", "Alt+Right");
    QT_TRANSLATE_NOOP("QFileDialog", "Parent Directory");
    QT_TRANSLATE_NOOP("QFileDialog", "Go to the parent directory");
    QT_TRANSLATE_NOOP("QFileDialog", "Alt+Up");
    QT_TRANSLATE_NOOP("QFileDialog", "Create New Folder");
    QT_TRANSLATE_NOOP("QFileDialog", "Create a New Folder");
    QT_TRANSLATE_NOOP("QFileDialog", "List View");
    QT_TRANSLATE_NOOP("QFileDialog", "Change to list view mode");
    QT_TRANSLATE_NOOP("QFileDialog", "Detail View");
    QT_TRANSLATE_NOOP("QFileDialog", "Change to detail view mode");
    QT_TRANSLATE_NOOP("QFileDialog", "Sidebar");
    QT_TRANSLATE_NOOP("QFileDialog", "List of places and bookmarks");
    QT_TRANSLATE_NOOP("QFileDialog", "Files");
    QT_TRANSLATE_NOOP("QFileDialog", "Files of type:");
    QT_TRANSLATE_NOOP("QFileDialog", "Find Directory");
    QT_TRANSLATE_NOOP("QFileDialog", "Open");
    QT_TRANSLATE_NOOP("QFileDialog", "Save As");
    QT_TRANSLATE_NOOP("QFileDialog", "Directory:");
    QT_TRANSLATE_NOOP("QFileDialog", "File &name:");
    QT_TRANSLATE_NOOP("QFileDialog", "&Open");
    QT_TRANSLATE_NOOP("QFileDialog", "&Choose");
    QT_TRANSLATE_NOOP("QFileDialog", "&Save");
    QT_TRANSLATE_NOOP("QFileDialog", "All Files (*)");
    QT_TRANSLATE_NOOP("QFileDialog", "Show ");
    QT_TRANSLATE_NOOP("QFileDialog", "&Rename");
    QT_TRANSLATE_NOOP("QFileDialog", "&Delete");
    QT_TRANSLATE_NOOP("QFileDialog", "Show &hidden files");
    QT_TRANSLATE_NOOP("QFileDialog", "&New Folder");
    QT_TRANSLATE_NOOP("QFileDialog", "All files (*)");
    QT_TRANSLATE_NOOP("QFileDialog", "Directories");
    QT_TRANSLATE_NOOP("QFileDialog", "%1\nDirectory not found.\nPlease verify the correct directory name was given.");
    QT_TRANSLATE_NOOP("QFileDialog", "%1 already exists.\nDo you want to replace it?");
    QT_TRANSLATE_NOOP("QFileDialog", "%1\nFile not found.\nPlease verify the correct file name was given.");
    QT_TRANSLATE_NOOP("QFileDialog", "New Folder");
    QT_TRANSLATE_NOOP("QFileDialog", "Delete");
    QT_TRANSLATE_NOOP("QFileDialog", "'%1' is write protected.\nDo you want to delete it anyway?");
    QT_TRANSLATE_NOOP("QFileDialog", "Are you sure you want to delete '%1'?");
    QT_TRANSLATE_NOOP("QFileDialog", "Could not delete directory.");
    QT_TRANSLATE_NOOP("QFileDialog", "Recent Places");
    QT_TRANSLATE_NOOP("QFileDialog", "Remove");
    QT_TRANSLATE_NOOP("QFileDialog", "My Computer");
    QT_TRANSLATE_NOOP("QFileDialog", "Drive");
    QT_TRANSLATE_NOOP("QFileDialog", "%1 File");
    QT_TRANSLATE_NOOP("QFileDialog", "File");
    QT_TRANSLATE_NOOP("QFileDialog", "File Folder");
    QT_TRANSLATE_NOOP("QFileDialog", "Folder");
    QT_TRANSLATE_NOOP("QFileDialog", "Alias");
    QT_TRANSLATE_NOOP("QFileDialog", "Shortcut");
    QT_TRANSLATE_NOOP("QFileDialog", "Unknown");

    QT_TRANSLATE_NOOP("QFileSystemModel", "%1 TB");
    QT_TRANSLATE_NOOP("QFileSystemModel", "%1 GB");
    QT_TRANSLATE_NOOP("QFileSystemModel", "%1 MB");
    QT_TRANSLATE_NOOP("QFileSystemModel", "%1 KB");
    QT_TRANSLATE_NOOP("QFileSystemModel", "%1 bytes");
    QT_TRANSLATE_NOOP("QFileSystemModel", "Invalid filename");
    QT_TRANSLATE_NOOP("QFileSystemModel", "<b>The name \"%1\" cannot be used.</b><p>Try using another name, with fewer characters or no punctuation marks.");
    QT_TRANSLATE_NOOP("QFileSystemModel", "Name");
    QT_TRANSLATE_NOOP("QFileSystemModel", "Size");
    QT_TRANSLATE_NOOP("QFileSystemModel", "Kind");
    QT_TRANSLATE_NOOP("QFileSystemModel", "Type");
    QT_TRANSLATE_NOOP("QFileSystemModel", "Date Modified");
    QT_TRANSLATE_NOOP("QFileSystemModel", "My Computer");
    QT_TRANSLATE_NOOP("QFileSystemModel", "Computer");

    QT_TRANSLATE_NOOP("Installer", "Choose Users");
    QT_TRANSLATE_NOOP("Installer", "Choose for which users you want to install $(^NameDA).");
    QT_TRANSLATE_NOOP("Installer", "Select whether you want to install $(^NameDA) for yourself only or for all users of this computer. $(^ClickNext)");
    QT_TRANSLATE_NOOP("Installer", "Install for anyone using this computer");
    QT_TRANSLATE_NOOP("Installer", "Install just for me");

    QT_TRANSLATE_NOOP("MegaError", "No error");
    QT_TRANSLATE_NOOP("MegaError", "Internal error");
    QT_TRANSLATE_NOOP("MegaError", "Invalid argument");
    QT_TRANSLATE_NOOP("MegaError", "Request failed, retrying");
    QT_TRANSLATE_NOOP("MegaError", "Rate limit exceeded");
    QT_TRANSLATE_NOOP("MegaError", "Failed permanently");
    QT_TRANSLATE_NOOP("MegaError", "Too many concurrent connections or transfers");
    QT_TRANSLATE_NOOP("MegaError", "Terms of Service breached");
    QT_TRANSLATE_NOOP("MegaError", "Out of range");
    QT_TRANSLATE_NOOP("MegaError", "Expired");
    QT_TRANSLATE_NOOP("MegaError", "Not found");
    QT_TRANSLATE_NOOP("MegaError", "Circular linkage detected");
    QT_TRANSLATE_NOOP("MegaError", "Upload produces recursivity");
    QT_TRANSLATE_NOOP("MegaError", "Access denied");
    QT_TRANSLATE_NOOP("MegaError", "Already exists");
    QT_TRANSLATE_NOOP("MegaError", "Incomplete");
    QT_TRANSLATE_NOOP("MegaError", "Invalid key/Decryption error");
    QT_TRANSLATE_NOOP("MegaError", "Bad session ID");
    QT_TRANSLATE_NOOP("MegaError", "File removed as it violated our Terms of Service");
    QT_TRANSLATE_NOOP("MegaError", "Blocked");
    QT_TRANSLATE_NOOP("MegaError", "Over quota");
    QT_TRANSLATE_NOOP("MegaError", "Temporarily not available");
    QT_TRANSLATE_NOOP("MegaError", "Connection overflow");
    QT_TRANSLATE_NOOP("MegaError", "Write error");
    QT_TRANSLATE_NOOP("MegaError", "Read error");
    QT_TRANSLATE_NOOP("MegaError", "Invalid application key");
    QT_TRANSLATE_NOOP("MegaError", "SSL verification failed");
    QT_TRANSLATE_NOOP("MegaError", "Not enough quota");
    QT_TRANSLATE_NOOP("MegaError", "Unknown error");
    QT_TRANSLATE_NOOP("MegaError", "Your account has been suspended due to copyright violations. Please check your email inbox.");
    QT_TRANSLATE_NOOP("MegaError", "Your account was terminated due to a breach of MEGA's Terms of Service, such as abuse of rights of others; sharing and/or importing illegal data; or system abuse.");
    QT_TRANSLATE_NOOP("MegaError", "Storage Quota Exceeded. Upgrade now");
    QT_TRANSLATE_NOOP("FinderExtensionApp", "Get MEGA link");
    QT_TRANSLATE_NOOP("FinderExtensionApp", "View on MEGA");
    QT_TRANSLATE_NOOP("FinderExtensionApp", "No options available");
    QT_TRANSLATE_NOOP("FinderExtensionApp", "Click the toolbar item for a menu.");
    QT_TRANSLATE_NOOP("FinderExtensionApp", "%n file", "", n);
    QT_TRANSLATE_NOOP("FinderExtensionApp", "%n folder", "", n);
    QT_TRANSLATE_NOOP("FinderExtensionApp", "View previous versions");
    QT_TRANSLATE_NOOP("MegaSyncError", "No error");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unknown error");
    QT_TRANSLATE_NOOP("MegaSyncError", "File system not supported");
    QT_TRANSLATE_NOOP("MegaSyncError", "Remote node is not valid");
    QT_TRANSLATE_NOOP("MegaSyncError", "Local path is not valid");
    QT_TRANSLATE_NOOP("MegaSyncError", "Initial scan failed");
    QT_TRANSLATE_NOOP("MegaSyncError", "Local path temporarily unavailable");
    QT_TRANSLATE_NOOP("MegaSyncError", "Local path not available");
    QT_TRANSLATE_NOOP("MegaSyncError", "Remote node not found");
    QT_TRANSLATE_NOOP("MegaSyncError", "Reached storage quota limit");
    QT_TRANSLATE_NOOP("MegaSyncError", "Your plan has expired");
    QT_TRANSLATE_NOOP("MegaSyncError", "Foreign target storage quota reached");
    QT_TRANSLATE_NOOP("MegaSyncError", "Remote path has changed");
    QT_TRANSLATE_NOOP("MegaSyncError", "Remote node moved to Rubbish Bin");
    QT_TRANSLATE_NOOP("MegaSyncError", "Share without full access");
    QT_TRANSLATE_NOOP("MegaSyncError", "Put nodes error");
    QT_TRANSLATE_NOOP("MegaSyncError", "Active sync below path");
    QT_TRANSLATE_NOOP("MegaSyncError", "Active sync above path");
    QT_TRANSLATE_NOOP("MegaSyncError", "Remote node has been deleted");
    QT_TRANSLATE_NOOP("MegaSyncError", "Remote node is inside Rubbish Bin");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unsupported VBoxSharedFolderFS filesystem");
    QT_TRANSLATE_NOOP("MegaSyncError", "Local path collides with an existing sync");
    QT_TRANSLATE_NOOP("MegaSyncError", "Local filesystem is FAT");
    QT_TRANSLATE_NOOP("MegaSyncError", "Local filesystem is HGFS");
    QT_TRANSLATE_NOOP("MegaSyncError", "Your account is blocked");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unknown temporary error");
    QT_TRANSLATE_NOOP("MegaSyncError", "Too many changes in account, local state invalid");
    QT_TRANSLATE_NOOP("MegaSyncError", "Session closed");
    QT_TRANSLATE_NOOP("MegaSyncError", "Undefined error");
    QT_TRANSLATE_NOOP("MegaSyncError", "Active sync same path");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unknown drive path.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Local filesystem mismatch");
    QT_TRANSLATE_NOOP("MegaSyncError", "Backup externally modified");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unable to create initial ignore file.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unable to read sync configs from disk.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Invalid scan interval specified.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Filesystem notification subsystem unavailable.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unable to add filesystem watch.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unable to retrieve sync root FSID.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unable to open state cache database.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Insufficient disk space.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Failure accessing to persistent storage");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unable to retrieve the ID of current device");
    QT_TRANSLATE_NOOP("MegaSyncError", "Mismatch on sync root FSID.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Syncing of exFAT, FAT32, FUSE and LIFS file systems is not supported by MEGA on macOS.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Could not get the filesystem's ID.");
    QT_TRANSLATE_NOOP("MegaSyncError", "Unable to write sync config to disk.");
    // This string is not used yet, restore it when applicable.
    // QT_TRANSLATE_NOOP("MegaSyncError", "Backup source path not below drive path.");
#endif
}
