#!/usr/bin/env bash

: <<'END'
dhmode
Switch DigiHub's active digital mode: stops every conflicting service
and starts/enables the one requested, since most DigiHub digital modes
share the same sound card / serial port and cannot run simultaneously.

Version 0.1

Steve de Bode - W0FFS - July 2026

Input:  MODE           one of: standby tnc tnc300b tracker digipeater
                        node winlinkrms wsjtx js8call qsstv
        --boot          apply the mode already persisted in $HOME/.dhinfo
                         (defaultmode field), for use by a boot-time unit
        --services MODE print the systemd service(s) MODE maps to and exit;
                         read-only, takes no action, does not need .dhinfo
        -h, --help      show this help text
Output: none - manages systemd services, updates $HOME/.dhinfo's
               defaultmode field (unless run with --boot or --services)
END

set -uo pipefail

UI_LIB="/usr/local/lib/digihub/dh.lib"
[ -r "$UI_LIB" ] && source "$UI_LIB" || {
  printf "\nERROR: Missing UI library: %s\n\n" "$UI_LIB" >&2
  exit 1
}

usage() {
  sed -n '/^: <<'\''END'\''/,/^END$/p' "$0" | sed '1d;$d'
}

if [[ "${1:-}" == "-h" || "${1:-}" == "--help" || $# -eq 0 ]]; then
  usage
  exit 0
fi

DhInfoPath="$HOME/.dhinfo"

# Every mode DigiHub is designed to eventually support. As each phase of
# the DigiPi replacement lands (Direwolf, rigctld, Pat/Winlink, ARDOP,
# AX.25 node/BBS, VNC-streamed GUI apps), add its systemd service
# name(s) to the matching case arm in mode_services() below, instead of
# building a separate switcher per feature.
#
# APRS WebChat is deliberately NOT here: unlike the modes above, it
# doesn't need exclusive radio hardware -- it rides on whichever
# Direwolf-backed mode's KISS-over-TCP port is already active, so it's
# an independent toggle instead (see dhwebchat(1)), like dhrig/dhpat/
# dhardop.
#
# FLDigi is ALSO deliberately not here: it's a GUI app, but its own
# XML-RPC remote-control interface (see dhweb's FLDigi page) is more
# capable than a VNC view for it specifically, and doesn't need
# exclusive audio-device access the way wsjtx/js8call/qsstv do -- so it
# stays a "run locally" app with no service at all.
#
# wsjtx/js8call/qsstv, by contrast, ARE real modes here even though
# they're GUI-only: they need exclusive access to the same sound card
# Direwolf/ardopcf use, so each one's service starts a TigerVNC desktop
# + noVNC bridge (see run_vnc_session() in dh.lib) and runs the app on
# it, matching DigiPi's own wsjtx.service/js8call.service/sstv.service,
# which do exactly the same exclusive stop-everything-else dance.
KNOWN_MODES="standby tnc tnc300b tracker digipeater node winlinkrms wsjtx js8call qsstv"

is_known_mode() {
  local m
  for m in $KNOWN_MODES; do
    [[ "$m" == "$1" ]] && return 0
  done
  return 1
}

# Print the systemd service name(s) (space-separated) that implement a
# mode, or nothing if that mode has no service wired up yet.
mode_services() {
  case "$1" in
    standby) : ;;
    tnc) printf 'dhdirewolf.service\n' ;;
    tnc300b) printf 'dhdirewolf.service\n' ;;
    tracker) printf 'dhdirewolf.service\n' ;;
    digipeater) printf 'dhdirewolf.service\n' ;;
    node) printf 'dhnode.service\ndhdirewolf.service\n' ;;
    winlinkrms) : ;;
    wsjtx) printf 'dhwsjtx.service\n' ;;
    js8call) printf 'dhjs8call.service\n' ;;
    qsstv) printf 'dhqsstv.service\n' ;;
  esac
}

# run_sudo() and ensure_unit_installed() are provided by dh.lib.

# Run before a mode's service(s) are started, to generate any config the
# service needs. Returns non-zero to abort the whole mode switch (leaving
# the previous mode running) if setup fails.
mode_prestart() {
  case "$1" in
    tnc|tnc300b|tracker|digipeater|node)
      run_sudo python3 /usr/local/bin/direwolfconf.py "$1" || return 1
      ;;
  esac
  case "$1" in
    node)
      run_sudo python3 /usr/local/bin/nodeconf.py
      ;;
    *) return 0 ;;
  esac
}

# Read-only introspection for callers (e.g. dhweb) that need to know which
# service(s) a mode maps to, without .dhinfo needing to exist and without
# taking any privileged action.
if [[ "${1:-}" == "--services" ]]; then
  is_known_mode "${2:-}" || exit 2
  mode_services "$2"
  exit 0
fi

persist=1
if [[ "${1:-}" == "--boot" ]]; then
  persist=0
  need_file "$DhInfoPath" "Run install.sh to install DigiHub first."
  IFS=',' read -r -a boot_fields < "$DhInfoPath" || true
  mode="${boot_fields[20]:-standby}"
else
  mode="$1"
fi

if ! is_known_mode "$mode"; then
  printf 'Unknown mode "%s".\nKnown modes: %s\n' "$mode" "$KNOWN_MODES" >&2
  exit 2
fi

need_file "$DhInfoPath" "Run install.sh to install DigiHub first."

if ! mode_prestart "$mode"; then
  printf '\nCould not prepare "%s" mode; leaving the current mode running.\n\n' "$mode" >&2
  exit 1
fi

target_services="$(mode_services "$mode")"
for svc in $target_services; do
  if ! ensure_unit_installed "$svc"; then
    printf '\nCould not prepare "%s" mode; leaving the current mode running.\n\n' "$mode" >&2
    exit 1
  fi
done

# Stop every other mode's service(s) before starting the requested one.
for m in $KNOWN_MODES; do
  [[ "$m" == "$mode" ]] && continue
  for svc in $(mode_services "$m"); do
    run_sudo systemctl disable --now "$svc" >/dev/null 2>&1 || true
  done
done

if [[ -z "$target_services" ]]; then
  if [[ "$mode" != "standby" ]]; then
    printf '\n"%s" mode is not yet implemented on this DigiHub installation.\n' "$mode" >&2
    printf 'The default mode has been recorded, but no service was started.\n\n' >&2
  fi
else
  for svc in $target_services; do
    run_sudo systemctl enable --now "$svc" >/dev/null 2>&1 || true
  done
fi

if (( persist )); then
  IFS=',' read -r -a fields < "$DhInfoPath"
  fields[20]="$mode"

  cp -f -- "$DhInfoPath" "$DhInfoPath.last" >/dev/null 2>&1 || true

  tmp="$(mktemp)"
  (IFS=','; printf '%s\n' "${fields[*]}") > "$tmp"
  mv -f "$tmp" "$DhInfoPath"
fi

printf 'DigiHub mode set to "%s".\n' "$mode"
