#!/bin/sh
# Install Capturefab 0.5.0 for the current user.
#
# Generated by scripts/site-release.py from web/releases.json; do not edit
# web/install.sh by hand. The release files and their SHA-256 checksums are
# written into this script, so it downloads from the official release, checks
# the checksum before extracting or mounting anything, and never runs sudo.
# macOS releases from 0.5.0 are a disk image (.dmg); earlier ones are a .zip.
#
#   curl -fsSL https://capturefab.com/install.sh | sh
#   curl -fsSLO https://capturefab.com/install.sh && less install.sh && sh install.sh --dry-run
#
# Options (after `sh -s --` when piping):
#   --prefix DIR    where the app goes: ~/Applications on macOS,
#                   ${XDG_DATA_HOME:-~/.local/share}/capturefab on Linux
#   --bin-dir DIR   where the capturefab command goes (default ~/.local/bin)
#   --dry-run       print the selected file, URL and checksum, then exit
set -eu

VERSION='0.5.0'
SITE='https://capturefab.com/'

# Sets name, url and expected for an operating system and processor.
asset() {
  case "$1/$2" in
    macos/x86_64) name='capturefab-0.5.0-macos-x86_64.dmg' url='https://capturefab.com/download/v0.5.0/capturefab-0.5.0-macos-x86_64.dmg' expected='5894e9ef68245dede3e7dcc2732f26610c272e6ea308623f399d9d9ccb095243' ;;
    macos/aarch64) name='capturefab-0.5.0-macos-aarch64.dmg' url='https://capturefab.com/download/v0.5.0/capturefab-0.5.0-macos-aarch64.dmg' expected='85ab45bad3e5b4ebea73f47b93c917d65a7a84ea8adc8875c3916ea2a3a1861e' ;;
    linux/x86_64) name='capturefab-0.5.0-linux-x86_64.tar.gz' url='https://capturefab.com/download/v0.5.0/capturefab-0.5.0-linux-x86_64.tar.gz' expected='fbb7088edaada8de5e107c8a5d5081f716e43e4b49798dcf5982cd571d255511' ;;
    linux/aarch64) name='capturefab-0.5.0-linux-aarch64.tar.gz' url='https://capturefab.com/download/v0.5.0/capturefab-0.5.0-linux-aarch64.tar.gz' expected='24bda40e610bb39de7dc294fe95cdd5678df75f8975231536d67e720016654fc' ;;
    *) return 1 ;;
  esac
}

say() { printf '%s\n' "$*"; }
fail() { printf 'capturefab install: %s\n' "$*" >&2; exit 1; }

prefix=''
bin_dir="${HOME:?HOME is not set}/.local/bin"
dry_run=0
while [ $# -gt 0 ]; do
  case "$1" in
    --prefix) [ $# -ge 2 ] || fail "--prefix needs a directory"; prefix=$2; shift 2 ;;
    --prefix=*) prefix=${1#*=}; shift ;;
    --bin-dir) [ $# -ge 2 ] || fail "--bin-dir needs a directory"; bin_dir=$2; shift 2 ;;
    --bin-dir=*) bin_dir=${1#*=}; shift ;;
    --dry-run) dry_run=1; shift ;;
    -h|--help) say "Usage: sh install.sh [--prefix DIR] [--bin-dir DIR] [--dry-run]"; exit 0 ;;
    *) fail "unknown option: $1 (use --prefix DIR, --bin-dir DIR or --dry-run)" ;;
  esac
done
# The command symlink must name an absolute target: a relative one would
# resolve against the bin directory, not the directory the user ran this in.
case "$prefix" in ''|/*) ;; *) prefix="$PWD/$prefix" ;; esac

[ -n "$VERSION" ] || fail "no published release is available yet; see ${SITE}downloads.html or build from source"

kernel=$(uname -s)
case "$kernel" in
  Darwin) os=macos ;;
  Linux) os=linux ;;
  MINGW*|MSYS*|CYGWIN*) fail "on Windows, run this in PowerShell instead: irm ${SITE}install.ps1 | iex" ;;
  *) fail "no Capturefab build for $kernel; see ${SITE}downloads.html" ;;
esac

machine=$(uname -m)
if [ "$os" = linux ] && command -v dpkg >/dev/null 2>&1; then
  # The installed userspace decides, not the processor: 32-bit Raspberry Pi OS
  # reports aarch64 from uname -m.
  machine=$(dpkg --print-architecture)
fi
if [ "$os" = macos ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = 1 ]; then
  machine=arm64 # A Rosetta shell on Apple silicon.
fi
case "$machine" in
  x86_64|amd64) arch=x86_64 ;;
  aarch64|arm64) arch=aarch64 ;;
  armhf|armel|armv6l|armv7l|i386|i686) fail "there is no build for 32-bit $machine systems; install a 64-bit operating system (Raspberry Pi 3 and later run 64-bit Raspberry Pi OS)" ;;
  *) fail "no Capturefab build for $machine; see ${SITE}downloads.html" ;;
esac

name='' url='' expected=''
asset "$os" "$arch" || fail "Capturefab $VERSION has no published $os $arch build; see ${SITE}downloads.html"
if [ -n "${CAPTUREFAB_INSTALL_BASE_URL:-}" ]; then
  # A mirror or test server; the checksum above still decides.
  url="${CAPTUREFAB_INSTALL_BASE_URL%/}/$name"
fi

if [ "$dry_run" = 1 ]; then
  say "Capturefab $VERSION for $os $arch"
  say "file:   $name"
  say "url:    $url"
  say "sha256: $expected"
  exit 0
fi

work=$(mktemp -d "${TMPDIR:-/tmp}/capturefab-install.XXXXXX")
# A mounted disk image, which must be detached before $work is removed.
mounted=''
cleanup() {
  if [ -n "$mounted" ]; then
    if ! hdiutil detach -quiet "$mounted" >/dev/null 2>&1 && ! hdiutil detach -quiet -force "$mounted" >/dev/null 2>&1; then
      printf 'capturefab install: cannot detach %s; eject it in Finder, then delete %s\n' "$mounted" "$work" >&2
      return
    fi
  fi
  rm -rf "$work"
}
trap cleanup EXIT
trap 'exit 130' INT TERM

say "Downloading $name"
if command -v curl >/dev/null 2>&1; then
  curl -fL --retry 2 --progress-bar -o "$work/$name" "$url" || fail "download failed: $url"
elif command -v wget >/dev/null 2>&1; then
  wget -q -O "$work/$name" "$url" || fail "download failed: $url"
else
  fail "curl or wget is needed to download $url"
fi

if command -v sha256sum >/dev/null 2>&1; then
  actual=$(sha256sum "$work/$name")
elif command -v shasum >/dev/null 2>&1; then
  actual=$(shasum -a 256 "$work/$name")
elif command -v openssl >/dev/null 2>&1; then
  actual=$(openssl dgst -sha256 -r "$work/$name")
else
  fail "sha256sum, shasum or openssl is needed to check the download"
fi
actual=$(printf '%s' "${actual%% *}" | tr 'A-F' 'a-f')
[ "$actual" = "$expected" ] || fail "checksum mismatch for $name: expected $expected, got $actual. Nothing was installed."
say "Checksum OK: $expected"

stem=${name%.zip}
stem=${stem%.tar.gz}
stem=${stem%.dmg}
mkdir "$work/x"
if [ "$os" = macos ]; then
  case "$name" in
    *.dmg)
      mkdir "$work/mnt"
      hdiutil attach -nobrowse -readonly -noautoopen -mountpoint "$work/mnt" "$work/$name" >/dev/null </dev/null || fail "cannot open the disk image $name"
      mounted="$work/mnt"
      [ -d "$mounted/Capturefab.app" ] || fail "$name has no Capturefab.app"
      # ditto keeps the code signature, extended attributes and symlinks.
      ditto "$mounted/Capturefab.app" "$work/x/$stem/Capturefab.app" || fail "cannot copy Capturefab.app from $name"
      hdiutil detach -quiet "$mounted" >/dev/null 2>&1 && mounted=''
      ;;
    *) ditto -x -k "$work/$name" "$work/x" || fail "cannot extract $name" ;;
  esac
  app_dir=${prefix:-$HOME/Applications}
  app="$app_dir/Capturefab.app"
  [ -d "$work/x/$stem/Capturefab.app" ] || fail "$name has no Capturefab.app"
  if [ -e "$app" ] && [ ! -x "$app/Contents/MacOS/capturefab" ]; then
    fail "$app exists but is not a Capturefab app; move it away or use --prefix DIR"
  fi
  mkdir -p "$app_dir"
  rm -rf "$app.previous"
  if [ -e "$app" ]; then mv "$app" "$app.previous"; fi
  mv "$work/x/$stem/Capturefab.app" "$app"
  rm -rf "$app.previous"
  target="$app/Contents/MacOS/capturefab"
  installed=$app
else
  tar -xzf "$work/$name" -C "$work/x" || fail "cannot extract $name"
  base=${prefix:-${XDG_DATA_HOME:-$HOME/.local/share}/capturefab}
  dest="$base/$VERSION"
  [ -x "$work/x/$stem/capturefab" ] || fail "$name has no capturefab executable"
  mkdir -p "$base"
  rm -rf "$dest.previous"
  if [ -e "$dest" ]; then mv "$dest" "$dest.previous"; fi
  mv "$work/x/$stem" "$dest"
  rm -rf "$dest.previous"
  target="$dest/capturefab"
  installed=$dest
fi

mkdir -p "$bin_dir"
ln -sfn "$target" "$bin_dir/capturefab"

if [ "$os" = linux ] && [ -f "$installed/share/applications/capturefab.desktop" ]; then
  # Add a launcher entry. Desktop sessions often lack ~/.local/bin on PATH, so
  # the entry names the command's full path.
  share=${XDG_DATA_HOME:-$HOME/.local/share}
  mkdir -p "$share/applications"
  # Only plain absolute paths go into Exec, so neither sed nor Desktop Entry
  # quoting has to escape anything.
  case $bin_dir in
    /*[!A-Za-z0-9._/~+@\ -]*|[!/]*) exec_path=capturefab
      say "Launcher entry runs plain capturefab: $bin_dir has characters a launcher entry cannot hold safely." ;;
    *) exec_path=$bin_dir/capturefab ;;
  esac
  sed "s|^Exec=capturefab\$|Exec=\"$exec_path\"|" "$installed/share/applications/capturefab.desktop" > "$share/applications/capturefab.desktop"
  for icon in "$installed"/share/icons/hicolor/*/apps/capturefab.*; do
    [ -f "$icon" ] || continue
    size=$(basename "$(dirname "$(dirname "$icon")")")
    mkdir -p "$share/icons/hicolor/$size/apps"
    cp "$icon" "$share/icons/hicolor/$size/apps/"
  done
fi

say "Installed Capturefab $VERSION in $installed"
say "Command: $bin_dir/capturefab -> $target"
"$bin_dir/capturefab" --version || fail "the installed capturefab did not start"
case ":${PATH:-}:" in
  *":$bin_dir:"*) ;;
  *)
    say ""
    say "$bin_dir is not on your PATH. Add it, then open a new terminal:"
    case "${SHELL:-}" in
      */fish) say "  fish_add_path $bin_dir" ;;
      */zsh) say "  echo 'export PATH=\"$bin_dir:\$PATH\"' >> ~/.zshrc" ;;
      *) say "  echo 'export PATH=\"$bin_dir:\$PATH\"' >> ~/.profile" ;;
    esac
    ;;
esac
say ""
say "Next: capturefab doctor"
say "Try:  capturefab --direct --camera sim:0 capture -o 'first-{seq}.png'"
if [ "$os" = linux ]; then
  say "USB3 Vision and GigE Vision cameras can need system settings; capturefab doctor prints each fix."
fi
