/*GRB*
Gerbera - https://gerbera.io/

    grb_compile_info.cc - this file is part of Gerbera.

    Copyright (C) 2024-2026 Gerbera Contributors

    Gerbera is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2
    as published by the Free Software Foundation.

    Gerbera is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Gerbera.  If not, see <http://www.gnu.org/licenses/>.
*/

/// @file config/grb_compile_info.cc
#define GRB_LOG_FAC GrbLogFacility::server

#include "grb_runtime.h" // API

#include "upnp/compat.h"
#include "util/tools.h"

#include <cxxopts.hpp>
#include <fmt/format.h>
#if FMT_VERSION >= 100202
#include <fmt/ranges.h>
#endif
#include <json/version.h>
#include <pugixml.hpp>
#include <spdlog/version.h>
#include <sqlite3.h>

#ifdef HAVE_ZIP
#include <zip.h>
#endif

#ifdef HAVE_JS
#include <duktape.h>
#endif

#ifdef HAVE_CURL
#include <curl/curlver.h>
#endif

#ifdef HAVE_MAGIC
#include <magic.h>
#endif

#ifdef HAVE_ICU
#include <unicode/uvernum.h>
#endif

#ifdef HAVE_MYSQL
#include <mysql.h>
#endif

#ifdef HAVE_PGSQL
extern "C" {
#include <libpq-fe.h>
}
#include <pqxx/version>
#endif

#ifdef HAVE_FFMPEG
extern "C" {

#include <libavformat/avformat.h>
#include <libavutil/avutil.h>

} // extern "C"
#endif

#ifdef HAVE_TAGLIB
#include <taglib.h>
#endif

#ifdef HAVE_EXIV2
#include <exiv2/exv_conf.h>
#endif

#ifdef HAVE_WAVPACK
#include <wavpack/wavpack.h>
#endif

#ifdef HAVE_MATROSKA
#include <ebml/EbmlVersion.h>
#include <matroska/KaxVersion.h>
#endif

static constexpr auto gitBranch = std::string_view(GIT_BRANCH);
static constexpr auto gitCommitHash = std::string_view(GIT_COMMIT_HASH);

bool GerberaRuntime::printCompileInfo(const std::string& arg)
{
    printCopyright(arg);

    // Info from cmake
    fmt::print(
        "Compile info:\n"
        "-------------\n");
    {
        std::vector<std::pair<std::string, std::string>> compileOptions;
        for (auto&& opt : splitString(COMPILE_INFO, '\n')) {
            auto line = splitString(opt, '=');
            if (line.size() == 1) {
                compileOptions.emplace_back(line.front(), "");
            } else if (line.size() == 2) {
                compileOptions.emplace_back(line.front(), line.back());
            }
        }
        std::size_t len = 0;
        for (auto&& opt : compileOptions) {
            len = std::max(opt.first.size(), len);
        }
        for (auto&& [lib, val] : compileOptions) {
            if (val.empty())
                fmt::print("{}\n", val);
            else
                fmt::print("{1:{0}}  {2}\n", len, lib, val);
        }
    }

    // Active defines
    {
        std::vector<std::pair<std::string_view, std::string>> buildOptions {
            { "FMT    ", fmt::to_string(FMT_VERSION) },
            { "SPDLOG ", fmt::to_string(SPDLOG_VERSION) },
#ifdef SPDLOG_FMT_EXTERNAL
            { "", "SPDLOG_FMT_EXTERNAL" },
#endif
            { "CXXOPTS", fmt::format("{}.{}.{}", cxxopts::version.major, cxxopts::version.minor, cxxopts::version.patch) },
            { "SQLITE ", fmt::to_string(SQLITE_VERSION) },
            { "PUGIXML", fmt::to_string(PUGIXML_VERSION) },
            { "JSONCPP", JSONCPP_VERSION_STRING },
#ifdef HAVE_ZIP
            { "LIBZIPPP", LIBZIPPP_VERSION },
            { "LIBZIP", zip_libzip_version() },
#endif
#ifdef PACKAGE_DATADIR
            { "PACKAGE_DATADIR", PACKAGE_DATADIR },
#endif
#ifdef USING_NPUPNP
            { "NPUPNP ", fmt::to_string(UPNP_VERSION) },
#else
            { "PUPNP  ", fmt::to_string(UPNP_VERSION) },
#endif
#ifdef UPNP_MINISERVER_REUSEADDR
            { "", "UPNP_REUSEADDR" },
#endif
#ifdef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
            { "", "UPNP_BLOCKING_TCP_CONNECTIONS" },
#endif
#ifdef UPNP_HAVE_TOOLS
            { "", "UPNP_HAVE_TOOLS" },
#endif
            { "", fmt::format("GRB_READ_ERROR={}", GRB_READ_ERROR) },
#ifdef HAVE_NL_LANGINFO
            { "HAVE_NL_LANGINFO", "" },
#endif
#ifdef HAVE_SETLOCALE
            { "HAVE_SETLOCALE", "" },
#endif
#ifdef HAVE_EXECINFO
            { "HAVE_EXECINFO", "" },
#endif
#ifdef HAVE_INOTIFY
            { "HAVE_INOTIFY", "" },
#endif
#ifdef HAVE_SYSTEMD
            { "HAVE_SYSTEMD", LIBSYSTEMD_VERSION },
#endif
#ifdef HAVE_ICU
            { "HAVE_ICU", U_ICU_VERSION },
#endif
#ifdef HAVE_JS
            { "HAVE_JS", fmt::to_string(DUK_VERSION) },
#endif
#ifdef HAVE_MYSQL
            { "HAVE_MYSQL", fmt::format("Server {}, Client {}", MYSQL_SERVER_VERSION, mysql_get_client_info()) },
#endif
#ifdef HAVE_PGSQL
            { "HAVE_PGSQL", fmt::format("Pqxx {}, PqLib {}", PQXX_VERSION, PQlibVersion()) },
#endif
#ifdef HAVE_CURL
            { "HAVE_CURL", LIBCURL_VERSION },
#endif
#ifdef HAVE_TAGLIB
            { "HAVE_TAGLIB", fmt::format("{}.{}.{}", TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION, TAGLIB_PATCH_VERSION) },
#endif
#ifdef HAVE_WAVPACK
            { "HAVE_WAVPACK", WavpackGetLibraryVersionString() },
#endif
#ifdef HAVE_MAGIC
            { "HAVE_MAGIC", fmt::to_string(MAGIC_VERSION) },
#endif
#ifdef HAVE_FFMPEG
            { "HAVE_FFMPEG", LIBAVUTIL_IDENT ", " LIBAVFORMAT_IDENT },
#endif
#ifdef HAVE_AVSTREAM_CODECPAR
            { "", "HAVE_AVSTREAM_CODECPAR" },
#endif
#ifdef HAVE_FFMPEGTHUMBNAILER
            { "HAVE_FFMPEGTHUMBNAILER", FFMPEGTHUMBNAILER_VERSION },
#endif
#ifdef HAVE_LIBEXIF
            { "HAVE_LIBEXIF", EXIF_VERSION },
#endif
#ifdef HAVE_EXIV2
            { "HAVE_EXIV2", EXV_PACKAGE_VERSION },
#endif
#ifdef HAVE_MATROSKA
            { "HAVE_MATROSKA", fmt::format("Matroska {:x}, Ebml {:x}", LIBMATROSKA_VERSION, LIBEBML_VERSION) },
#endif
#ifdef ONLINE_SERVICES
            { "ONLINE_SERVICES", "" },
#endif
#ifdef HAVE_LASTFM
            { "HAVE_LASTFM",
#ifdef HAVE_LASTFMLIB
                "HAVE_LASTFMLIB"
#else
                ""
#endif
            },
#endif
#ifdef ICONV_CONST
            { "ICONV_CONST", "" },
#endif
#ifdef GRBDEBUG
            { "GRBDEBUG", "" },
#endif
#ifdef TOMBDEBUG
            { "TOMBDEBUG", "" },
#endif
#ifdef SOLARIS
            { "SOLARIS", "" },
#endif
#ifdef BSD
            { "BSD", "" },
#endif
#ifdef HAVE_ATOMIC
            { "HAVE_ATOMIC", "" },
#endif
        };
        // Print active defines
        fmt::print(
            "\n\nBuild info:\n"
            "-----------\n");
        std::size_t len = 0;
        for (auto&& opt : buildOptions) {
            len = std::max(opt.first.size(), len);
        }
        for (auto&& [lib, vers] : buildOptions) {
            if (vers.empty())
                fmt::print("{}\n", lib);
            else
                fmt::print("{1:{0}}  {2}\n", len, lib, vers);
        }
        fmt::print("\n\n");
    }

    // Print git branch
    if (!gitBranch.empty()) {
        fmt::print(
            "Git info:\n"
            "---------\n"
            "Git Branch: {}\n",
            gitBranch);
    }

    // Print git commit hash
    if (!gitCommitHash.empty()) {
        fmt::print("Git Commit: {}\n", gitCommitHash);
    }

    return true;
}
