#!/bin/bash -eu
# SPDX-FileCopyrightText: 2025 Uwe Fechner
# SPDX-License-Identifier: MIT

if [[ $(basename $(pwd)) == "bin" ]]; then
    cd ..
fi

print_usage() {
    cat <<'USAGE'
Usage: bin/install [-y] [-u] [-h]

Install the Julia environments of this repository: the tracked
Manifest-v<major>.toml.default is copied over Manifest-v<major>.toml, and the main,
examples and docs projects are instantiated and precompiled from it. Nothing else -
no test suite, no system image, and nothing outside the repository.

  -y, --yes     no terminal: skip the Julia version menu and use the julia on PATH
  -u, --update  update the live manifests instead of installing the tracked ones,
                leaving the .default files untouched
  -h, --help    print this message
USAGE
}

assume_yes=false
update_manifests=false
while [[ $# -gt 0 ]]; do
    case "$1" in
        -y|--yes)    assume_yes=true ;;
        -u|--update) update_manifests=true ;;
        -h|--help)   print_usage; exit 0 ;;
        *)           echo "Error: unknown option '$1'."; print_usage; exit 1 ;;
    esac
    shift
done

if ! command -v julia &> /dev/null; then
    echo "Error: julia is not installed. Please install juliaup first."
    case "$(uname -s)" in
        Linux|Darwin)
            echo "  curl -fsSL https://install.julialang.org | sh -s -- --default-channel 1.12"
            ;;
        MINGW*|MSYS*|CYGWIN*)
            echo "  winget install --name Julia --id 9NJNWW8PVKMN -e -s msstore"
            ;;
        *)
            echo "  See https://github.com/JuliaLang/juliaup for installation instructions."
            ;;
    esac
    exit 1
fi

# Check available disk space in home directory (require at least 5 GB)
_required_kb=$((5 * 1024 * 1024))
_available_kb=$(df -k "$HOME" | awk 'NR==2 {print $4}')
if [[ -z "${_available_kb:-}" ]]; then
    echo "Error: Could not determine free disk space for $HOME."
    exit 1
fi
if (( _available_kb < _required_kb )); then
    _available_gb=$(awk -v kb="$_available_kb" 'BEGIN {printf "%.2f", kb/1024/1024}')
    echo "Error: Not enough free disk space in $HOME."
    echo "At least 5.00 GB required, found ${_available_gb} GB."
    exit 1
fi
unset _required_kb _available_kb _available_gb

# Ask which Julia version to use. It is addressed as `julia +<channel>`, so the
# machine's default channel stays where the user put it.
JULIA=(julia)
if [[ $assume_yes == false ]]; then
    if ! command -v juliaup &> /dev/null; then
        echo "Error: juliaup is not installed, so the Julia version cannot be chosen."
        echo "Run 'bin/install -y' to install with the julia already on PATH."
        exit 1
    fi
    if juliaup status 2>/dev/null | grep '^\s*\*' | grep -q '1\.13'; then
        _default_choice=2
        _default_julia="1.13"
    else
        _default_choice=1
        _default_julia="1.12"
    fi
    echo "Which Julia version do you want to use?"
    echo "  1) Julia 1.12"
    echo "  2) Julia 1.13"
    read -rp "Enter 1 or 2 [default: ${_default_choice}]: " _julia_choice
    case "${_julia_choice}" in
        1)
            _desired_julia="1.12"
            ;;
        2)
            _desired_julia="1.13"
            ;;
        "")
            _desired_julia="${_default_julia}"
            ;;
        *)
            echo "Invalid choice: '${_julia_choice}'. Please enter 1 or 2."
            exit 1
            ;;
    esac
    echo "Installing Julia ${_desired_julia}..."
    juliaup add "${_desired_julia}"
    JULIA=(julia "+${_desired_julia}")
    echo
fi

# Detect Julia version
julia_version=$("${JULIA[@]}" --version | awk '{print($3)}')
julia_major=${julia_version:0:3}
if [[ $julia_major == "1.1" ]]; then
    julia_major=${julia_version:0:4}
fi

echo "Detected Julia version: $julia_version (major: $julia_major)"

export JULIA_PKG_SERVER_REGISTRY_PREFERENCE=eager

if [[ "$(uname -s)" == "Linux" ]]; then
    export FONTCONFIG_FILE=/etc/fonts/fonts.conf
    # Preload Julia-bundled libraries to avoid version conflicts with older system libs.
    # On Ubuntu 22.04: OpenSSL 3.0.2 (artifacts need 3.3+), fontconfig 2.13 (missing
    # FcConfigSetDefaultSubstitute added in 2.15). The dynamic linker reuses already-loaded
    # system libs instead of the bundled ones, so we force the bundled versions in first.
    _PRELOADS=()
    _BUNDLED=$(find ~/.julia/artifacts -maxdepth 3 -name "libcrypto.so.3"   -path "*/lib/*" 2>/dev/null | head -1); [[ -n "$_BUNDLED" ]] && _PRELOADS+=("$_BUNDLED")
    # Pick the fontconfig that exports FcConfigSetDefaultSubstitute (needed by Pango >= 1.57).
    # Multiple artifact versions may coexist; `find | head -1` can return the wrong one.
    _BUNDLED=""
    for _fc in $(find ~/.julia/artifacts -maxdepth 3 -name "libfontconfig.so.1" -path "*/lib/*" 2>/dev/null); do
        if nm -D "$_fc" 2>/dev/null | grep -q FcConfigSetDefaultSubstitute; then
            _BUNDLED="$_fc"
            break
        fi
    done
    [[ -n "$_BUNDLED" ]] && _PRELOADS+=("$_BUNDLED")
    if [[ ${#_PRELOADS[@]} -gt 0 ]]; then
        export LD_PRELOAD=$(IFS=:; echo "${_PRELOADS[*]}")
    fi
    unset _PRELOADS _BUNDLED
fi

# Detect current git branch
if [ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1 ; then
    branch=$(git rev-parse --abbrev-ref HEAD | sed 's/\//-/g')
else
    branch=""
fi

# Delete any existing custom system image for this Julia version and branch
if [[ $branch != "" ]]; then
    SOFILE="bin/kps-image-${julia_major}-${branch}.so"
else
    SOFILE="bin/kps-image-${julia_major}.so"
fi
if test -f "$SOFILE"; then
    echo "Deleting old system image: $SOFILE"
    rm -f "$SOFILE"
fi

# Only accept Julia 1.12 and 1.13
if [[ $julia_major != "1.12" ]] && [[ $julia_major != "1.13" ]]; then
    echo "Error: Julia $julia_major is not supported. Only Julia 1.12 and 1.13 are supported."
    echo "Install it with 'juliaup add 1.12' and pick it from the menu of bin/install."
    exit 1
fi

# Remove any existing Manifest.toml symlink so Julia creates a fresh regular file.
if [ -L "Manifest.toml" ]; then
    rm -f Manifest.toml
    echo "Removed stale symlink Manifest.toml"
fi
if [ -L "Manifest-v${julia_major}.toml" ]; then
    rm -f "Manifest-v${julia_major}.toml"
    echo "Removed stale symlink Manifest-v${julia_major}.toml"
fi

# Copy the default manifest of this Julia version over the live one, unless the live
# manifest is what is being updated
if [[ $update_manifests == false ]]; then
    default_manifest="Manifest-v${julia_major}.toml.default"
    if [ ! -f "$default_manifest" ]; then
        echo "Error: $default_manifest not found"
        exit 1
    fi
    cp "$default_manifest" "Manifest-v${julia_major}.toml"
    echo "Copied $default_manifest to Manifest-v${julia_major}.toml"
fi

# Only the main project has a tracked manifest, so only it installs without resolving.
REGISTRY='using Pkg
if any(r -> r.name == "General", Pkg.Registry.reachable_registries())
    Pkg.Registry.update()
else
    Pkg.Registry.add("General")
end
'
if [[ $update_manifests == true ]]; then
    install_verb="Updating"
    done_message="All projects updated."
    MAIN_CODE="${REGISTRY}Pkg.update()"
    SUBPROJECT_CODE=$MAIN_CODE
else
    install_verb="Instantiating"
    done_message="All projects installed."
    MAIN_CODE="${REGISTRY}Pkg.instantiate()"
    SUBPROJECT_CODE="${REGISTRY}Pkg.resolve(); Pkg.instantiate()"
fi

FAILED_PROJECTS=()

# Install one project and precompile it, recording the project when the install fails.
install_project() {
    local label=$1 project=$2 code=$3
    echo "${install_verb} ${label} project..."
    if "${JULIA[@]}" "--project=${project}" -e "$code"; then
        echo "Precompiling ${label} project..."
        "${JULIA[@]}" "--project=${project}" -e 'using Pkg; Pkg.precompile()'
    else
        FAILED_PROJECTS+=("${label}")
    fi
}

install_project "main" "." "$MAIN_CODE"
# Pkg writes a plain Manifest.toml where it found none; name it for this Julia version.
if [ -f "Manifest.toml" ]; then
    mv Manifest.toml "Manifest-v${julia_major}.toml"
    echo "Renamed Manifest.toml to Manifest-v${julia_major}.toml"
fi

rm -f examples/Manifest*.toml
cp "Manifest-v${julia_major}.toml" "examples/Manifest-v${julia_major}.toml"
install_project "examples" "examples" "$SUBPROJECT_CODE"
if [ -f "examples/Manifest.toml" ]; then
    mv examples/Manifest.toml "examples/Manifest-v${julia_major}.toml"
    echo "Renamed examples/Manifest.toml to examples/Manifest-v${julia_major}.toml"
fi

# Docs resolves independently (different dep tree)
rm -f docs/Manifest*.toml
install_project "docs" "docs" "$SUBPROJECT_CODE"
if [ -f "docs/Manifest.toml" ]; then
    mv docs/Manifest.toml "docs/Manifest-v${julia_major}.toml"
    echo "Renamed docs/Manifest.toml to docs/Manifest-v${julia_major}.toml"
fi

echo
if [[ ${#FAILED_PROJECTS[@]} -gt 0 ]]; then
    echo "The following projects FAILED:"
    for _label in "${FAILED_PROJECTS[@]}"; do
        echo "  - ${_label}"
    done
    exit 1
fi

printf "\033[1;32m%s\033[0m\n" "$done_message"
echo
echo "You can now launch Julia by typing"
echo "cd .."
echo "bin/run_julia"
echo "and then get the menu with the examples by typing:"
echo "menu()"
