#include "zlibreference.h"

#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QProcessEnvironment>
#include <QStringList>

#include <utility>

#if defined(Q_OS_LINUX)
#include <dlfcn.h>
#endif

namespace {

QString translate(const char *text)
{
    return QCoreApplication::translate("ZlibReference", text);
}

} // namespace

bool ZlibReference::systemIsZlibNg()
{
#if defined(Q_OS_LINUX)
    // Спрашиваем ровно ту библиотеку, которую подберёт ld.so процессу java.
    // zlib-ng в режиме совместимости честно пишет о себе в zlibVersion().
    static const bool result = [] {
        void *handle = dlopen("libz.so.1", RTLD_LAZY);
        if (!handle)
            return false;
        using VersionFn = const char *(*)();
        const auto zlibVersion = reinterpret_cast<VersionFn>(dlsym(handle, "zlibVersion"));
        const QString version = zlibVersion ? QString::fromLatin1(zlibVersion()) : QString();
        dlclose(handle);
        return version.contains(QLatin1String("zlib-ng"), Qt::CaseInsensitive);
    }();
    return result;
#else
    return false;
#endif
}

QString ZlibReference::bundledPath()
{
#if defined(Q_OS_LINUX)
    static const QString path = [] {
        QStringList candidates;
        const QString appDir = QCoreApplication::applicationDirPath();
        if (!appDir.isEmpty()) {
            candidates << appDir + QStringLiteral("/zlib/libz.so.1")           // дерево сборки
                       << appDir + QStringLiteral("/../lib/galeonLauncher/libz.so.1");
        }
#ifdef LAUNCHER_ZLIB_INSTALL_DIR
        candidates << QStringLiteral(LAUNCHER_ZLIB_INSTALL_DIR) + QStringLiteral("/libz.so.1");
#endif
        for (const QString &candidate : std::as_const(candidates)) {
            const QFileInfo info(candidate);
            if (info.exists() && info.isFile())
                return info.canonicalFilePath();
        }
        return QString();
    }();
    return path;
#else
    return {};
#endif
}

bool ZlibReference::applyTo(QProcessEnvironment &env, QString *note)
{
#if defined(Q_OS_LINUX)
    if (!systemIsZlibNg()) {
        if (note)
            note->clear();
        return true;
    }

    const QString path = bundledPath();
    if (path.isEmpty()) {
        if (note)
            *note = translate("В системе zlib-ng, а эталонный libz.so.1 рядом с лаунчером "
                              "не найден — установщик почти наверняка не сойдётся по sha1.");
        return false;
    }

    // Не затираем чужой LD_PRELOAD: наш идёт первым, остальное остаётся.
    const QString existing = env.value(QStringLiteral("LD_PRELOAD"));
    env.insert(QStringLiteral("LD_PRELOAD"), existing.isEmpty() ? path : path + u':' + existing);
    if (note)
        *note = translate("Системный zlib-ng подменён эталонным zlib: %1")
                    .arg(QDir::toNativeSeparators(path));
    return true;
#else
    Q_UNUSED(env);
    if (note)
        note->clear();
    return true;
#endif
}
