#!/usr/bin/env bash
DW_THEME_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "$DW_THEME_DIR/Theme.sh" ]]; then source "$DW_THEME_DIR/Theme.sh"
elif [[ -f "$DW_THEME_DIR/../share/dw-infoscreen/Theme.sh" ]]; then source "$DW_THEME_DIR/../share/dw-infoscreen/Theme.sh"
else C_RESET=""; C_BOLD=""; C_INFO=""; C_GOLD=""; C_OK=""; C_WARN=""; C_DANGER=""; C_MUTED=""; fi
# DW InfoScreen 1.1.1
# Tagesgericht und Preise auf einem Bildschirm: Kiosk-Anzeige (Chromium) mit eigenem Editor im lokalen Netz.
# Alles lokal, keine Verbindung ins Internet, keine Telemetrie. Änderungen nur mit --ausfuehren.
# MIT-Lizenz, (c) 2026 Michael Bergfeld, DoMeZos-Ware
set -u
export LC_ALL=C.UTF-8 2>/dev/null || export LC_ALL=C

VERSION="1.1.1"
PROG="dw-infoscreen"
MARK="dw-infoscreen-eigene-datei"
PORT=8480; ADRESSE="0.0.0.0"
AUSFUEHREN=0; JA=0; OPT_DIENST=0; OPT_AUTOSTART=0; OPT_NEU=0; OPT_EINMAL=0; OPT_SKRIPT=0
SELF="$(readlink -f -- "${BASH_SOURCE[0]}" 2>/dev/null || printf '%s' "${BASH_SOURCE[0]}")"

usage() {
cat <<'EOF'
DW InfoScreen 1.1.1 - Tagesgericht und Preise auf dem Bildschirm

Aufruf:
  dw-infoscreen BEFEHL [Optionen]

Befehle:
  installieren     Zugangscode, Beispieltafel und Anzeige-Seite anlegen; auf Wunsch Dienst und Autostart
                   (ohne --ausfuehren nur ein Probelauf: es wird nichts angelegt)
  server           Editor-Server starten (Port 8480; nur Geräte aus privaten Netzen werden bedient)
  code             Zugangscode anzeigen; mit --neu einen neuen erzeugen (mit Rückfrage)
  anzeige          Pfad der Anzeige-Seite (anzeige.html) ausgeben
  kiosk            Chromium im Kiosk-Modus mit der Anzeige starten (mit Wiederanlauf)
  export ORDNER    Tafeldaten in einen (USB-)Ordner sichern
  import ORDNER    Tafeldaten aus einem Ordner oder einer Datei zurücklesen (ohne --ausfuehren nur Vorschau)
  status           Zustand anzeigen: Daten, Dienst, Autostart, Server erreichbar
  deinstallieren   Dienst und Autostart entfernen (ohne --ausfuehren nur Vorschau; die Daten bleiben)

Optionen:
  --ausfuehren     Änderungen wirklich durchführen (Standard bei installieren, import, deinstallieren: Probelauf)
  --ja             bei code --neu die Rückfrage bestätigen (für Skripte); Dienst/Autostart nur mit --dienst/--autostart
  --dienst         bei installieren: Editor als Benutzerdienst (systemd --user) einrichten
  --autostart      bei installieren: Kiosk-Anzeige beim Anmelden automatisch starten
  --neu            bei code: neuen Zugangscode erzeugen
  --port N         Port des Editors (Standard 8480)
  --adresse IP     Adresse, an die der Server sich bindet (Standard 0.0.0.0; 127.0.0.1 = nur dieses Gerät)
  --einmal         bei kiosk: Chromium nur einmal starten (kein Wiederanlauf)
  --als-skript     bei kiosk: nur das Startskript ausgeben, nichts starten
  --version        Version anzeigen
  -h, --help, --hilfe  diese Hilfe

Ablauf: 1. dw-infoscreen installieren --ausfuehren   2. dw-infoscreen server   3. Editor im Browser öffnen
        (http://<IP des Geräts>:8480/, Zugangscode von "dw-infoscreen code")   4. dw-infoscreen kiosk

Sicherheit: kein Zugriff aus dem Internet (nur private Netze), Zugangscode nötig, kein HTTPS im lokalen Netz.
Kiosk und Server verweigern den Start als Root. Ausführliche Hinweise: "man dw-infoscreen".
EOF
}

say()  { printf '%s\n' "$*"; }
err()  { printf 'Fehler: %s\n' "$*" >&2; }
die()  { err "$*"; exit 1; }
usage_fehler() { err "$*"; printf 'Hilfe: %s --hilfe\n' "$PROG" >&2; exit 2; }
hat() { command -v "$1" >/dev/null 2>&1; }

[ -n "${HOME:-}" ] || die "HOME ist nicht gesetzt."
DATA="${XDG_DATA_HOME:-$HOME/.local/share}/dw-infoscreen"
CONF="${XDG_CONFIG_HOME:-$HOME/.config}/dw-infoscreen"
CONFBASE="${XDG_CONFIG_HOME:-$HOME/.config}"
UNIT="$CONFBASE/systemd/user/dw-infoscreen.service"
AUTOSTART="$CONFBASE/autostart/dw-infoscreen-kiosk.desktop"

find_server() {
  local d c
  d="$(dirname -- "$SELF")"
  for c in "${DW_INFOSCREEN_SERVER:-}" "$d/dw-infoscreen-server.py" "$d/../share/dw-infoscreen/dw-infoscreen-server.py" \
           /usr/share/dw-infoscreen/dw-infoscreen-server.py /usr/local/share/dw-infoscreen/dw-infoscreen-server.py \
           "${XDG_DATA_HOME:-$HOME/.local/share}/dw-infoscreen/dw-infoscreen-server.py"; do
    [ -n "$c" ] && [ -f "$c" ] && { SERVER="$c"; return 0; }
  done
  return 1
}
SERVER=""
brauche_python() {
  hat python3 || die "Python 3 fehlt. Installation z. B.: sudo apt install python3"
  python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)' 2>/dev/null || die "Python 3.8 oder neuer wird benötigt."
  find_server || die "Die Datei dw-infoscreen-server.py wurde nicht gefunden (Installation unvollständig?)."
}
py() { python3 "$SERVER" "$@"; }

nicht_root() { [ "$(id -u)" -ne 0 ] || die "$1 soll nicht als Root laufen. Bitte unter dem normalen Benutzerkonto starten (Kiosk-Benutzer ohne Rechte)."; }

# Rückfrage: nur mit Terminal oder --ja
frage_ja() { # TEXT
  local a=""
  [ "$JA" -eq 1 ] && return 0
  if [ ! -t 0 ]; then return 1; fi
  printf '%s [j/N]: ' "$1" >&2
  IFS= read -r a || return 1
  case "$a" in j|J|ja|Ja|JA) return 0;; *) return 1;; esac
}

# Pfade für systemd/.desktop müssen einfach sein
pfad_einfach() { case "$1" in *[!A-Za-z0-9/._+-]*) return 1;; *) return 0;; esac; }

port_pruefen() { [[ "$PORT" =~ ^[0-9]+$ ]] && [ "$PORT" -ge 1 ] && [ "$PORT" -le 65535 ]; }

# ---------------------------------------------------------------- installieren
cmd_installieren() {
  local code_da=0 tafel_da=0 dienst=0 auto=0 neu_code=""
  [ -f "$CONF/code" ] && code_da=1
  [ -f "$DATA/tafel.json" ] && tafel_da=1
  say "=== Plan ==="
  say "Datenordner:    $DATA"
  say "Konfiguration:  $CONF (nur für Sie lesbar)"
  if [ "$code_da" -eq 1 ]; then say "Zugangscode:    vorhanden, bleibt unverändert"; else say "Zugangscode:    wird erzeugt ($CONF/code, Modus 600)"; fi
  if [ "$tafel_da" -eq 1 ]; then say "Tafel:          vorhanden, bleibt unverändert"; else say "Tafel:          Beispieltafel wird angelegt ($DATA/tafel.json)"; fi
  say "Anzeige-Seite: $DATA/anzeige.html wird (neu) erzeugt"
  [ "$OPT_DIENST" -eq 1 ] && say "Dienst:         Benutzerdienst $UNIT (systemd --user) wird eingerichtet"
  [ "$OPT_AUTOSTART" -eq 1 ] && say "Autostart:      $AUTOSTART (Kiosk-Anzeige beim Anmelden)"
  if [ "$AUSFUEHREN" -ne 1 ]; then
    say ""
    say "PROBELAUF: Es wurde nichts angelegt oder geändert."
    say "Zum Ausführen:  $PROG installieren --ausfuehren [--dienst] [--autostart]"
    return 0
  fi
  nicht_root "Die Einrichtung"
  brauche_python
  mkdir -p "$DATA" || die "Ordner nicht anlegbar: $DATA"
  mkdir -p "$CONF" || die "Ordner nicht anlegbar: $CONF"
  chmod 700 "$CONF" || die "Rechte für $CONF nicht setzbar."
  neu_code="$(py code --konfig "$CONF" 2>/dev/null)" || die "Zugangscode konnte nicht angelegt werden."
  py beispiel --daten "$DATA" >/dev/null || die "Beispieltafel konnte nicht angelegt werden."
  py render --daten "$DATA" || die "Anzeige-Seite konnte nicht erzeugt werden."
  say ""
  say "Fertig. Daten: $DATA"
  say "Zugangscode (für den Editor im Browser): $neu_code"
  say "  Später wieder anzeigen:  $PROG code"
  # Dienst
  if [ "$OPT_DIENST" -eq 0 ] && [ "$JA" -eq 0 ] && [ -t 0 ]; then
    frage_ja "Soll der Editor als Benutzerdienst bei der Anmeldung automatisch starten?" && OPT_DIENST=1
  fi
  if [ "$OPT_AUTOSTART" -eq 0 ] && [ "$JA" -eq 0 ] && [ -t 0 ]; then
    frage_ja "Soll die Kiosk-Anzeige beim Anmelden automatisch starten (Anzeigegerät)?" && OPT_AUTOSTART=1
  fi
  local rc=0
  [ "$OPT_DIENST" -eq 1 ] && dienst=1
  [ "$OPT_AUTOSTART" -eq 1 ] && auto=1
  if [ "$dienst" -eq 1 ]; then einrichten_dienst || rc=1; fi
  if [ "$auto" -eq 1 ]; then einrichten_autostart || rc=1; fi
  if [ "$dienst" -eq 0 ] && [ "$auto" -eq 0 ]; then
    say ""
    say "Dienst und Autostart wurden nicht eingerichtet. Starten Sie von Hand:"
    say "  $PROG server      (Editor)"
    say "  $PROG kiosk       (Anzeige)"
    say "oder später:  $PROG installieren --ausfuehren --dienst --autostart"
  fi
  return "$rc"
}

einrichten_dienst() {
  pfad_einfach "$SELF" || { err "Dienst nicht eingerichtet: Der Programmpfad enthält Sonderzeichen oder Leerzeichen ($SELF)."; return 1; }
  if [ -e "$UNIT" ] && ! grep -q "$MARK" "$UNIT" 2>/dev/null; then err "Dienst nicht eingerichtet: Es gibt schon eine fremde Datei $UNIT."; return 1; fi
  mkdir -p "$(dirname "$UNIT")" || return 1
  cat >"$UNIT" <<EOF
# $MARK: von DW InfoScreen angelegt; "$PROG deinstallieren --ausfuehren" entfernt sie wieder
[Unit]
Description=DW InfoScreen Editor (lokaler Tafel-Editor, nur lokales Netz)

[Service]
ExecStart=$SELF server --port $PORT --adresse $ADRESSE
Restart=on-failure
RestartSec=5
NoNewPrivileges=yes
PrivateTmp=yes

[Install]
WantedBy=default.target
EOF
  say "Dienstdatei angelegt: $UNIT"
  if hat systemctl; then
    if systemctl --user daemon-reload 2>/dev/null && systemctl --user enable --now dw-infoscreen.service 2>/dev/null; then
      say "Der Editor-Dienst ist aktiviert und gestartet."
    else
      err "systemctl --user konnte den Dienst nicht aktivieren (keine Benutzersitzung?). Später nachholen:  systemctl --user enable --now dw-infoscreen.service"
    fi
  else
    say "systemctl nicht gefunden: Dienstdatei liegt bereit, wurde aber nicht aktiviert."
  fi
}

einrichten_autostart() {
  pfad_einfach "$SELF" || { err "Autostart nicht eingerichtet: Der Programmpfad enthält Sonderzeichen oder Leerzeichen ($SELF)."; return 1; }
  if [ -e "$AUTOSTART" ] && ! grep -q "$MARK" "$AUTOSTART" 2>/dev/null; then err "Autostart nicht eingerichtet: Es gibt schon eine fremde Datei $AUTOSTART."; return 1; fi
  mkdir -p "$(dirname "$AUTOSTART")" || return 1
  cat >"$AUTOSTART" <<EOF
[Desktop Entry]
# $MARK: von DW InfoScreen angelegt; "$PROG deinstallieren --ausfuehren" entfernt sie wieder
Type=Application
Name=DW InfoScreen Anzeige
Comment=Zeigt die Tafel im Vollbild
Exec=$SELF kiosk
Terminal=false
X-GNOME-Autostart-enabled=true
EOF
  say "Autostart-Datei angelegt: $AUTOSTART"
}

# ---------------------------------------------------------------- server
cmd_server() {
  nicht_root "Der Editor-Server"
  port_pruefen || usage_fehler "--port: Zahl von 1 bis 65535 erwartet."
  brauche_python
  [ -f "$CONF/code" ] || die "Es gibt noch keinen Zugangscode. Bitte zuerst:  $PROG installieren --ausfuehren"
  exec python3 "$SERVER" serve --daten "$DATA" --konfig "$CONF" --port "$PORT" --adresse "$ADRESSE"
}

# ---------------------------------------------------------------- code
cmd_code() {
  brauche_python
  if [ "$OPT_NEU" -eq 1 ]; then
    [ -f "$CONF/code" ] || die "Es gibt noch keinen Zugangscode. Bitte zuerst:  $PROG installieren --ausfuehren"
    frage_ja "Neuen Zugangscode erzeugen? Der alte Code und alle angemeldeten Browser werden ungültig." || die "Abbruch, der Zugangscode blieb unverändert. (Ohne Terminal: --ja angeben.)"
    py code --konfig "$CONF" --neu
  else
    [ -f "$CONF/code" ] || die "Es gibt noch keinen Zugangscode. Bitte zuerst:  $PROG installieren --ausfuehren"
    py code --konfig "$CONF"
  fi
}

# ---------------------------------------------------------------- anzeige / kiosk
anzeige_pfad() {
  [ -f "$DATA/tafel.json" ] || die "Es gibt noch keine Tafel. Bitte zuerst:  $PROG installieren --ausfuehren"
  if [ ! -f "$DATA/anzeige.html" ]; then brauche_python; py render --daten "$DATA" || die "Anzeige-Seite konnte nicht erzeugt werden."; fi
  printf '%s' "$DATA/anzeige.html"
}

cmd_anzeige() { anzeige_pfad; printf '\n'; }

finde_chromium() {
  local c
  for c in chromium chromium-browser; do hat "$c" && { CHROMIUM="$c"; return 0; }; done
  return 1
}

cmd_kiosk() {
  local anz uri chrom_dir
  brauche_python
  anz="$(anzeige_pfad)" || exit 1
  uri="$(py uri "$anz")" || die "Adresse der Anzeige konnte nicht gebildet werden."
  chrom_dir="${XDG_CACHE_HOME:-$HOME/.cache}/dw-infoscreen/chromium"
  local -a args=(--kiosk --noerrdialogs --disable-infobars --disable-session-crashed-bubble --no-first-run --incognito
                 --disable-translate --overscroll-history-navigation=0 "--user-data-dir=$chrom_dir" "$uri")
  if [ "$OPT_SKRIPT" -eq 1 ]; then
    say "#!/usr/bin/env bash"
    say "# Startskript der Kiosk-Anzeige (von DW InfoScreen erzeugt). Chromium wird nach einem Absturz nach 5 Sekunden neu gestartet."
    say "BROWSER=\"\$(command -v chromium || command -v chromium-browser)\" || { echo 'Chromium nicht gefunden' >&2; exit 1; }"
    printf 'ARGS=('; printf ' %q' "${args[@]}"; printf ' )\n'
    # shellcheck disable=SC2016
    say 'while :; do "$BROWSER" "${ARGS[@]}"; sleep 5; done'
    return 0
  fi
  nicht_root "Die Kiosk-Anzeige"
  [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] || die "Keine grafische Sitzung gefunden (DISPLAY/WAYLAND_DISPLAY leer). Die Kiosk-Anzeige läuft nur auf dem Desktop des Anzeigegeräts."
  finde_chromium || die "Chromium fehlt. Installation z. B.: sudo apt install chromium (oder chromium-browser)"
  mkdir -p "$chrom_dir" || die "Ordner nicht anlegbar: $chrom_dir"
  say "Kiosk-Anzeige startet ($CHROMIUM). Beenden: Strg+C im Terminal, oder Chromium mit Alt+F4 schließen."
  if [ "$OPT_EINMAL" -eq 1 ]; then "$CHROMIUM" "${args[@]}"; exit $?; fi
  trap 'exit 0' INT TERM
  while :; do
    "$CHROMIUM" "${args[@]}"
    say "Chromium wurde beendet (Code $?). Neustart in 5 Sekunden ... (Strg+C beendet)"
    sleep 5
  done
}

# ---------------------------------------------------------------- export / import
cmd_export() {
  local ziel="$1" n=1 f
  [ -n "$ziel" ] || usage_fehler "export braucht einen Ordner: $PROG export /pfad/zum/usb-stick"
  [ -f "$DATA/tafel.json" ] || die "Es gibt noch keine Tafel zum Sichern."
  [ -d "$ziel" ] || die "Ordner nicht gefunden: $ziel"
  [ -w "$ziel" ] || die "Ordner nicht beschreibbar: $ziel"
  f="$ziel/dw-infoscreen-tafel.json"
  while [ -e "$f" ] || [ -L "$f" ]; do n=$((n + 1)); f="$ziel/dw-infoscreen-tafel-$n.json"; [ "$n" -gt 999 ] && die "Kein freier Dateiname in $ziel."; done
  cp -- "$DATA/tafel.json" "$f" || die "Kopieren fehlgeschlagen."
  say "Tafel gesichert: $f"
}

cmd_import() {
  local quelle="$1" datei="" kand="" bak info
  [ -n "$quelle" ] || usage_fehler "import braucht einen Ordner oder eine Datei: $PROG import /pfad/zum/usb-stick"
  if [ -f "$quelle" ]; then datei="$quelle"
  elif [ -d "$quelle" ]; then
    local k
    for k in "$quelle"/dw-infoscreen-tafel*.json; do
      [ -f "$k" ] || continue
      if [ -z "$kand" ] || [ "$k" -nt "$kand" ]; then kand="$k"; fi
    done
    [ -n "$kand" ] || die "In $quelle liegt keine Datei dw-infoscreen-tafel*.json."
    datei="$kand"
  else die "Nicht gefunden: $quelle"; fi
  brauche_python
  info="$(py pruefen "$datei")" || exit 1
  say "Gefunden: $datei"
  say "Inhalt:   $info"
  if [ "$AUSFUEHREN" -ne 1 ]; then
    say ""
    say "VORSCHAU: Es wurde nichts geändert. Zum Übernehmen:  $PROG import $(printf '%q' "$quelle") --ausfuehren"
    say "(Die bisherige Tafel wird vorher als tafel.json.sicherung-ZEITSTEMPEL im Datenordner gesichert.)"
    return 0
  fi
  mkdir -p "$DATA" || die "Ordner nicht anlegbar: $DATA"
  if [ -f "$DATA/tafel.json" ]; then
    bak="$DATA/tafel.json.sicherung-$(date +%Y%m%d-%H%M%S)"
    cp -- "$DATA/tafel.json" "$bak" || die "Sicherung der bisherigen Tafel fehlgeschlagen. Abbruch."
    say "Bisherige Tafel gesichert: $bak"
  fi
  py pruefen "$datei" --daten "$DATA" --uebernehmen >/dev/null || die "Übernahme fehlgeschlagen."
  say "Tafel übernommen, Anzeige-Seite neu erzeugt."
}

# ---------------------------------------------------------------- status
port_offen() { (exec 3<>"/dev/tcp/127.0.0.1/$PORT") 2>/dev/null; }

cmd_status() {
  say "DW InfoScreen $VERSION"
  say "Datenordner:     $DATA"
  if [ -f "$DATA/tafel.json" ]; then say "Tafel:           vorhanden"; else say "Tafel:           fehlt (installieren --ausfuehren)"; fi
  if [ -f "$DATA/anzeige.html" ]; then say "Anzeige-Seite:   vorhanden ($DATA/anzeige.html)"; else say "Anzeige-Seite:   fehlt"; fi
  if [ -f "$CONF/code" ]; then say "Zugangscode:     vorhanden"; else say "Zugangscode:     fehlt (installieren --ausfuehren)"; fi
  if [ -f "$UNIT" ]; then
    local st="unbekannt"
    hat systemctl && st="$(systemctl --user is-active dw-infoscreen.service 2>/dev/null)"; [ -n "$st" ] || st="unbekannt"
    say "Dienst:          eingerichtet ($st)"
  else say "Dienst:          nicht eingerichtet"; fi
  if [ -f "$AUTOSTART" ]; then say "Autostart:       eingerichtet (Kiosk beim Anmelden)"; else say "Autostart:       nicht eingerichtet"; fi
  if port_pruefen && port_offen; then say "Server:          läuft (Port $PORT antwortet auf diesem Gerät)"; else say "Server:          läuft nicht (Port $PORT)"; fi
  if finde_chromium; then say "Chromium:        gefunden ($CHROMIUM)"; else say "Chromium:        nicht gefunden (nur für die Kiosk-Anzeige nötig)"; fi
}

# ---------------------------------------------------------------- deinstallieren
cmd_deinstallieren() {
  local gef=0
  say "=== Plan ==="
  if [ -f "$UNIT" ]; then
    if grep -q "$MARK" "$UNIT" 2>/dev/null; then say "Dienst entfernen:     $UNIT (vorher: systemctl --user disable --now)"; gef=1
    else say "Übersprungen (fremde Datei): $UNIT"; fi
  else say "Dienst:               nicht eingerichtet"; fi
  if [ -f "$AUTOSTART" ]; then
    if grep -q "$MARK" "$AUTOSTART" 2>/dev/null; then say "Autostart entfernen:  $AUTOSTART"; gef=1
    else say "Übersprungen (fremde Datei): $AUTOSTART"; fi
  else say "Autostart:            nicht eingerichtet"; fi
  say "Daten ($DATA) und Zugangscode ($CONF) bleiben erhalten; löschen Sie sie bei Bedarf selbst."
  if [ "$AUSFUEHREN" -ne 1 ]; then
    say ""; say "VORSCHAU: Es wurde nichts entfernt. Zum Entfernen:  $PROG deinstallieren --ausfuehren"
    return 0
  fi
  if [ -f "$UNIT" ] && grep -q "$MARK" "$UNIT" 2>/dev/null; then
    hat systemctl && systemctl --user disable --now dw-infoscreen.service >/dev/null 2>&1
    rm -f -- "$UNIT" && say "Dienstdatei entfernt."
    hat systemctl && systemctl --user daemon-reload >/dev/null 2>&1
  fi
  if [ -f "$AUTOSTART" ] && grep -q "$MARK" "$AUTOSTART" 2>/dev/null; then rm -f -- "$AUTOSTART" && say "Autostart-Datei entfernt."; fi
  [ "$gef" -eq 1 ] || say "Es war nichts zu entfernen."
}

# ---------------------------------------------------------------- Start
main() {
  local cmd="" arg=""
  if [ $# -gt 0 ]; then
    case "$1" in -*) ;; *) cmd="$1"; shift;; esac
  fi
  while [ $# -gt 0 ]; do
    case "$1" in
      --ausfuehren) AUSFUEHREN=1;;
      --ja) JA=1;;
      --dienst) OPT_DIENST=1;;
      --autostart) OPT_AUTOSTART=1;;
      --neu) OPT_NEU=1;;
      --einmal) OPT_EINMAL=1;;
      --als-skript) OPT_SKRIPT=1;;
      --port) [ $# -ge 2 ] || usage_fehler "--port braucht einen Wert."; PORT="$2"; shift;;
      --adresse) [ $# -ge 2 ] || usage_fehler "--adresse braucht einen Wert."; ADRESSE="$2"; shift;;
      --version) say "DW InfoScreen $VERSION"; exit 0;;
      -h|--help|--hilfe) usage; exit 0;;
      -*) usage_fehler "Unbekannte Option: $1";;
      *) [ -z "$arg" ] || usage_fehler "Zu viele Angaben: $1"; arg="$1";;
    esac
    shift
  done
  [[ "$ADRESSE" =~ ^[0-9A-Fa-f.:]+$ ]] || usage_fehler "--adresse: IP-Adresse erwartet (zum Beispiel 127.0.0.1)."
  case "$cmd" in
    "") usage;;
    hilfe|help) usage;;
    installieren) port_pruefen || usage_fehler "--port: Zahl von 1 bis 65535 erwartet."; cmd_installieren;;
    server) cmd_server;;
    code) cmd_code;;
    anzeige) cmd_anzeige;;
    kiosk) cmd_kiosk;;
    export) cmd_export "$arg";;
    import) cmd_import "$arg";;
    status) cmd_status;;
    deinstallieren) cmd_deinstallieren;;
    *) usage_fehler "Unbekannter Befehl: $cmd";;
  esac
}
main "$@"
