#!/usr/bin/env bash

set -u

readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly APP_REGISTRY="${APP_REGISTRY:-/usr/share/mpwrd-menu/mesh-apps.conf}"
readonly DISPLAY_REGISTRY="${DISPLAY_REGISTRY:-/usr/share/mpwrd-menu/display-profiles.conf}"
readonly SERVICE_REGISTRY="${SERVICE_REGISTRY:-/usr/share/mpwrd-menu/mesh-services.conf}"
readonly MESHTASTIC_PACKAGE="meshtasticd"
readonly MESHTASTIC_DEBIAN_SERIES="Debian_13"
readonly MPWRD_MENU_PACKAGE="mpwrd-menu"
readonly MPWRD_RELEASE_FILE="/etc/mPWRD-release"
readonly REPO_CHANNELS=("beta" "alpha" "daily")
readonly AVAILABLE_CONFIG_DIR="/etc/meshtasticd/available.d"
readonly ACTIVE_CONFIG_DIR="/etc/meshtasticd/config.d"
readonly ARMBIAN_ENV_FILE="${ARMBIAN_ENV_FILE:-/boot/armbianEnv.txt}"
readonly COLOR_RESET=$'\033[0m'
readonly COLOR_YELLOW=$'\033[33m'

MENU_RESULT=-1
READ_KEY=""
APP_LABELS=()
APP_MANAGERS=()
APP_PACKAGES=()
APP_ACTION_LABELS=("Install" "Upgrade" "Uninstall" "Back")
APP_ACTION_KEYS=("install" "upgrade" "uninstall" "back")
DISPLAY_LABELS=()
DISPLAY_BOARDS=()
DISPLAY_OVERLAYS=()
DISPLAY_PROBE_KINDS=()
DISPLAY_PROBE_BUSES=()
DISPLAY_PROBE_ADDRS=()
DISPLAY_PROBE_REGS=()
DISPLAY_PROBE_VALUES=()
SERVICE_LABELS=()
SERVICE_UNITS=()
SERVICE_ACTION_LABELS=("Status" "Logs (tail Ctrl+C to end)" "Start" "Stop" "Restart" "Enable" "Disable" "Back")
SERVICE_ACTION_KEYS=("status" "logs" "start" "stop" "restart" "enable" "disable" "back")

cleanup() {
  printf '\033[0m\033[?25h'
}

trap cleanup EXIT INT TERM

hide_cursor() {
  printf '\033[?25l'
}

show_cursor() {
  printf '\033[?25h'
}

clear_screen() {
  printf '\033[2J\033[H'
}

terminal_rows() {
  local rows=""
  local cols=""

  if command_exists tput; then
    rows="$(tput lines 2>/dev/null || true)"
  fi

  if [[ ! "$rows" =~ ^[0-9]+$ ]] && read -r rows cols < <(stty size 2>/dev/null); then
    :
  fi

  if [[ ! "$rows" =~ ^[0-9]+$ ]] || (( rows < 12 )); then
    rows=24
  fi

  printf '%s' "$rows"
}

menu_window_size() {
  local rows=0
  local window_size=0

  rows="$(terminal_rows)"
  window_size=$(( rows - 10 ))
  if (( window_size < 4 )); then
    window_size=4
  fi

  printf '%s' "$window_size"
}

pause_prompt() {
  printf '\nPress Enter to continue...'
  read -r _
}

message_box() {
  show_cursor
  clear_screen
  printf '%s\n' "$1"
  pause_prompt
  hide_cursor
}

trim() {
  local value="$1"
  value="${value#"${value%%[![:space:]]*}"}"
  value="${value%"${value##*[![:space:]]}"}"
  printf '%s' "$value"
}

mpwrd_os_version() {
  local version=""

  if [[ ! -r "$MPWRD_RELEASE_FILE" ]]; then
    return 1
  fi

  IFS= read -r version < "$MPWRD_RELEASE_FILE" || true
  version="$(trim "$version")"
  if [[ -z "$version" ]]; then
    return 1
  fi

  printf '%s' "$version"
}

mpwrd_menu_version() {
  local version=""

  if ! command_exists dpkg-query; then
    return 1
  fi

  version="$(dpkg-query -W -f='${Version}' "$MPWRD_MENU_PACKAGE" 2>/dev/null || true)"
  version="$(trim "$version")"
  if [[ -z "$version" ]]; then
    return 1
  fi

  printf '%s' "$version"
}

board_not_configured() {
  local first_entry=""

  IFS= read -r first_entry < <(find "$ACTIVE_CONFIG_DIR" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null || true)
  if [[ -n "$first_entry" ]]; then
    return 1
  fi

  if [[ -d /proc/device-tree/hat ]]; then
    return 1
  fi

  if command_exists lsusb && lsusb -d 1a86:5512 >/dev/null 2>&1; then
    return 1
  fi

  return 0
}

print_version_info() {
  local menu_version=""
  local os_version=""

  if menu_version="$(mpwrd_menu_version)"; then
    printf 'mPWRD-menu v%s\n' "$menu_version"
  else
    printf 'mPWRD-menu version unavailable\n'
  fi

  if os_version="$(mpwrd_os_version)"; then
    printf 'mPWRD-OS v%s\n' "$os_version"
  else
    printf 'mPWRD-OS version unavailable\n'
  fi
}

read_key() {
  local key extra
  IFS= read -rsn1 key || {
    READ_KEY="quit"
    return 1
  }

  if [[ "$key" == $'\e' ]]; then
    IFS= read -rsn2 -t 0.01 extra || true
    key+="$extra"
  fi

  case "$key" in
    $'\e[A')
      READ_KEY="up"
      ;;
    $'\e[B')
      READ_KEY="down"
      ;;
    ""|$'\n'|$'\r')
      READ_KEY="enter"
      ;;
    $'\e')
      READ_KEY="quit"
      ;;
    *)
      READ_KEY="other"
      ;;
  esac
}

render_menu() {
  local title="$1"
  local prompt="$2"
  local selected="$3"
  local options_name="$4"
  local -n options_ref="$options_name"
  local count=0
  local end=0
  local i=0
  local menu_title="mPWRD-menu"
  local menu_version=""
  local page_size=0
  local release_version=""
  local start=0
  local option_display=""
  local warn_unconfigured=0

  count="${#options_ref[@]}"
  page_size="$(menu_window_size)"
  if (( page_size > count )); then
    page_size="$count"
  fi

  start=$(( selected - (page_size / 2) ))
  if (( start < 0 )); then
    start=0
  fi

  end=$(( start + page_size ))
  if (( end > count )); then
    end="$count"
    start=$(( end - page_size ))
    if (( start < 0 )); then
      start=0
    fi
  fi

  clear_screen
  if menu_version="$(mpwrd_menu_version)"; then
    menu_title+=" v${menu_version}"
  fi
  printf '%s\n' "$menu_title"
  if [[ "$title" == "Main Menu" ]] && release_version="$(mpwrd_os_version)"; then
    printf 'mPWRD-OS v%s\n' "$release_version"
  fi
  if [[ "$title" == "Main Menu" ]] && board_not_configured; then
    printf '%b\n' "${COLOR_YELLOW}BOARD NOT CONFIGURED${COLOR_RESET}"
    warn_unconfigured=1
  fi
  printf '============\n\n'
  printf '%s\n' "$title"
  printf '%s\n\n' "$prompt"

  if (( start > 0 )); then
    printf '   ...\n'
  fi

  for (( i = start; i < end; i++ )); do
    option_display="${options_ref[$i]}"
    if (( warn_unconfigured )) && [[ "$option_display" == "Board Config" ]]; then
      option_display="${COLOR_YELLOW}BOARD CONFIG${COLOR_RESET}"
    fi
    if (( i == selected )); then
      printf ' > %b\n' "$option_display"
    else
      printf '   %b\n' "$option_display"
    fi
  done

  if (( end < count )); then
    printf '   ...\n'
  fi

  printf '\nShowing %d-%d of %d. Arrows to move, Enter to select, Esc to go back.\n' \
    "$(( start + 1 ))" "$end" "$count"
}

menu_choose() {
  local title="$1"
  local prompt="$2"
  local options_name="$3"
  local -n options_ref="$options_name"
  local selected=0

  if (( ${#options_ref[@]} == 0 )); then
    MENU_RESULT=-1
    return 1
  fi

  while true; do
    render_menu "$title" "$prompt" "$selected" "$options_name"
    read_key || return 1

    case "$READ_KEY" in
      up)
        (( selected = (selected - 1 + ${#options_ref[@]}) % ${#options_ref[@]} ))
        ;;
      down)
        (( selected = (selected + 1) % ${#options_ref[@]} ))
        ;;
      enter)
        MENU_RESULT="$selected"
        return 0
        ;;
      quit)
        MENU_RESULT=-1
        return 1
        ;;
    esac
  done
}

command_exists() {
  command -v "$1" >/dev/null 2>&1
}

as_root() {
  if (( EUID == 0 )); then
    "$@"
  elif command_exists sudo; then
    sudo "$@"
  else
    printf 'sudo is required for this action.\n' >&2
    return 127
  fi
}

download_stdout() {
  local url="$1"

  if command_exists curl; then
    curl -fsSL "$url"
  elif command_exists wget; then
    wget -qO- "$url"
  else
    return 127
  fi
}

run_and_pause() {
  local title="$1"
  shift
  local rc

  show_cursor
  clear_screen
  printf '%s\n\n' "$title"
  "$@"
  rc=$?
  printf '\n'
  if (( rc == 0 )); then
    printf 'Completed successfully.\n'
  else
    printf 'Command exited with status %d.\n' "$rc"
  fi
  pause_prompt
  hide_cursor
  return "$rc"
}

run_terminal_program() {
  local mode="$1"
  local program="$2"
  shift 2 || true
  local run_as_root=0

  if ! command_exists "$program"; then
    message_box "Command not found: $program"
    return 1
  fi

  if [[ "$mode" == root-* ]]; then
    run_as_root=1
  fi

  if [[ "$mode" == *pause ]]; then
    if (( run_as_root )); then
      run_and_pause "Launching $program..." as_root "$program" "$@"
    else
      run_and_pause "Launching $program..." "$program" "$@"
    fi
    return $?
  fi

  show_cursor
  clear_screen
  if (( run_as_root )); then
    as_root "$program" "$@"
  else
    "$program" "$@"
  fi
  hide_cursor
}

apt_package_action() {
  local action="$1"
  local package_spec="$2"
  local packages=()

  read -r -a packages <<< "$package_spec"

  case "$action" in
    install)
      as_root apt-get update && as_root apt-get install -y "${packages[@]}"
      ;;
    upgrade)
      as_root apt-get update && as_root apt-get install --only-upgrade -y "${packages[@]}"
      ;;
    uninstall)
      as_root apt-get remove -y "${packages[@]}"
      ;;
  esac
}

install_board_config_file() {
  local source_file="$1"
  local target_file="$2"
  as_root mkdir -p "$ACTIVE_CONFIG_DIR" &&
    as_root install -m 0644 "$source_file" "$target_file" &&
    as_root systemctl restart "$MESHTASTIC_PACKAGE"
}

reset_array() {
  local -n array_ref="$1"
  array_ref=()
}

append_array() {
  local -n array_ref="$1"
  array_ref+=("$2")
}

load_delimited_records() {
  local source_file="$1"
  shift
  local field_names=("$@")
  local field_count="${#field_names[@]}"
  local line valid i
  local fields=()
  local field_name

  for field_name in "${field_names[@]}"; do
    reset_array "$field_name"
  done

  [[ -f "$source_file" ]] || return 0

  while IFS= read -r line || [[ -n "$line" ]]; do
    IFS='|' read -r -a fields <<< "$line"

    for (( i = 0; i < field_count; i++ )); do
      fields[$i]="$(trim "${fields[$i]:-}")"
    done

    if [[ -z "${fields[0]:-}" || "${fields[0]:0:1}" == "#" ]]; then
      continue
    fi

    valid=1
    for (( i = 0; i < field_count; i++ )); do
      if [[ -z "${fields[$i]:-}" ]]; then
        valid=0
        break
      fi
    done

    (( valid )) || continue

    for (( i = 0; i < field_count; i++ )); do
      append_array "${field_names[$i]}" "${fields[$i]}"
    done
  done < "$source_file"
}

resolve_registry_path() {
  local configured_path="$1"
  local file_name="${configured_path##*/}"
  local candidate

  for candidate in \
    "$configured_path" \
    "$SCRIPT_DIR/$file_name"
  do
    if [[ -f "$candidate" ]]; then
      printf '%s' "$candidate"
      return 0
    fi
  done

  printf '%s' "$configured_path"
  return 1
}

load_app_registry() {
  local registry_file

  registry_file="$(resolve_registry_path "$APP_REGISTRY")"
  load_delimited_records "$registry_file" \
    APP_LABELS APP_MANAGERS APP_PACKAGES
}

load_display_registry() {
  local registry_file

  registry_file="$(resolve_registry_path "$DISPLAY_REGISTRY")"
  load_delimited_records "$registry_file" \
    DISPLAY_LABELS DISPLAY_BOARDS DISPLAY_OVERLAYS DISPLAY_PROBE_KINDS \
    DISPLAY_PROBE_BUSES DISPLAY_PROBE_ADDRS DISPLAY_PROBE_REGS DISPLAY_PROBE_VALUES
}

load_service_registry() {
  local registry_file

  registry_file="$(resolve_registry_path "$SERVICE_REGISTRY")"
  load_delimited_records "$registry_file" \
    SERVICE_LABELS SERVICE_UNITS
}

current_repo_channel() {
  local found=()
  local channel

  for channel in "${REPO_CHANNELS[@]}"; do
    if [[ -f "/etc/apt/sources.list.d/network:Meshtastic:${channel}.list" ]]; then
      found+=("$channel")
    fi
  done

  case "${#found[@]}" in
    0)
      printf 'none'
      ;;
    1)
      printf '%s' "${found[0]}"
      ;;
    *)
      printf 'mixed (%s)' "${found[*]}"
      ;;
  esac
}

repo_is_ready() {
  local current
  local channel

  current="$(current_repo_channel)"
  for channel in "${REPO_CHANNELS[@]}"; do
    [[ "$current" == "$channel" ]] && return 0
  done

  return 1
}

linux_distribution_id() {
  if [[ -r /etc/os-release ]]; then
    . /etc/os-release
    printf '%s' "${ID:-}"
    return 0
  fi

  printf 'unknown'
}

switch_repo_channel() {
  local channel="$1"
  local distro
  local repo_http="http://download.opensuse.org/repositories/network:/Meshtastic:/${channel}/${MESHTASTIC_DEBIAN_SERIES}/"
  local repo_https="https://download.opensuse.org/repositories/network:/Meshtastic:/${channel}/${MESHTASTIC_DEBIAN_SERIES}/"
  local list_file="/etc/apt/sources.list.d/network:Meshtastic:${channel}.list"
  local key_file="/etc/apt/trusted.gpg.d/network_Meshtastic_${channel}.gpg"
  local temp_list temp_key existing rc=0

  distro="$(linux_distribution_id)"

  if [[ "$distro" == "ubuntu" || "$distro" == "raspbian" ]]; then
    show_cursor
    clear_screen
    printf 'Switching meshtasticd repository to %s (PPA mode)...\n\n' "$channel"

    as_root apt-get update && as_root apt-get install -y software-properties-common || rc=$?

    if (( rc == 0 )) && ! command_exists add-apt-repository; then
      rc=127
    fi

    if (( rc == 0 )); then
      for existing in "${REPO_CHANNELS[@]}"; do
        as_root add-apt-repository -y --remove "ppa:meshtastic/${existing}" >/dev/null 2>&1 || true
        as_root rm -f "/etc/apt/sources.list.d/network:Meshtastic:${existing}.list"
      done

      as_root add-apt-repository -y "ppa:meshtastic/${channel}" || rc=$?
      (( rc == 0 )) && as_root apt-get update || rc=$?
      if (( rc == 0 )); then
        printf '# marker for mPWRD-menu (PPA: meshtastic/%s)\n' "$channel" | \
          as_root tee "$list_file" >/dev/null
      fi
    fi

    printf '\n'
    if (( rc == 0 )); then
      printf 'meshtasticd repository is now set to %s.\n' "$channel"
    else
      printf 'Repository switch failed with status %d.\n' "$rc"
    fi
    pause_prompt
    hide_cursor
    return "$rc"
  fi

  if ! command_exists gpg; then
    message_box 'gpg is required to install the Meshtastic repository key.'
    return 1
  fi

  if ! command_exists curl && ! command_exists wget; then
    message_box 'curl or wget is required to download the Meshtastic repository key.'
    return 1
  fi

  temp_list="$(mktemp)"
  temp_key="$(mktemp)"

  printf 'deb %s /\n' "$repo_http" > "$temp_list"
  if ! download_stdout "${repo_https}Release.key" | gpg --dearmor > "$temp_key"; then
    rm -f "$temp_list" "$temp_key"
    message_box "Failed to download or convert the Meshtastic ${channel} repository key."
    return 1
  fi

  show_cursor
  clear_screen
  printf 'Switching meshtasticd repository to %s...\n\n' "$channel"

  for existing in "${REPO_CHANNELS[@]}"; do
    as_root rm -f "/etc/apt/sources.list.d/network:Meshtastic:${existing}.list"
    as_root rm -f "/etc/apt/trusted.gpg.d/network_Meshtastic_${existing}.gpg"
  done

  if ! as_root install -m 0644 "$temp_list" "$list_file"; then
    rc=$?
  elif ! as_root install -m 0644 "$temp_key" "$key_file"; then
    rc=$?
  elif ! as_root apt-get update; then
    rc=$?
  fi

  rm -f "$temp_list" "$temp_key"
  if (( rc != 0 )); then
    as_root rm -f "$list_file" "$key_file"
  fi

  printf '\n'
  if (( rc == 0 )); then
    printf 'meshtasticd repository is now set to %s.\n' "$channel"
  else
    printf 'Repository switch failed with status %d.\n' "$rc"
  fi
  pause_prompt
  hide_cursor
  return "$rc"
}

install_meshtasticd() {
  if ! repo_is_ready; then
    message_box 'Select beta, alpha, or daily first so meshtasticd installs from the correct repository.'
    return 1
  fi

  run_and_pause 'Installing meshtasticd...' apt_package_action install "$MESHTASTIC_PACKAGE"
}

upgrade_meshtasticd() {
  if ! repo_is_ready; then
    message_box 'Select beta, alpha, or daily first so meshtasticd upgrades from the correct repository.'
    return 1
  fi

  run_and_pause 'Upgrading meshtasticd...' apt_package_action upgrade "$MESHTASTIC_PACKAGE"
}

uninstall_meshtasticd() {
  run_and_pause 'Uninstalling meshtasticd...' apt_package_action uninstall "$MESHTASTIC_PACKAGE"
}

service_status_prompt() {
  local unit="$1"
  local active enabled

  active="$(systemctl is-active "$unit" 2>/dev/null || true)"
  enabled="$(systemctl is-enabled "$unit" 2>/dev/null || true)"

  if [[ -z "$active" ]]; then
    active='unknown'
  fi

  if [[ -z "$enabled" ]]; then
    enabled='unknown'
  fi

  printf 'Unit: %s | Active: %s | Enabled: %s' "$unit" "$active" "$enabled"
}

manage_service() {
  local unit="$1"
  local action="$2"
  run_and_pause "systemctl ${action} ${unit}" as_root systemctl "$action" "$unit"
}

run_service_action() {
  local unit="$1"
  local action="$2"

  case "$action" in
    status)
      run_and_pause "systemctl status ${unit}" systemctl status --no-pager --full "$unit"
      ;;
    logs)
      run_and_pause "journalctl -u ${unit} -f" as_root journalctl -u "$unit" -n 100 -f --no-pager
      ;;
    start|stop|restart|enable|disable)
      manage_service "$unit" "$action"
      ;;
  esac
}

list_relative_files() {
  local dir="$1"
  local result_name="$2"
  local -n result_ref="$result_name"

  result_ref=()
  [[ -d "$dir" ]] || return 0

  mapfile -t result_ref < <(find "$dir" -mindepth 1 -type f -printf '%P\n' 2>/dev/null | sort)
}

apply_board_config() {
  local relative_path="$1"
  local source_path="$AVAILABLE_CONFIG_DIR/$relative_path"
  local target_path="$ACTIVE_CONFIG_DIR/$(basename "$relative_path")"

  if [[ ! -f "$source_path" ]]; then
    message_box "Config not found: $source_path"
    return 1
  fi

  run_and_pause "Applying board config: $relative_path" install_board_config_file "$source_path" "$target_path"
}

remove_board_config() {
  local filename="$1"
  local target_path="$ACTIVE_CONFIG_DIR/$filename"

  if [[ ! -f "$target_path" ]]; then
    message_box "Active config not found: $target_path"
    return 1
  fi

  run_and_pause "Removing board config: $filename" as_root rm -f "$target_path"
}

manage_app_action() {
  local label="$1"
  local manager="$2"
  local package="$3"
  local action="$4"
  local verb=""
  local resolved_manager=""

  case "$action" in
    install)
      verb="Installing"
      ;;
    upgrade)
      verb="Upgrading"
      ;;
    uninstall)
      verb="Uninstalling"
      ;;
  esac

  resolved_manager="$manager"
  if [[ "$manager" == "pipx" ]]; then
    resolved_manager="$(resolve_pipx_manager "$package")"
  fi

  case "$resolved_manager" in
    apt)
      run_and_pause "${verb} ${label}..." apt_package_action "$action" "$package"
      ;;
    pipx)
      if (( EUID == 0 )); then
        message_box 'Run pipx actions as a regular user, not root.'
        return 1
      fi

      if ! command_exists pipx; then
        message_box 'pipx is not installed.'
        return 1
      fi

      run_and_pause "${verb} ${label}..." pipx "$action" "$package"
      ;;
    pipx-global)
      if ! command_exists pipx; then
        message_box 'pipx is not installed.'
        return 1
      fi

      run_and_pause "${verb} ${label}..." run_pipx_global_action "$action" "$package"
      ;;
    *)
      message_box "Unsupported app manager: $resolved_manager"
      return 1
      ;;
  esac
}

pipx_layout_path() {
  local scope="$1"
  local key="$2"

  python3 - "$scope" "$key" 2>/dev/null <<'PY' || true
import sys
from pipx import paths

scope, key = sys.argv[1:]
ctx = paths.ctx

if scope == "global":
    ctx.make_global()

values = {
    "bin": ctx.bin_dir,
    "home": ctx.home,
}

print(values[key])
PY
}

resolve_pipx_manager() {
  local package="$1"
  local command_path=""
  local global_bin_dir=""
  local global_home=""
  local resolved_path=""
  local user_bin_dir=""
  local user_home=""

  command_path="$(command -v "$package" 2>/dev/null || true)"
  if [[ -z "$command_path" ]]; then
    printf 'pipx'
    return 0
  fi

  if command_exists python3; then
    user_bin_dir="$(pipx_layout_path user bin)"
    user_home="$(pipx_layout_path user home)"
    global_bin_dir="$(pipx_layout_path global bin)"
    global_home="$(pipx_layout_path global home)"
  fi

  resolved_path="$(readlink -f "$command_path" 2>/dev/null || printf '%s' "$command_path")"
  if [[ -n "$user_bin_dir" && "$command_path" == "$user_bin_dir"/* ]]; then
    printf 'pipx'
    return 0
  fi

  if [[ -n "$user_home" && "$resolved_path" == "$user_home"/venvs/* ]]; then
    printf 'pipx'
    return 0
  fi

  if [[ -n "$global_bin_dir" && "$command_path" == "$global_bin_dir"/* ]]; then
    printf 'pipx-global'
    return 0
  fi

  if [[ -n "$global_home" && "$resolved_path" == "$global_home"/venvs/* ]]; then
    printf 'pipx-global'
    return 0
  fi

  printf 'pipx'
}

run_pipx_global_action() {
  local action="$1"
  local package="$2"

  as_root pipx "$action" --global "$package"
}

indexed_list_menu() {
  local title="$1"
  local prompt="$2"
  local labels_name="$3"
  local handler="$4"
  local -n labels_ref="$labels_name"
  local options=()
  local label

  while true; do
    options=()
    for label in "${labels_ref[@]}"; do
      options+=("$label")
    done
    options+=("Back")

    menu_choose "$title" "$prompt" options || return 0
    if (( MENU_RESULT == ${#labels_ref[@]} )); then
      return 0
    fi

    "$handler" "$MENU_RESULT"
  done
}

service_menu() {
  local index="$1"
  local service_label="${SERVICE_LABELS[$index]}"
  local unit="${SERVICE_UNITS[$index]}"
  local prompt
  local action

  while true; do
    prompt="$(service_status_prompt "$unit")"
    menu_choose "$service_label" "$prompt" SERVICE_ACTION_LABELS || return 0
    action="${SERVICE_ACTION_KEYS[$MENU_RESULT]}"
    if [[ "$action" == "back" ]]; then
      return 0
    fi

    run_service_action "$unit" "$action"
  done
}

related_services_menu() {
  local empty_options=("Open Repository Menu" "Back")

  load_service_registry
  if (( ${#SERVICE_LABELS[@]} == 0 )); then
    menu_choose 'Meshtastic Related Services' \
      "No services are configured. Add entries to $SERVICE_REGISTRY." \
      empty_options || return 0
    if (( MENU_RESULT == 0 )); then
      repo_menu
    fi
    return 0
  fi

  indexed_list_menu 'Meshtastic Related Services' \
    'Manage systemd units for Meshtastic support services.' \
    SERVICE_LABELS service_menu
}

repo_menu() {
  local options=()
  local prompt
  local channel
  local channel_count="${#REPO_CHANNELS[@]}"

  while true; do
    options=()
    for channel in "${REPO_CHANNELS[@]}"; do
      options+=("Switch to $channel")
    done
    options+=("Install meshtasticd" "Upgrade meshtasticd" "Uninstall meshtasticd" "Back")

    prompt="Current repo: $(current_repo_channel)"
    menu_choose 'meshtasticd Repository' "$prompt" options || return 0
    if (( MENU_RESULT < channel_count )); then
      switch_repo_channel "${REPO_CHANNELS[$MENU_RESULT]}"
      continue
    fi

    case $(( MENU_RESULT - channel_count )) in
      0)
        install_meshtasticd
        ;;
      1)
        upgrade_meshtasticd
        ;;
      2)
        uninstall_meshtasticd
        ;;
      3)
        return 0
        ;;
    esac
  done
}

app_action_menu() {
  local index="$1"
  local label="${APP_LABELS[$index]}"
  local manager="${APP_MANAGERS[$index]}"
  local package="${APP_PACKAGES[$index]}"
  local prompt="Manager: ${manager} | Package: ${package}"
  local action

  while true; do
    menu_choose "$label" "$prompt" APP_ACTION_LABELS || return 0
    action="${APP_ACTION_KEYS[$MENU_RESULT]}"
    if [[ "$action" == "back" ]]; then
      return 0
    fi

    manage_app_action "$label" "$manager" "$package" "$action"
  done
}

mesh_apps_menu() {
  load_app_registry
  if (( ${#APP_LABELS[@]} == 0 )); then
    message_box "No mesh apps are configured. Add entries to $APP_REGISTRY."
    return 0
  fi

  indexed_list_menu 'Mesh Apps Manager' \
    'Install, upgrade, or remove configured Meshtastic companion apps.' \
    APP_LABELS app_action_menu
}

board_config_file_menu() {
  local title="$1"
  local directory="$2"
  local empty_message="$3"
  local handler="$4"
  local configs=()

  list_relative_files "$directory" configs
  if (( ${#configs[@]} == 0 )); then
    message_box "$empty_message"
    return 0
  fi

  configs+=("Back")
  while true; do
    menu_choose "$title" "$directory" configs || return 0
    if (( MENU_RESULT == ${#configs[@]} - 1 )); then
      return 0
    fi

    "$handler" "${configs[$MENU_RESULT]}"
    return 0
  done
}

current_board_id() {
  local board_value=""

  if [[ -r /etc/armbian-release ]]; then
    board_value="$(. /etc/armbian-release 2>/dev/null && printf '%s' "$BOARD")" || true
  fi

  printf '%s' "$board_value"
}

csv_contains() {
  local needle="$1"
  local csv="$2"
  local entry=""
  local entries=()

  IFS=',' read -r -a entries <<< "$csv"
  for entry in "${entries[@]}"; do
    entry="$(trim "$entry")"
    [[ -n "$entry" && "$entry" == "$needle" ]] && return 0
  done

  return 1
}

collect_display_profile_indices_for_board() {
  local board_id="$1"
  local result_name="$2"
  local -n result_ref="$result_name"
  local i

  load_display_registry
  result_ref=()

  [[ -n "$board_id" ]] || return 0

  for i in "${!DISPLAY_LABELS[@]}"; do
    if csv_contains "$board_id" "${DISPLAY_BOARDS[$i]}"; then
      result_ref+=("$i")
    fi
  done
}

supports_display_profiles() {
  local board_id=""
  local indices=()

  board_id="$(current_board_id)"
  collect_display_profile_indices_for_board "$board_id" indices
  (( ${#indices[@]} > 0 ))
}

i2c_reg_read() {
  local bus="$1"
  local addr="$2"
  local reg="$3"

  as_root python3 - "$bus" "$addr" "$reg" <<'PY'
import fcntl
import os
import sys
import time

I2C_SLAVE_FORCE = 0x0706

bus = int(sys.argv[1], 0)
addr = int(sys.argv[2], 0)
reg = int(sys.argv[3], 0)
path = f"/dev/i2c-{bus}"
fd = None

try:
    fd = os.open(path, os.O_RDWR)
    fcntl.ioctl(fd, I2C_SLAVE_FORCE, addr)
    os.write(fd, bytes([reg]))
    time.sleep(0.01)
    data = os.read(fd, 1)
    if len(data) != 1:
        raise OSError("short read")
    print(f"0x{data[0]:02x}")
except Exception:
    sys.exit(1)
finally:
    if fd is not None:
        os.close(fd)
PY
}

display_profile_matches_probe() {
  local index="$1"
  local kind="${DISPLAY_PROBE_KINDS[$index]}"
  local probe_value=""

  case "$kind" in
    i2c-reg-match)
      probe_value="$(i2c_reg_read "${DISPLAY_PROBE_BUSES[$index]}" \
        "${DISPLAY_PROBE_ADDRS[$index]}" "${DISPLAY_PROBE_REGS[$index]}")" || return 1
      csv_contains "$probe_value" "${DISPLAY_PROBE_VALUES[$index]}"
      ;;
    i2c-reg-low-nibble-match)
      probe_value="$(i2c_reg_read "${DISPLAY_PROBE_BUSES[$index]}" \
        "${DISPLAY_PROBE_ADDRS[$index]}" "${DISPLAY_PROBE_REGS[$index]}")" || return 1
      probe_value="$(printf '0x%02x' "$(( ${probe_value} & 0x0f ))")"
      csv_contains "$probe_value" "${DISPLAY_PROBE_VALUES[$index]}"
      ;;
    *)
      return 1
      ;;
  esac
}

confirm_action() {
  local title="$1"
  local prompt="$2"
  local options=("Apply" "Cancel")

  menu_choose "$title" "$prompt" options || return 1
  (( MENU_RESULT == 0 ))
}

ensure_display_probe_access() {
  if (( EUID == 0 )); then
    return 0
  fi

  if ! command_exists sudo; then
    message_box 'sudo is required for display detection.'
    return 1
  fi

  run_and_pause 'Checking sudo access for display detection...' sudo -v
}

detect_display_profile_index() {
  local result_name="$1"
  local -n result_ref="$result_name"
  local board_id=""
  local candidate_indices=()
  local matched_indices=()
  local matched_labels=()
  local i

  result_ref=""
  board_id="$(current_board_id)"
  collect_display_profile_indices_for_board "$board_id" candidate_indices

  for i in "${candidate_indices[@]}"; do
    if display_profile_matches_probe "$i"; then
      matched_indices+=("$i")
      matched_labels+=("${DISPLAY_LABELS[$i]}")
    fi
  done

  if (( ${#matched_indices[@]} == 0 )); then
    return 1
  fi

  if (( ${#matched_indices[@]} == 1 )); then
    result_ref="${matched_indices[0]}"
    return 0
  fi

  matched_labels+=("Back")
  menu_choose 'Detected Displays' "Board: ${board_id}" matched_labels || return 1
  if (( MENU_RESULT == ${#matched_indices[@]} )); then
    return 1
  fi

  result_ref="${matched_indices[$MENU_RESULT]}"
  return 0
}

choose_display_profile_index_for_board() {
  local result_name="$1"
  local -n result_ref="$result_name"
  local board_id=""
  local indices=()
  local labels=()
  local i

  result_ref=""
  board_id="$(current_board_id)"
  collect_display_profile_indices_for_board "$board_id" indices

  if (( ${#indices[@]} == 0 )); then
    return 1
  fi

  for i in "${indices[@]}"; do
    labels+=("${DISPLAY_LABELS[$i]}")
  done
  labels+=("Back")

  menu_choose 'Choose Display' "Board: ${board_id}" labels || return 1
  if (( MENU_RESULT == ${#indices[@]} )); then
    return 1
  fi

  result_ref="${indices[$MENU_RESULT]}"
  return 0
}

read_armbian_env_value() {
  local key="$1"

  [[ -r "$ARMBIAN_ENV_FILE" ]] || return 0

  python3 - "$ARMBIAN_ENV_FILE" "$key" <<'PY'
from pathlib import Path
import sys

path = Path(sys.argv[1])
key = sys.argv[2]
value = ""

for line in path.read_text().splitlines():
    if "=" not in line:
        continue
    current_key, current_value = line.split("=", 1)
    if current_key == key:
        value = current_value.strip()

print(value, end="")
PY
}

write_armbian_env_value() {
  local key="$1"
  local value="$2"

  if [[ ! -e "$ARMBIAN_ENV_FILE" ]]; then
    printf 'Boot config not found: %s\n' "$ARMBIAN_ENV_FILE" >&2
    return 1
  fi

  as_root python3 - "$ARMBIAN_ENV_FILE" "$key" "$value" <<'PY'
from pathlib import Path
import sys

path = Path(sys.argv[1])
key = sys.argv[2]
value = sys.argv[3]
lines = path.read_text().splitlines()
new_lines = []
updated = False

for line in lines:
    if "=" not in line:
        new_lines.append(line)
        continue
    current_key, _ = line.split("=", 1)
    if current_key == key:
        if not updated and value:
            new_lines.append(f"{key}={value}")
        updated = True
    else:
        new_lines.append(line)

if not updated and value:
    new_lines.append(f"{key}={value}")

path.write_text("\n".join(new_lines) + "\n")
PY
}

collect_known_display_overlays() {
  local result_name="$1"
  local -n result_ref="$result_name"
  local overlay=""
  local seen=()
  local already_seen=0

  load_display_registry
  result_ref=()

  for overlay in "${DISPLAY_OVERLAYS[@]}"; do
    already_seen=0
    for entry in "${seen[@]}"; do
      if [[ "$entry" == "$overlay" ]]; then
        already_seen=1
        break
      fi
    done
    if (( ! already_seen )); then
      seen+=("$overlay")
      result_ref+=("$overlay")
    fi
  done
}

current_display_overlay() {
  local raw_value=""
  local tokens=()
  local known_overlays=()
  local token=""

  raw_value="$(read_armbian_env_value user_overlays)"
  read -r -a tokens <<< "$raw_value"
  collect_known_display_overlays known_overlays

  for token in "${tokens[@]}"; do
    if csv_contains "$token" "$(IFS=,; printf '%s' "${known_overlays[*]}")"; then
      printf '%s' "$token"
      return 0
    fi
  done

  return 1
}

current_display_label() {
  local board_id=""
  local overlay=""
  local indices=()
  local i

  board_id="$(current_board_id)"
  overlay="$(current_display_overlay 2>/dev/null || true)"
  collect_display_profile_indices_for_board "$board_id" indices

  for i in "${indices[@]}"; do
    if [[ "${DISPLAY_OVERLAYS[$i]}" == "$overlay" ]]; then
      printf '%s' "${DISPLAY_LABELS[$i]}"
      return 0
    fi
  done

  printf 'none'
  return 1
}

apply_display_overlay() {
  local overlay_name="$1"
  local raw_value=""
  local current_tokens=()
  local known_overlays=()
  local updated_tokens=()
  local token=""
  local known_csv=""
  local deduped=()
  local already_seen=0
  local entry=""

  raw_value="$(read_armbian_env_value user_overlays)"
  read -r -a current_tokens <<< "$raw_value"
  collect_known_display_overlays known_overlays
  known_csv="$(IFS=,; printf '%s' "${known_overlays[*]}")"

  for token in "${current_tokens[@]}"; do
    if [[ -n "$token" ]] && ! csv_contains "$token" "$known_csv"; then
      updated_tokens+=("$token")
    fi
  done

  if [[ -n "$overlay_name" ]]; then
    updated_tokens+=("$overlay_name")
  fi

  for token in "${updated_tokens[@]}"; do
    already_seen=0
    for entry in "${deduped[@]}"; do
      if [[ "$entry" == "$token" ]]; then
        already_seen=1
        break
      fi
    done
    if (( ! already_seen )); then
      deduped+=("$token")
    fi
  done

  write_armbian_env_value user_overlays "${deduped[*]}"
}

display_reboot_required_message() {
  local message="$1"

  message_box "${message}

Reboot required for the display change to take effect."
}

detect_and_enable_display() {
  local index=""
  local label=""
  local overlay=""

  ensure_display_probe_access || return 1

  if ! detect_display_profile_index index; then
    message_box "No supported display detected for board: $(current_board_id)"
    return 0
  fi

  label="${DISPLAY_LABELS[$index]}"
  overlay="${DISPLAY_OVERLAYS[$index]}"

  if ! confirm_action 'Enable Display' \
    "Detected display: ${label}
Overlay: ${overlay}

This updates ${ARMBIAN_ENV_FILE}."; then
    return 0
  fi

  run_and_pause "Enabling display: ${label}" apply_display_overlay "$overlay" || return 1
  display_reboot_required_message "Enabled display: ${label}"
}

choose_and_enable_display_manually() {
  local index=""
  local label=""
  local overlay=""

  if ! choose_display_profile_index_for_board index; then
    return 0
  fi

  label="${DISPLAY_LABELS[$index]}"
  overlay="${DISPLAY_OVERLAYS[$index]}"

  if ! confirm_action 'Enable Display' \
    "Selected display: ${label}
Overlay: ${overlay}

This updates ${ARMBIAN_ENV_FILE}."; then
    return 0
  fi

  run_and_pause "Enabling display: ${label}" apply_display_overlay "$overlay" || return 1
  display_reboot_required_message "Enabled display: ${label}"
}

disable_display_overlay() {
  local active_label=""
  local active_overlay=""

  active_label="$(current_display_label)"
  active_overlay="$(current_display_overlay 2>/dev/null || true)"

  if [[ -z "$active_overlay" ]]; then
    message_box 'No managed display overlay is currently enabled.'
    return 0
  fi

  if ! confirm_action 'Disable Display' \
    "Active display: ${active_label}
Overlay: ${active_overlay}

This removes the managed display overlay from ${ARMBIAN_ENV_FILE}."; then
    return 0
  fi

  run_and_pause "Disabling display overlay: ${active_label}" apply_display_overlay "" || return 1
  display_reboot_required_message "Disabled display overlay: ${active_label}"
}

display_menu() {
  local board_id=""
  local prompt=""
  local options=(
    "Detect and enable display"
    "Choose display manually"
    "Disable active display overlay"
    "Back"
  )

  board_id="$(current_board_id)"
  if [[ -z "$board_id" ]]; then
    message_box 'Could not detect the current board.'
    return 0
  fi

  if ! supports_display_profiles; then
    message_box "No display profiles are configured for board: ${board_id}"
    return 0
  fi

  while true; do
    prompt="Board: ${board_id} | Active display: $(current_display_label)"
    menu_choose 'Display' "$prompt" options || return 0
    case "$MENU_RESULT" in
      0)
        detect_and_enable_display
        ;;
      1)
        choose_and_enable_display_manually
        ;;
      2)
        disable_display_overlay
        ;;
      3)
        return 0
        ;;
    esac
  done
}

edit_current_config() {
  local config_files=()
  local editor
  local edit_options=()
  local fallback_source="/etc/meshtasticd/config.yaml"
  local fallback_target="$ACTIVE_CONFIG_DIR/config.yaml"
  local relative_path=""

  list_relative_files "$ACTIVE_CONFIG_DIR" config_files
  if (( ${#config_files[@]} == 0 )); then
    if [[ -f "$fallback_source" ]]; then
      run_and_pause \
        "No active configs found. Copying ${fallback_source} to ${fallback_target}..." \
        install_board_config_file "$fallback_source" "$fallback_target" || return 1
      config_files=("config.yaml")
    else
      message_box "No active configs found in $ACTIVE_CONFIG_DIR."
      return 0
    fi
  fi

  editor="${EDITOR:-nano}"
  if ! command_exists "$editor"; then
    message_box "Editor not found: $editor"
    return 1
  fi

  if (( ${#config_files[@]} == 1 )); then
    relative_path="${config_files[0]}"
  else
    edit_options=("${config_files[@]}" "Back")
    while true; do
      menu_choose 'Edit Current Config' "$ACTIVE_CONFIG_DIR" edit_options || return 0
      if (( MENU_RESULT == ${#config_files[@]} )); then
        return 0
      fi

      relative_path="${config_files[$MENU_RESULT]}"
      break
    done
  fi

  run_and_pause "Editing current config with $editor..." "$editor" "$ACTIVE_CONFIG_DIR/$relative_path"
}

i2c_overlay_config_file() {
  if [[ -f /boot/firmware/config.txt ]]; then
    printf '/boot/firmware/config.txt'
    return 0
  fi

  if [[ -f /boot/config.txt ]]; then
    printf '/boot/config.txt'
    return 0
  fi

  return 1
}

is_raspberry_pi_system() {
  local board_value=""
  local model=""

  if [[ -r /etc/armbian-release ]]; then
    board_value="$(. /etc/armbian-release 2>/dev/null && printf '%s' "$BOARD")" || true
    if [[ "$board_value" == rpi* ]]; then
      return 0
    fi
  fi

  if [[ -r /proc/device-tree/model ]]; then
    model="$(tr -d '\0' < /proc/device-tree/model 2>/dev/null || true)"
    if [[ "$model" == *'Raspberry Pi'* ]]; then
      return 0
    fi
  fi

  return 1
}

supports_i2c_overlay() {
  is_raspberry_pi_system && i2c_overlay_config_file >/dev/null
}

enable_i2c_overlay() {
  local config_file=""

  if ! supports_i2c_overlay; then
    message_box 'I2C overlay toggle is only available on Raspberry Pi systems.'
    return 1
  fi

  config_file="$(i2c_overlay_config_file)"
  if ! grep -q '^dtparam=i2c_arm=on' "$config_file" 2>/dev/null; then
    run_and_pause \
      'Enabling I2C overlay for Raspberry Pi...' \
      as_root sh -c "echo 'dtparam=i2c_arm=on' >> '$config_file'"
  else
    message_box 'I2C overlay is already enabled.'
  fi
}

board_config_name_if_compatible() {
  local file="$1"
  local query="$2"

  awk -v query="$query" '
    BEGIN {
      in_meta = 0
      in_compatible = 0
      matched = 0
      name = ""
    }

    /^[[:space:]]*#/ { next }

    /^[^[:space:]#][^:]*:[[:space:]]*$/ {
      if ($0 == "Meta:") {
        in_meta = 1
        in_compatible = 0
        next
      }
      if (in_meta) {
        in_meta = 0
        in_compatible = 0
      }
    }

    !in_meta { next }

    /^  name:[[:space:]]*/ {
      name = $0
      sub(/^  name:[[:space:]]*/, "", name)
      gsub(/[[:space:]]+$/, "", name)
      next
    }

    /^  compatible:[[:space:]]*$/ {
      in_compatible = 1
      next
    }

    in_compatible && /^    -[[:space:]]*/ {
      value = $0
      sub(/^    -[[:space:]]*/, "", value)
      sub(/[[:space:]]*#.*/, "", value)
      gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
      if (value == query) {
        matched = 1
      }
      next
    }

    in_compatible {
      in_compatible = 0
    }

    END {
      if (matched) {
        printf("%s\n", name)
      }
    }
  ' "$file"
}

board_config_query_alias() {
  case "$1" in
    rpi4b)
      printf 'raspberry-pi'
      ;;
    luckfox-lyra-zero-deck)
      printf 'luckfox-lyra-zero-w'
      ;;
    *)
      printf '%s' "$1"
      ;;
  esac
}

find_compatible_configs() {
  local directory="$1"
  local query="$2"
  local result_files_name="$3"
  local result_labels_name="$4"
  local -n result_files_ref="$result_files_name"
  local -n result_labels_ref="$result_labels_name"
  local file name relative_path

  result_files_ref=()
  result_labels_ref=()

  [[ -d "$directory" && -n "$query" ]] || return 0

  while IFS= read -r file; do
    name="$(board_config_name_if_compatible "$file" "$query")"
    [[ -n "$name" ]] || continue

    relative_path="${file#"$directory/"}"
    result_files_ref+=("$relative_path")
    if [[ -n "$name" ]]; then
      result_labels_ref+=("$name [$relative_path]")
    else
      result_labels_ref+=("$relative_path")
    fi
  done < <(find "$directory" -mindepth 1 -type f \( -name '*.yaml' -o -name '*.yml' \) 2>/dev/null | sort)
}

default_board_config_query() {
  local board_value=""

  board_value="$(. /etc/armbian-release 2>/dev/null && printf '%s' "$BOARD")" || true
  case "$board_value" in
    "")
      printf ''
      ;;
    *)
      board_config_query_alias "$board_value"
      ;;
  esac
}

apply_suggested_board_config_menu() {
  local query=""
  local config_files=()
  local config_labels=()
  local options=()

  query="$(default_board_config_query)"

  if [[ -z "$query" ]]; then
    message_box 'Could not detect the current board. Use Browse all configs instead.'
    return 0
  fi

  find_compatible_configs "$AVAILABLE_CONFIG_DIR" "$query" config_files config_labels

  if (( ${#config_files[@]} == 0 )); then
    message_box "No suggested configs found for '$query'. Use Browse all configs instead."
    return 0
  fi

  options=("${config_labels[@]}" "Back")

  while true; do
    menu_choose 'Select Radio Configuration' "Detected board: $query" options || return 0
    if (( MENU_RESULT == ${#config_files[@]} )); then
      return 0
    fi

    apply_board_config "${config_files[$MENU_RESULT]}"
    return 0
  done
}

board_config_menu() {
  local options=(
    "Select radio configuration"
    "Browse all configs"
    "Remove active config"
    "Edit Current Config"
  )
  local has_i2c_overlay=0
  local i2c_option=-1
  local back_option=-1

  if supports_i2c_overlay; then
    i2c_option="${#options[@]}"
    options+=("Enable I2C Overlay rPi")
    has_i2c_overlay=1
  fi

  back_option="${#options[@]}"
  options+=("Back")

  while true; do
    menu_choose 'Board Config' 'Copy configs from available.d into config.d or remove active configs.' options || return 0
    case "$MENU_RESULT" in
      0)
        apply_suggested_board_config_menu
        ;;
      1)
        board_config_file_menu 'Apply Board Config' "$AVAILABLE_CONFIG_DIR" \
          "No configs found in $AVAILABLE_CONFIG_DIR" apply_board_config
        ;;
      2)
        board_config_file_menu 'Remove Board Config' "$ACTIVE_CONFIG_DIR" \
          "No active configs found in $ACTIVE_CONFIG_DIR" remove_board_config
        ;;
      3)
        edit_current_config
        ;;
      *)
        if (( has_i2c_overlay && MENU_RESULT == i2c_option )); then
          enable_i2c_overlay
        elif (( MENU_RESULT == back_option )); then
          return 0
        fi
        ;;
    esac
  done
}

main_menu() {
  local options=(
    "Contact - A Console UI for Meshtastic"
    "Configure"
    "Meshtastic Related Services"
    "meshtasticd Repository"
    "Mesh Apps Manager"
    "Network Quick Start"
    "Board Config"
  )
  local display_option=-1
  local exit_option=-1

  if supports_display_profiles; then
    display_option="${#options[@]}"
    options+=("Display")
  fi

  exit_option="${#options[@]}"
  options+=("Exit")

  while true; do
    menu_choose 'Main Menu' 'Lightweight TUI for mPWRD-OS.' options || return 0
    case "$MENU_RESULT" in
      0)
        run_terminal_program pause contact
        ;;
      1)
        run_terminal_program pause contact -c
        ;;
      2)
        related_services_menu
        ;;
      3)
        repo_menu
        ;;
      4)
        mesh_apps_menu
        ;;
      5)
        run_terminal_program root-no-pause nmtui
        ;;
      6)
        board_config_menu
        ;;
      *)
        if (( display_option >= 0 && MENU_RESULT == display_option )); then
          display_menu
        elif (( MENU_RESULT == exit_option )); then
          return 0
        fi
        ;;
    esac
  done
}

case "${1:-}" in
  --version)
    trap - EXIT INT TERM
    print_version_info
    ;;
  "")
    hide_cursor
    main_menu
    clear_screen
    ;;
  *)
    trap - EXIT INT TERM
    printf 'Usage: %s [--version]\n' "${0##*/}" >&2
    exit 1
    ;;
esac
