#!/bin/sh
# Installs the Caprock desktop app on Linux (x64), for your user, no sudo:
#   curl -fsSL https://caprock.dev/linux.sh | sh
#
# It downloads the AppImage the site's button links to into
# ~/.local/share/caprock, adds Caprock to your applications menu and opens it.
# Where FUSE 2 is missing (Ubuntu 22.04 and newer ship without it), the
# launcher runs the AppImage without FUSE. Under WSL and with the NVIDIA
# driver it turns off WebKitGTK's DMA-BUF renderer, which otherwise leaves a
# white window. Run it again to update. Source: https://github.com/dspv/caprock
#
# CAPROCK_NO_OPEN=1 skips the open; CAPROCK_WSL=1 installs inside WSL;
# NO_COLOR=1 prints plain lines.
#
# Everything lives in main(), called on the last line: when the script arrives
# through a pipe, sh has read all of it before the first command runs.
set -eu

REPO="https://github.com/dspv/caprock"
URL="$REPO/releases/latest/download/Caprock-Linux.AppImage"

# ── Output: colour and a live bar on a terminal, plain lines otherwise ──────
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-dumb}" != "dumb" ]; then
  A=$(printf '\033[38;5;215m'); G=$(printf '\033[32m'); D=$(printf '\033[2m')
  B=$(printf '\033[1m'); R=$(printf '\033[0m'); TTY=1
else
  A=; G=; D=; B=; R=; TTY=
fi
step() { printf '  %s✓%s %-27s %s%s%s\n' "$G" "$R" "$1" "$D" "${2:-}" "$R"; }
note() { printf '  %s!%s %s\n' "$A" "$R" "$1"; }
fail() { [ -n "$TTY" ] && printf '\r\033[K'; printf '\n  %s✗ %s%s\n\n' "$B" "$1" "$R" >&2; exit 1; }
# mb BYTES -> "12.8"
mb() { t=$(( $1 * 10 / 1048576 )); printf '%d.%d' $((t / 10)) $((t % 10)); }
# bar HAVE TOTAL LABEL -> one frame of the progress line
bar() {
  pct=$(( $1 * 100 / $2 )); [ "$pct" -gt 100 ] && pct=100
  fill=$(( pct * 24 / 100 )); cells=""; i=0
  while [ "$i" -lt 24 ]; do
    if [ "$i" -lt "$fill" ]; then cells="${cells}█"; else cells="${cells} "; fi
    i=$((i + 1))
  done
  printf '\r  %s↓%s %-27s %s▕%s▏%s %3d%%  %s%s/%s MB%s\033[K' \
    "$A" "$R" "$3" "$A" "$cells" "$R" "$pct" "$D" "$(mb "$1")" "$(mb "$2")" "$R"
}

# download URL FILE LABEL: curl runs in the background and leaves its exit
# code in FILE.rc; on a terminal the bar is redrawn from the file's growth
# until that code appears. (Polling the pid would not do: a finished child
# stays a zombie until it is waited for, and kill -0 still succeeds on it.)
download() {
  head=$(curl -fsSLI "$1" 2>/dev/null) || head=
  total=$(printf '%s\n' "$head" | tr -d '\r' | awk 'tolower($1)=="content-length:"{n=$2} END{print n+0}')
  rc="$2.rc"; rm -f "$rc" "$rc.tmp"
  (
    trap 'kill "$c" 2>/dev/null; exit 1' TERM
    curl -fsSL -o "$2" "$1" 2> "$2.err" &
    c=$!
    if wait "$c"; then echo 0; else echo $?; fi > "$rc.tmp"
    mv "$rc.tmp" "$rc"
  ) &
  CURL_PID=$!
  while [ ! -f "$rc" ]; do
    if [ -n "$TTY" ]; then
      have=0; [ -f "$2" ] && have=$(wc -c < "$2" | tr -d ' ')
      if [ "${total:-0}" -gt 0 ]; then bar "${have:-0}" "$total" "$3"
      else printf '\r  %s↓%s %-27s %s%s MB%s\033[K' "$A" "$R" "$3" "$D" "$(mb "${have:-0}")" "$R"; fi
    fi
    sleep 0.15
  done
  wait "$CURL_PID" 2>/dev/null || true
  CURL_PID=
  [ -n "$TTY" ] && printf '\r\033[K'
  if [ "$(cat "$rc")" != 0 ]; then
    printf '  %s%s%s\n' "$D" "$(cat "$2.err")" "$R" >&2
    fail "The download failed. Check your connection and run the line again."
  fi
}

main() {
  START=$(date +%s)
  printf '\n  %s▲ caprock%s  %s— installing the desktop app%s\n\n' "$A$B" "$R" "$D" "$R"

  [ "$(uname -s)" = "Linux" ] || fail "This installs the Linux app. macOS and Windows: https://caprock.dev/install/"
  case "$(uname -m)" in
    x86_64 | amd64) ;;
    *) fail "Caprock's Linux app is built for x64 only." ;;
  esac

  WSL=
  if [ -n "${WSL_DISTRO_NAME:-}" ] || grep -qi microsoft /proc/version 2>/dev/null; then WSL=1; fi
  if [ -n "$WSL" ] && [ "${CAPROCK_WSL:-}" != 1 ]; then
    note "You're inside WSL. For the Windows app, run this in PowerShell:"
    printf '\n      %sirm https://caprock.dev/win.ps1 | iex%s\n\n' "$B" "$R"
    printf '  %sTo install the Linux app inside WSL anyway:%s\n' "$D" "$R"
    printf '      curl -fsSL https://caprock.dev/linux.sh | CAPROCK_WSL=1 sh\n\n'
    exit 0
  fi

  DISTRO=Linux
  # shellcheck disable=SC1091
  if [ -r /etc/os-release ]; then DISTRO=$(. /etc/os-release && printf '%s' "${PRETTY_NAME:-Linux}") || DISTRO=Linux; fi
  step "Checked your system" "$DISTRO · x64${WSL:+ · WSL}"

  VERSION=$(curl -fsSI "$REPO/releases/latest" 2>/dev/null | tr -d '\r' | awk 'tolower($1)=="location:"{n=split($2,p,"/v"); print p[n]}' | tail -1) || VERSION=

  SHARE="${XDG_DATA_HOME:-$HOME/.local/share}"
  DIR="$SHARE/caprock"
  APP="$DIR/Caprock.AppImage"
  BIN="$HOME/.local/bin"
  mkdir -p "$DIR" "$BIN" "$SHARE/applications"

  tmp="$(mktemp -d)"
  CURL_PID=
  cleanup() {
    [ -n "${CURL_PID:-}" ] && kill "$CURL_PID" 2>/dev/null
    rm -rf "$tmp"
  }
  trap cleanup EXIT
  trap 'exit 130' INT TERM

  download "$URL" "$tmp/Caprock.AppImage" "Downloading Caprock${VERSION:+ $VERSION}"
  chmod +x "$tmp/Caprock.AppImage"
  mv -f "$tmp/Caprock.AppImage" "$APP"
  step "Downloaded Caprock${VERSION:+ $VERSION}" "$(mb "$(wc -c < "$APP" | tr -d ' ')") MB"

  # A launcher that works with or without FUSE 2, and keeps WebKitGTK off
  # DMA-BUF where that leaves a white window (WSLg, the NVIDIA driver).
  cat > "$BIN/caprock-app" <<LAUNCH
#!/bin/sh
if ! (ldconfig -p 2>/dev/null | grep -q 'libfuse.so.2'); then
  export APPIMAGE_EXTRACT_AND_RUN=1
fi
if [ -n "\${WSL_DISTRO_NAME:-}" ] || [ -e /proc/driver/nvidia/version ] || grep -qi microsoft /proc/version 2>/dev/null; then
  export WEBKIT_DISABLE_DMABUF_RENDERER="\${WEBKIT_DISABLE_DMABUF_RENDERER:-1}"
fi
exec "$APP" "\$@"
LAUNCH
  chmod +x "$BIN/caprock-app"

  # The icon comes out of the AppImage itself; without it the entry still works.
  ICON=
  if (cd "$tmp" && "$APP" --appimage-extract '*.png' >/dev/null 2>&1); then
    png=$(find "$tmp/squashfs-root" -name '*.png' -type f -exec ls -S {} + 2>/dev/null | head -1) || png=
    if [ -n "$png" ]; then cp -f "$png" "$DIR/caprock.png" && ICON="$DIR/caprock.png"; fi
  fi

  cat > "$SHARE/applications/caprock.desktop" <<DESKTOP
[Desktop Entry]
Type=Application
Name=Caprock
Comment=Run your coding agents and see what they are doing
Exec=$BIN/caprock-app
${ICON:+Icon=$ICON
}Terminal=false
Categories=Development;
DESKTOP
  step "Installed" "$APP"
  step "Added to your menu" "run it any time: $BIN/caprock-app"

  if [ -z "${CAPROCK_NO_OPEN:-}" ] && [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ]; then
    nohup "$BIN/caprock-app" >/dev/null 2>&1 &
    step "Opened Caprock"
  fi

  printf '\n  %sDone in %ss.%s %sRun this line again to update.%s\n\n' "$B" "$(( $(date +%s) - START ))" "$R" "$D" "$R"
}

main "$@" </dev/null
