#!/usr/bin/env bash
# AltoSignal -- per-user install for the Linux tarball.
#
# WHAT THIS DOES, exactly, and nothing else:
#   1. copies the bundle to  ~/.local/share/altosignal/
#   2. writes a launcher to  ~/.local/share/applications/com.altosignal.altosignal.desktop
#   3. writes icons to       ~/.local/share/icons/hicolor/<N>x<N>/apps/
#   4. refreshes the desktop and icon caches if the tools exist
#
# ⚠️ PER-USER BY DESIGN, and it needs NO root. This matches the Windows decision
# (a per-user install with no UAC prompt): a practice workstation is a shared
# machine where the person installing is rarely an administrator. A system-wide
# install is the .deb's job, not this script's.
#
# ⚠️ It is RE-RUNNABLE. Re-running over an existing install replaces the program
# files and leaves settings alone -- settings live in
# ~/.local/share/com.altosignal.altosignal/ (note: a DIFFERENT directory, named by
# the Flutter application id) and this script never touches that path.

set -euo pipefail

APP_ID="com.altosignal.altosignal"
BIN_NAME="altosignal"

# Fixed literal destinations. Deliberately not composed from user input: every
# removal in uninstall.sh targets one of these exact paths.
DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
APP_DIR="$DATA_HOME/altosignal"
DESKTOP_DIR="$DATA_HOME/applications"
ICON_ROOT="$DATA_HOME/icons/hicolor"
DESKTOP_FILE="$DESKTOP_DIR/$APP_ID.desktop"

SRC=$(cd "$(dirname "$0")" && pwd)

die()  { printf '\n[FAIL] %s\n' "$*" >&2; exit 1; }
step() { printf '\n== %s\n' "$*"; }
ok()   { printf '   ok  %s\n' "$*"; }

step "0. preconditions"
[ -x "$SRC/bundle/$BIN_NAME" ] || die "not found or not executable: $SRC/bundle/$BIN_NAME
       Run this script from inside the unpacked tarball, not from a copy of the
       script on its own."
[ -d "$SRC/icons" ] || die "not found: $SRC/icons (incomplete tarball)"
[ -f "$SRC/$APP_ID.desktop" ] || die "not found: $SRC/$APP_ID.desktop (incomplete tarball)"
ok "tarball contents look complete"

# The settings directory is NOT this app dir. Say so out loud, because the two
# names are one character apart in a listing and deleting the wrong one loses a
# practice's channel configuration.
SETTINGS_DIR="$DATA_HOME/$APP_ID"
if [ -d "$SETTINGS_DIR" ]; then
  ok "existing settings found at $SETTINGS_DIR -- they will be left untouched"
fi

step "1. program files -> $APP_DIR"
if [ -d "$APP_DIR" ]; then
  # Replace the contents without a recursive delete of a variable path: copy in,
  # then drop only files that the new bundle does not have.
  ok "existing install found -- replacing program files in place"
fi
mkdir -p "$APP_DIR"
cp -a "$SRC/bundle/." "$APP_DIR/"
chmod +x "$APP_DIR/$BIN_NAME"
ok "installed $(find "$APP_DIR" -type f | wc -l) files"

step "2. launcher -> $DESKTOP_FILE"
mkdir -p "$DESKTOP_DIR"
# Substitute the absolute Exec path. A desktop entry with a relative Exec produces
# a launcher that silently does nothing.
sed "s|@EXEC@|$APP_DIR/$BIN_NAME|" "$SRC/$APP_ID.desktop" > "$DESKTOP_FILE"
chmod 644 "$DESKTOP_FILE"
grep -q "^Exec=$APP_DIR/$BIN_NAME$" "$DESKTOP_FILE" \
  || die "Exec= substitution did not take -- refusing to leave a dead launcher behind"
ok "launcher written with Exec=$APP_DIR/$BIN_NAME"

step "3. icons -> $ICON_ROOT"
n=0
for png in "$SRC"/icons/*.png; do
  [ -e "$png" ] || continue
  base=$(basename "$png" .png)          # e.g. "128"
  case "$base" in
    ''|*[!0-9]*) continue ;;            # skip anything not a bare pixel size
  esac
  dest="$ICON_ROOT/${base}x${base}/apps"
  mkdir -p "$dest"
  cp -f "$png" "$dest/$APP_ID.png"
  n=$((n + 1))
done
[ "$n" -gt 0 ] || die "no icons were installed -- the launcher would show a blank tile"
ok "installed $n icon sizes"

step "4. refresh caches"
# Both are best-effort: a missing tool is normal on a minimal desktop, and the
# launcher still works, it may just take a re-login to appear.
if command -v update-desktop-database >/dev/null 2>&1; then
  update-desktop-database "$DESKTOP_DIR" 2>/dev/null && ok "desktop database updated"
else
  ok "update-desktop-database not present (skipped -- harmless)"
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
  gtk-update-icon-cache -f -t "$ICON_ROOT" 2>/dev/null && ok "icon cache updated" \
    || ok "icon cache refresh declined (skipped -- harmless)"
else
  ok "gtk-update-icon-cache not present (skipped -- harmless)"
fi

cat <<EOF

Installed.

  run from a menu   AltoSignal (under Internet / Network)
  run from a shell  $APP_DIR/$BIN_NAME
  settings live in  $SETTINGS_DIR
  uninstall         ./uninstall.sh

⚠️ If the launcher does not appear yet, log out and back in -- some desktops only
   rescan ~/.local/share/applications at session start.
EOF
