#!/bin/bash
#
# setup-linux.sh -- install the rsync build toolchain and AGI.
#
# Tested on Ubuntu 22.04.5 LTS and 26.04 LTS, and on CachyOS.  Covers both
# rsync build systems: the autoconf one (./configure && make) and the CMake
# one (CMakeLists.txt), plus the optional feature libraries and the manpage
# generator.  Also installs AGI (Android GPU Inspector) from the latest
# google/agi release: the .deb on Debian/Ubuntu, the .zip on Arch/CachyOS.
#
# Usage:
#     ./setup-linux.sh              # rsync required + optional, then AGI
#     ./setup-linux.sh --minimal    # rsync required only, no AGI
#     ./setup-linux.sh --no-agi     # rsync deps only
#     ./setup-linux.sh --agi-only   # AGI and its dependencies only
#     ./setup-linux.sh --dry-run    # show what would be installed
#
# AGI_VERSION=3.3.3 ./setup-linux.sh pins a release instead of asking GitHub
# for the latest one.
#
# Safe to re-run: the package managers skip anything already present, and AGI
# is re-downloaded only when the installed version is not the wanted one.

set -uo pipefail

MINIMAL=0
DRY_RUN=0
WANT_AGI=1
WANT_RSYNC=1

for arg in "$@"; do
    case "$arg" in
    --minimal)  MINIMAL=1; WANT_AGI=0 ;;
    --dry-run)  DRY_RUN=1 ;;
    --no-agi)   WANT_AGI=0 ;;
    --agi-only) WANT_RSYNC=0; WANT_AGI=1 ;;
    -h|--help)
        sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//'
        exit 0 ;;
    *)
        echo "setup-linux.sh: unknown option '$arg' (try --help)" >&2
        exit 2 ;;
    esac
done

# ---------------------------------------------------------------- helpers

red()   { printf '\033[31m%s\033[0m\n' "$*"; }
green() { printf '\033[32m%s\033[0m\n' "$*"; }
bold()  { printf '\033[1m%s\033[0m\n' "$*"; }

die() { red "ERROR: $*"; exit 1; }

# ------------------------------------------------- package manager wrapper

# Two families are supported: apt (Debian/Ubuntu) and pacman (Arch/CachyOS).
PM=""
if command -v apt-get >/dev/null 2>&1; then
    PM=apt
elif command -v pacman >/dev/null 2>&1; then
    PM=pacman
else
    die "found neither apt-get nor pacman -- unsupported distribution."
fi

pkg_available() {
    case "$PM" in
    apt)
        local cand
        cand=$(apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/{print $2}')
        [ -n "$cand" ] && [ "$cand" != "(none)" ] ;;
    pacman)
        pacman -Si "$1" >/dev/null 2>&1 ;;
    esac
}

# Echo the first name the distro actually ships.  Package names drift between
# releases (libgtk-3-0 became libgtk-3-0t64 in Ubuntu 24.04, and the JDK
# version in the archive moves every six months), so nothing is hard-coded.
pick_pkg() {
    local p
    for p in "$@"; do
        if pkg_available "$p"; then
            echo "$p"
            return 0
        fi
    done
    return 1
}

pkg_install() {
    [ $# -eq 0 ] && return 0
    case "$PM" in
    apt)    apt-get install -y "$@" ;;
    pacman) pacman -S --needed --noconfirm "$@" ;;
    esac
}

# ------------------------------------------------------- sanity checks

DISTRO="unknown"
RELEASE="unknown"
if [ -r /etc/os-release ]; then
    # shellcheck disable=SC1091  # runtime file, not available to the linter
    . /etc/os-release
    DISTRO="${ID:-unknown}"
    RELEASE="${VERSION_ID:-unknown}"
fi

bold "rsync build dependencies and AGI"
echo "  distro ......... ${PRETTY_NAME:-$DISTRO $RELEASE}"
echo "  architecture ... $(uname -m)"
echo "  packages ....... $PM"

case "$DISTRO" in
ubuntu|debian|linuxmint|pop|arch|cachyos|endeavouros|manjaro) ;;
*)  echo
    red "Warning: '$DISTRO' is untested; assuming it behaves like a $PM distro."
    echo "Continuing anyway -- package names may differ."
    ;;
esac

# AGI ships x86-64 binaries only.
case "$(uname -m)" in
x86_64|amd64) ;;
*)  if [ "$WANT_AGI" -eq 1 ]; then
        red "Warning: AGI publishes x86-64 Linux builds only; skipping it."
        WANT_AGI=0
    fi
    ;;
esac

# The package managers need root.  Re-exec under sudo rather than sprinkling
# it around, so that a single password prompt covers the whole run.
if [ "$(id -u)" -ne 0 ] && [ "$DRY_RUN" -eq 0 ]; then
    command -v sudo >/dev/null 2>&1 \
        || die "not running as root and sudo is not installed."
    echo
    echo "Re-running under sudo..."
    # Plain sudo, not "sudo -E": some sudoers configs refuse to preserve the
    # environment, and the exports below happen in the root instance anyway.
    exec sudo AGI_VERSION="${AGI_VERSION:-}" "$0" "$@"
fi

export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a          # don't prompt about restarting services

# ------------------------------------------------------- rsync packages

REQUIRED=()
OPTIONAL=()

if [ "$WANT_RSYNC" -eq 1 ]; then
    case "$PM" in
    apt)
        # Needed to build rsync at all.
        REQUIRED=(
            build-essential     # gcc, g++, make, libc headers
            gawk                # rsync needs a modern awk for its code generators
            autoconf            # ./configure is generated from configure.ac
            automake
            python3             # manpage + header generators, and the test suite
            cmake               # the CMake build
            ninja-build         # ...and its default generator
            git                 # version stamping (git-version.h)
            pkg-config
        )

        # Optional: each one switches on an rsync feature.  Missing ones only
        # mean a smaller feature set, never a failed build.
        OPTIONAL=(
            acl libacl1-dev         # --acls  (helper tools also used by the tests)
            attr libattr1-dev       # --xattrs (likewise)
            libxxhash-dev           # xxhash checksums (becomes the default)
            libzstd-dev             # zstd compression (becomes the default)
            liblz4-dev              # lz4 compression
            libssl-dev              # OpenSSL MD4/MD5
            zlib1g-dev              # for cmake -DRSYNC_EXTERNAL_ZLIB=ON
            libpopt-dev             # for ./configure --with-included-popt=no
        )
        ;;
    pacman)
        # Arch splits nothing out into -dev packages: the headers ship with
        # the library, and base-devel covers the compiler toolchain.
        REQUIRED=(
            base-devel          # gcc, make, and friends
            gawk
            autoconf
            automake
            python
            cmake
            ninja
            git
            pkgconf
        )
        OPTIONAL=(
            acl                     # --acls
            attr                    # --xattrs
            xxhash                  # xxhash checksums (becomes the default)
            zstd                    # zstd compression (becomes the default)
            lz4                     # lz4 compression
            openssl                 # OpenSSL MD4/MD5
            zlib                    # for cmake -DRSYNC_EXTERNAL_ZLIB=ON
            popt                    # for ./configure --with-included-popt=no
        )
        ;;
    esac

    # Development tooling: not needed to build rsync, but useful when working
    # on its shell scripts -- including this one, which shellcheck keeps honest.
    OPTIONAL+=(shellcheck)

    # The manpages need one of two python3 markdown libraries; upstream prefers
    # cmarkgfm.  Pick whichever this release actually offers.
    case "$PM" in
    apt)    MARKDOWN_PKG=$(pick_pkg python3-cmarkgfm python3-commonmark) ;;
    pacman) MARKDOWN_PKG=$(pick_pkg python-cmarkgfm python-commonmark) ;;
    esac
    if [ -n "$MARKDOWN_PKG" ]; then
        OPTIONAL+=("$MARKDOWN_PKG")
    fi

    if [ "$MINIMAL" -eq 1 ]; then
        OPTIONAL=()
    fi
fi

# ------------------------------------------------------- AGI packages

# AGI is a Java/SWT desktop app plus a set of Go binaries.  The .deb declares
# "Depends: openjdk-22-jre, libgtk-3-0, libwebkit2gtk-4.1-0", but Ubuntu has
# never packaged openjdk-22 -- so pick a JRE that exists instead.  The GUI's
# class files are Java 19 bytecode, so anything from 21 up runs it; the launcher
# itself only insists on 11.  adb is not a hard dependency, but without it AGI
# cannot see Android devices ("adb could not be found from ANDROID_HOME or PATH").
AGI_DEPS=()
AGI_JRE=""
AGI_GTK=""
AGI_WEBKIT=""
AGI_ADB=""

if [ "$WANT_AGI" -eq 1 ]; then
    case "$PM" in
    apt)
        AGI_JRE=$(pick_pkg openjdk-21-jre openjdk-25-jre openjdk-24-jre \
                           openjdk-23-jre openjdk-22-jre openjdk-26-jre)
        AGI_GTK=$(pick_pkg libgtk-3-0t64 libgtk-3-0)
        AGI_WEBKIT=$(pick_pkg libwebkit2gtk-4.1-0)
        AGI_ADB=$(pick_pkg adb android-sdk-platform-tools)
        # curl fetches the release, binutils' ar rewrites the .deb's control
        # member, ca-certificates lets curl trust github.com.
        AGI_DEPS=(curl ca-certificates binutils xz-utils unzip)
        ;;
    pacman)
        AGI_JRE=$(pick_pkg jre-openjdk jre21-openjdk)
        AGI_GTK=$(pick_pkg gtk3)
        AGI_WEBKIT=$(pick_pkg webkit2gtk-4.1)
        AGI_ADB=$(pick_pkg android-tools)
        AGI_DEPS=(curl unzip)
        ;;
    esac

    for p in "$AGI_GTK" "$AGI_WEBKIT" "$AGI_ADB"; do
        [ -n "$p" ] && AGI_DEPS+=("$p")
    done

    # A JRE already on the box is good enough if it is new enough to load the
    # GUI's Java 19 class files.  On apt we install one anyway: the .deb's
    # rewritten Depends line has to name a package dpkg knows about.
    JAVA_MAJOR=0
    if command -v java >/dev/null 2>&1; then
        JAVA_MAJOR=$(java -version 2>&1 \
            | sed -n '1s/.*version "\([0-9]\{1,\}\).*/\1/p')
        JAVA_MAJOR=${JAVA_MAJOR:-0}
    fi
    if [ -n "$AGI_JRE" ] && { [ "$PM" = apt ] || [ "$JAVA_MAJOR" -lt 19 ]; }; then
        AGI_DEPS+=("$AGI_JRE")
    fi
fi

# ------------------------------------------------------- AGI release info

# Used only when GitHub cannot be reached; the release that this script was
# last checked against.
AGI_FALLBACK_VERSION=3.3.3
AGI_WANTED=""
AGI_INSTALLED=""

agi_installed_version() {
    [ -r /opt/agi/build.properties ] || return 1
    awk -F= '
        /^Version\.Major/ { maj = $2 }
        /^Version\.Minor/ { min = $2 }
        /^Version\.Micro/ { mic = $2 }
        END { if (maj == "") exit 1; print maj "." min "." mic }
    ' /opt/agi/build.properties | tr -d ' \r'
}

agi_latest_version() {
    command -v curl >/dev/null 2>&1 || return 1
    curl -fsSL --retry 3 --max-time 20 \
        https://api.github.com/repos/google/agi/releases/latest 2>/dev/null \
        | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v\{0,1\}\([^"]*\)".*/\1/p' \
        | head -1
}

if [ "$WANT_AGI" -eq 1 ]; then
    AGI_WANTED="${AGI_VERSION:-}"
    if [ -z "$AGI_WANTED" ]; then
        AGI_WANTED=$(agi_latest_version)
    fi
    if [ -z "$AGI_WANTED" ]; then
        red "Could not reach the GitHub releases API; falling back to AGI $AGI_FALLBACK_VERSION."
        AGI_WANTED="$AGI_FALLBACK_VERSION"
    fi
    AGI_INSTALLED=$(agi_installed_version) || AGI_INSTALLED=""
fi

AGI_BASE_URL="https://github.com/google/agi/releases/download"

# ------------------------------------------------------------- dry run

if [ "$DRY_RUN" -eq 1 ]; then
    if [ "$WANT_RSYNC" -eq 1 ]; then
        echo
        bold "Would install (rsync, required):"
        printf '  %s\n' "${REQUIRED[@]}"
        if [ ${#OPTIONAL[@]} -gt 0 ]; then
            bold "Would install (rsync, optional):"
            printf '  %s\n' "${OPTIONAL[@]}"
        fi
    fi
    if [ "$WANT_AGI" -eq 1 ]; then
        echo
        bold "Would install (AGI dependencies):"
        printf '  %s\n' "${AGI_DEPS[@]}"
        bold "Would install AGI $AGI_WANTED:"
        if [ "$PM" = apt ]; then
            echo "  $AGI_BASE_URL/v$AGI_WANTED/agi-$AGI_WANTED-linux.deb"
            echo "  via dpkg, with Depends retargeted at ${AGI_JRE:-the installed JRE}"
        else
            echo "  $AGI_BASE_URL/v$AGI_WANTED/agi-$AGI_WANTED-linux.zip"
            echo "  unpacked into /opt/agi, symlinked as /usr/local/bin/agi"
        fi
        [ -n "$AGI_INSTALLED" ] && echo "  (currently installed: $AGI_INSTALLED)"
    fi
    exit 0
fi

# ------------------------------------------------------------- install

echo
case "$PM" in
apt)
    bold "Updating package lists..."
    if ! apt-get update -qq; then
        red "apt-get update failed -- continuing with the cached lists."
    fi
    ;;
pacman)
    # No "pacman -Sy" here on purpose: refreshing the database without also
    # upgrading is how Arch systems end up half-broken.  If a package fetch
    # 404s below, the sync database is stale -- run "pacman -Syu" first.
    bold "Using the existing pacman database (run 'pacman -Syu' if it is stale)."
    ;;
esac

OPT_FAILED=()

if [ "$WANT_RSYNC" -eq 1 ]; then
    echo
    bold "Installing required packages..."
    if ! pkg_install "${REQUIRED[@]}"; then
        # Fall back to one-at-a-time so the failing package is obvious.
        red "Bulk install failed; retrying individually to find the culprit..."
        FAILED=()
        for p in "${REQUIRED[@]}"; do
            pkg_install "$p" || FAILED+=("$p")
        done
        [ ${#FAILED[@]} -eq 0 ] || die "could not install: ${FAILED[*]}"
    fi

    if [ ${#OPTIONAL[@]} -gt 0 ]; then
        echo
        bold "Installing optional packages..."
        if ! pkg_install "${OPTIONAL[@]}"; then
            red "Bulk install failed; retrying individually..."
            for p in "${OPTIONAL[@]}"; do
                pkg_install "$p" || OPT_FAILED+=("$p")
            done
        fi
    fi
fi

# ----------------------------------------------------------------- AGI

# The .desktop and MIME files the .deb ships, replayed for the .zip install so
# both routes leave the same system behind.
agi_desktop_files() {
    cat >/usr/share/applications/google-agi.desktop <<'DESKTOP'
[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=Android GPU Inspector
Comment=Android GPU Inspector
Categories=Development;Debugger;Graphics;3DGraphics
Icon=/opt/agi/icon.png
Exec=/opt/agi/agi %f
Terminal=false
MimeType=application/x-gfxtrace;application/x-perfetto
DESKTOP

    mkdir -p /usr/share/mime/packages
    cat >/usr/share/mime/packages/agi.xml <<'MIME'
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
   <mime-type type="application/x-gfxtrace">
     <comment>Graphics API Trace</comment>
     <glob pattern="*.gfxtrace"/>
   </mime-type>
   <mime-type type="application/x-perfetto">
     <comment>System Profile Trace</comment>
     <glob pattern="*.perfetto"/>
   </mime-type>
</mime-info>
MIME

    command -v update-mime-database >/dev/null 2>&1 \
        && update-mime-database /usr/share/mime >/dev/null 2>&1
    command -v update-desktop-database >/dev/null 2>&1 \
        && update-desktop-database /usr/share/applications >/dev/null 2>&1
    return 0
}

agi_download() {
    # $1 = url, $2 = destination file
    echo "  fetching $(basename "$1")..."
    curl -fL --retry 3 --progress-bar -o "$2" "$1"
}

# Every dependency the .deb names has to exist in the archive, or dpkg will
# refuse the package.  openjdk-22-jre does not exist in any Ubuntu release,
# so rewrite that line to the JRE we just installed.  Only the control member
# is rebuilt -- unpacking and recompressing the 230MB payload with dpkg-deb
# would take minutes and change nothing.
deb_depends_satisfiable() {
    local dep
    while IFS= read -r dep; do
        dep=$(echo "$dep" | sed 's/(.*)//; s/^[[:space:]]*//; s/[[:space:]]*$//')
        [ -z "$dep" ] && continue
        pkg_available "$dep" || return 1
    done < <(dpkg-deb -f "$1" Depends 2>/dev/null | tr ',' '\n')
    return 0
}

rewrite_deb_depends() {
    # $1 = .deb, $2 = new Depends line, $3 = scratch directory
    local deb="$1" depends="$2" work="$3/control"
    command -v ar >/dev/null 2>&1 || return 1
    rm -rf "$work"
    mkdir -p "$work/c" || return 1
    ( cd "$work" && ar x "$deb" control.tar.xz ) || return 1
    tar xf "$work/control.tar.xz" -C "$work/c" || return 1
    sed -i "s|^Depends:.*|Depends: $depends|" "$work/c/control" || return 1
    rm -f "$work/control.tar.xz"
    ( cd "$work/c" && tar cJf ../control.tar.xz . ) || return 1
    ( cd "$work" && ar r "$deb" control.tar.xz ) || return 1
    # Make sure dpkg still understands the archive we just edited.
    dpkg-deb -f "$deb" Depends >/dev/null 2>&1 || return 1
}

install_agi_deb() {
    local ver="$1" tmp="$2" deb="$tmp/agi.deb" depends

    agi_download "$AGI_BASE_URL/v$ver/agi-$ver-linux.deb" "$deb" || return 1

    if ! deb_depends_satisfiable "$deb"; then
        depends="${AGI_JRE:-default-jre}"
        [ -n "$AGI_GTK" ]    && depends="$depends, $AGI_GTK"
        [ -n "$AGI_WEBKIT" ] && depends="$depends, $AGI_WEBKIT"
        echo "  retargeting the package's Depends at: $depends"
        rewrite_deb_depends "$deb" "$depends" "$tmp" || {
            red "  could not rewrite the .deb's dependencies."
            return 1
        }
    fi

    if ! dpkg -i "$deb"; then
        red "  dpkg reported a problem; asking apt to resolve it..."
        apt-get -f install -y || return 1
        dpkg -i "$deb" || return 1
    fi
}

install_agi_zip() {
    local ver="$1" tmp="$2"

    agi_download "$AGI_BASE_URL/v$ver/agi-$ver-linux.zip" "$tmp/agi.zip" || return 1
    unzip -q "$tmp/agi.zip" -d "$tmp/unpacked" || return 1
    [ -x "$tmp/unpacked/agi/agi" ] || {
        red "  the zip did not contain agi/agi -- leaving /opt/agi alone."
        return 1
    }

    # Only ever replace a directory that looks like a previous AGI install.
    if [ -e /opt/agi ] && [ ! -e /opt/agi/build.properties ]; then
        red "  /opt/agi exists but is not an AGI install; refusing to replace it."
        return 1
    fi

    rm -rf /opt/agi
    mv "$tmp/unpacked/agi" /opt/agi || return 1
    chown -R root:root /opt/agi
    ln -sf /opt/agi/agi /usr/local/bin/agi
    agi_desktop_files
}

AGI_OK=0
if [ "$WANT_AGI" -eq 1 ]; then
    echo
    bold "Installing AGI dependencies..."
    if ! pkg_install "${AGI_DEPS[@]}"; then
        red "Bulk install failed; retrying individually..."
        for p in "${AGI_DEPS[@]}"; do
            pkg_install "$p" || OPT_FAILED+=("$p")
        done
    fi

    echo
    if [ -n "$AGI_INSTALLED" ] && [ "$AGI_INSTALLED" = "$AGI_WANTED" ]; then
        bold "AGI $AGI_INSTALLED is already installed in /opt/agi."
        AGI_OK=1
    else
        bold "Installing AGI $AGI_WANTED..."
        [ -n "$AGI_INSTALLED" ] && echo "  replacing the installed $AGI_INSTALLED"
        AGI_TMP=$(mktemp -d "${TMPDIR:-/tmp}/agi-install.XXXXXX") \
            || die "could not create a temporary directory."
        # 85MB of .deb or .zip; do not leave it behind on any exit path.
        trap 'rm -rf "$AGI_TMP"' EXIT

        if [ "$PM" = apt ]; then
            if install_agi_deb "$AGI_WANTED" "$AGI_TMP"; then
                AGI_OK=1
            else
                red "  .deb install failed; falling back to the zip release."
                install_agi_zip "$AGI_WANTED" "$AGI_TMP" && AGI_OK=1
            fi
        else
            install_agi_zip "$AGI_WANTED" "$AGI_TMP" && AGI_OK=1
        fi

        rm -rf "$AGI_TMP"
        trap - EXIT
        [ "$AGI_OK" -eq 1 ] || red "AGI installation failed."
    fi
fi

# --------------------------------------------------------------- verify

MISSING=()
check_cmd() {
    if command -v "$1" >/dev/null 2>&1; then
        printf '  %-16s %s\n' "$1" "$(command -v "$1")"
    else
        printf '  %-16s %s\n' "$1" "MISSING"
        MISSING+=("$1")
    fi
}

if [ "$WANT_RSYNC" -eq 1 ]; then
    echo
    bold "Verifying the toolchain..."

    for c in gcc g++ make gawk autoconf automake cmake ninja python3 git; do
        check_cmd "$c"
    done

    # Not required to build, so report it without failing the run.
    if command -v shellcheck >/dev/null 2>&1; then
        printf '  %-16s %s\n' "shellcheck" "$(command -v shellcheck)"
    else
        printf '  %-16s %s\n' "shellcheck" "not installed (optional)"
    fi

    echo
    bold "Verifying optional libraries..."
    check_header() {
        # $1 = human name, $2 = header path
        if [ -e "/usr/include/$2" ] || \
           find /usr/include -maxdepth 3 -name "$(basename "$2")" -print -quit \
                2>/dev/null | grep -q .; then
            printf '  %-16s yes\n' "$1"
        else
            printf '  %-16s no\n' "$1"
        fi
    }
    check_header acl        sys/acl.h
    check_header xattr      sys/xattr.h
    check_header xxhash     xxhash.h
    check_header zstd       zstd.h
    check_header lz4        lz4.h
    check_header openssl    openssl/md5.h
    check_header zlib       zlib.h

    echo
    bold "Verifying the manpage generator..."
    MD_OK=0
    for m in cmarkgfm commonmark; do
        if python3 -c "import $m" >/dev/null 2>&1; then
            echo "  python3 module '$m' importable"
            MD_OK=1
            break
        fi
    done
    if [ "$MD_OK" -eq 0 ]; then
        if [ "$MINIMAL" -eq 1 ]; then
            echo "  skipped (--minimal); build with ./configure --disable-md2man"
        else
            red "  neither cmarkgfm nor commonmark is importable."
            echo "  Install one with:  python3 -mpip install --user commonmark"
            echo "  ...or build with:  ./configure --disable-md2man"
        fi
    fi
fi

if [ "$WANT_AGI" -eq 1 ]; then
    echo
    bold "Verifying AGI..."
    if [ -x /opt/agi/agi ]; then
        printf '  %-16s %s\n' "agi" "/opt/agi/agi ($(agi_installed_version))"
    else
        printf '  %-16s %s\n' "agi" "MISSING"
    fi

    # The launcher settles for a JRE >= 11, but the GUI's jar is Java 19
    # bytecode -- an older JVM starts and then dies on UnsupportedClassVersion.
    if command -v java >/dev/null 2>&1; then
        JAVA_MAJOR=$(java -version 2>&1 \
            | sed -n '1s/.*version "\([0-9]\{1,\}\).*/\1/p')
        JAVA_MAJOR=${JAVA_MAJOR:-0}
        if [ "$JAVA_MAJOR" -ge 19 ]; then
            printf '  %-16s java %s\n' "jre" "$JAVA_MAJOR"
        else
            printf '  %-16s java %s (too old, AGI needs 19+)\n' "jre" "$JAVA_MAJOR"
        fi
    else
        printf '  %-16s %s\n' "jre" "MISSING"
    fi

    for lib in libgtk-3.so.0 libwebkit2gtk-4.1.so.0; do
        if ldconfig -p 2>/dev/null | grep -q "$lib"; then
            printf '  %-16s yes\n' "${lib%%.so*}"
        else
            printf '  %-16s no\n' "${lib%%.so*}"
        fi
    done

    # Not fatal: AGI runs without adb, it just cannot see Android devices.
    if command -v adb >/dev/null 2>&1; then
        printf '  %-16s %s\n' "adb" "$(command -v adb)"
    else
        printf '  %-16s %s\n' "adb" "not installed (no Android devices)"
    fi
fi

# ---------------------------------------------------------------- done

echo
if [ ${#MISSING[@]} -ne 0 ]; then
    red "Missing required tools: ${MISSING[*]}"
    exit 1
fi

if [ ${#OPT_FAILED[@]} -ne 0 ]; then
    red "Optional packages that failed to install: ${OPT_FAILED[*]}"
    echo "The build will still work, with those features disabled."
fi

if [ "$WANT_RSYNC" -eq 1 ]; then
    green "All required build dependencies are installed."
    cat <<'EOF'

Build rsync with either build system:

  autoconf:   ./configure && make
  CMake:      cmake -B build -G Ninja && cmake --build build

Run the test suite (needs the autoconf build's helper programs):

  make check
EOF
fi

if [ "$WANT_AGI" -eq 1 ] && [ "$AGI_OK" -eq 1 ]; then
    green "AGI $AGI_WANTED is installed."
    if [ "$PM" = apt ]; then
        cat <<'EOF'

Run it from the desktop menu ("Android GPU Inspector") or:

  /opt/agi/agi          # GUI      (--console to keep it in the terminal)
  /opt/agi/gapit        # CLI

Remove it with:  sudo apt-get remove agi
EOF
    else
        cat <<'EOF'

Run it from the desktop menu ("Android GPU Inspector") or:

  agi                   # GUI, symlinked into /usr/local/bin
  /opt/agi/gapit        # CLI

Remove it with:  sudo rm -rf /opt/agi /usr/local/bin/agi \
                     /usr/share/applications/google-agi.desktop \
                     /usr/share/mime/packages/agi.xml
EOF
    fi
    echo "Tracing an Android device needs adb and USB debugging enabled on it."
fi
