#!/bin/bash
# wrenctl: written by Wren Linux parts/wren-menu.sh
# One command for Wren Linux. Without an option it opens the menu on a sway
# screen and prints the help anywhere else. man wrenctl has the details.
set -u
# Where Wren Linux is: WREN_DIR if you set it; else the root-owned copy the
# Wren Linux package installs, which no program running as you can change; else
# your own copy, ~/wren
if [ -n "${WREN_DIR:-}" ]; then
    DIR=$WREN_DIR
elif [ -f "${WREN_SYSTEM_DIR:-/usr/share/wren}/lib.sh" ]; then
    DIR=${WREN_SYSTEM_DIR:-/usr/share/wren}
else
    DIR=$HOME/wren
fi

usage() {
    cat <<'TEXT'
usage: wrenctl [OPTION]

  (no option)  open the menu: a window in sway, or in this terminal on a
               text console or over ssh; this help when there is no terminal
  --menu       open the menu: browse, chat, files, More apps, Wi-Fi, Privacy, Settings, Help
  --update     bring Wren Linux up to date, then rerun the scripts whose files
               changed (more: --update --since COMMIT, --update --all)
  --apps       pick optional programs to install (--apps --list lists them)
  --install NAME        install the extra NAME, or repair or update it (Tab
                        completes NAME); options after NAME go to its script
  --remove NAME         remove the extra NAME (your own data stays)
  --manual NAME         print the steps to set up the extra NAME by hand
  --list       the extras: what each is, and whether it is installed
  --part NAME           run one part of the base desktop again (repairs it)
  --status     show what Wren Linux has set up on this computer
  --git [DIR]  set git up for you: your name and email, and pushing to GitHub
               over ssh with a key of your own, for DIR or the repository you
               are in (git-helper.sh)
  --test       check that the scripts, packages, downloads and keys still work
  --wifi [on|off]       switch Wi-Fi on or off (no word: say whether it is on)
  --bluetooth [on|off]  the same for Bluetooth
  --mic [on|off]        the same for the microphone (muted at every login)
  --camera [on|off]     the same for the webcam (with the webcam extra; off at every boot)
  --welcome    show the key cheat sheet again (the first-login screen)
  --learn      learn bash by reading Wren Linux's scripts, smallest first, checking each off
  --version    which version of Wren Linux this is
  --dir        where the Wren Linux folder is
  --help       this help

Each works without its dashes too: wrenctl update, wrenctl install vim.
More: man wrenctl, and README.md in the Wren Linux folder.
TEXT
}

die() { printf 'wrenctl: %s\n' "$*" >&2; exit 1; }

# need_dir: the commands that run a script of Wren Linux's need its folder
need_dir() {
    [ -f "$DIR/lib.sh" ] ||
        die "can't find the Wren Linux folder ($DIR). Set WREN_DIR to where it is, or get it again: git clone <address> ~/wren"
}

# script_for extras|parts NAME: the path of that script, only if NAME is
# exactly the name of one (lowercase letters, digits and dashes): nothing
# but a script of Wren Linux's own is ever run
script_for() {
    local kind=extra list="wrenctl --list shows the extras."
    [ "$1" = extras ] || kind=part list="The parts are in $DIR/parts."
    [ -n "${2:-}" ] || die "which $kind? $list"
    if [[ ! $2 =~ ^[a-z0-9][a-z0-9-]*$ ]] || [ ! -f "$DIR/$1/$2.sh" ]; then
        die "there is no $kind called \"$2\". $list"
    fi
    echo "$DIR/$1/$2.sh"
}

MENU=$HOME/.local/bin/wren-menu
# in_sway: there is a screen to open a window on
in_sway() { [ -n "${WAYLAND_DISPLAY:-}" ] && [ -x "$MENU" ]; }
# in_terminal: no sway, but a terminal to show the menu in (a text console, ssh)
in_terminal() { [ -t 0 ] && [ -t 1 ] && [ -x "$MENU" ]; }

cmd=${1:-}
[ $# -eq 0 ] || shift

# watched SCRIPT ARGS...: runs a Wren Linux script, then takes the startup files
# its run changed as Wren Linux's (parts/startup-watch.sh), so they don't show
# as "startup changed". A file that had changed before the run is left out:
# that change is for you to look at (wren-watch). Exits with the script's status.
WATCH=${WREN_WATCH_CMD:-/usr/local/bin/wren-watch}
watched() {
    local rc=0 before after list pending f
    if [ ! -x "$WATCH" ]; then bash "$@"; exit; fi
    before=$(mktemp) after=$(mktemp)
    "$WATCH" manifest >"$before" 2>/dev/null || true
    pending=$(cat "${WREN_WATCH_RUN:-/run/wren-watch}/$(id -un)" 2>/dev/null || true)
    bash "$@" || rc=$?
    "$WATCH" manifest >"$after" 2>/dev/null || true
    list=$({ diff <(sort -k5 "$before") <(sort -k5 "$after") || true; } | sed -n 's/^[<>] [^ ]* [^ ]* [^ ]* [^ ]* //p' | sort -u)
    rm -f "$before" "$after"
    local take=()
    while IFS= read -r f; do
        [ -n "$f" ] || continue
        if grep -qxF -- "$f" <<<"$pending"; then
            printf 'wrenctl: ~/%s had changed before this run: not taken as Wren Linux'"'"'s (look: wren-watch)\n' "$f" >&2
        else
            take+=("$f")
        fi
    done <<<"$list"
    if [ ${#take[@]} -gt 0 ]; then
        printf 'wrenctl: taking what this changed in your startup files as Wren Linux'"'"'s (sudo):\n' >&2
        printf '  ~/%s\n' "${take[@]}" >&2
        sudo "$WATCH" accept "${take[@]}" >/dev/null || printf 'wrenctl: not taken; they show as "startup changed" until: sudo wren-watch accept\n' >&2
    fi
    exit "$rc"
}

case $cmd in
    "")
        if in_sway; then exec "$MENU"; fi
        if in_terminal; then exec "$MENU" --tui; fi
        usage ;;
    --menu | menu)
        if in_sway; then exec "$MENU"; fi
        in_terminal || die "the menu needs your sway session or a terminal (and the Wren menu installed: bash $DIR/parts/wren-menu.sh)."
        exec "$MENU" --tui ;;
    --update | update) need_dir; watched "$DIR/update.sh" "$@" ;;
    --apps | apps) need_dir; watched "$DIR/install-extras.sh" "$@" ;;
    --status | status) need_dir; exec bash "$DIR/status.sh" "$@" ;;
    --install | install | --remove | remove | --manual | manual)
        # one name; anything after it goes to the script (--account NAME...)
        need_dir
        script=$(script_for extras "${1:-}") || exit 1
        shift
        case $cmd in
            *install)
                ! grep -q '^# retired: ' "$script" ||
                    die "Wren Linux retired $(basename "$script" .sh): $(sed -n 's/^# retired: //p' "$script" | head -1) (wrenctl remove $(basename "$script" .sh) takes it out.)"
                watched "$script" "$@" ;;
            *manual) exec bash "$script" --manual "$@" ;;
            *) watched "$script" "--${cmd#--}" "$@" ;;
        esac ;;
    --list | list) need_dir; exec bash "$DIR/install-extras.sh" --list ;;
    --part | part)
        need_dir
        [ $# -le 1 ] || die "one name at a time (wrenctl part NAME)."
        script=$(script_for parts "${1:-}") || exit 1
        watched "$script" ;;
    --git | git)
        need_dir
        [ $# -le 1 ] || die "one folder at a time (wrenctl git [DIR])."
        # no folder named: the repository you are in, if you are in one
        if [ $# -eq 0 ] && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then set -- "$PWD"; fi
        exec bash "$DIR/git-helper.sh" "$@" ;;
    --test | test) need_dir; cd "$DIR" && exec bash test.sh "$@" ;;
    --wifi | --bluetooth | --mic | --camera)
        [ -x "$HOME/.local/bin/radios" ] || die "the radio switch isn't installed (bash $DIR/parts/wren-menu.sh)."
        exec "$HOME/.local/bin/radios" "${cmd#--}" "$@" ;;
    --learn | learn)
        [ -x "$HOME/.local/bin/wren-learn" ] || die "wren-learn isn't installed (bash $DIR/parts/wren-menu.sh)."
        exec "$HOME/.local/bin/wren-learn" "$@" ;;
    --welcome | welcome)
        # the same cheat sheet the first login opens, in this terminal
        if [ -x "$HOME/.local/bin/keys" ]; then exec "$HOME/.local/bin/keys"; fi
        die "the key cheat sheet isn't installed (bash $DIR/install-base.sh)." ;;
    --version | version)
        need_dir
        git -C "$DIR" describe --always --dirty 2>/dev/null || echo unknown ;;
    --dir) echo "$DIR" ;;
    --help | -h | help) usage ;;
    *)
        printf 'wrenctl: unknown option "%s"\n\n' "$cmd" >&2
        usage >&2
        exit 2 ;;
esac
