#!/bin/sh
#
# This script does not have a stable API.

_gitstatus_install_main() {
  if [ -n "${ZSH_VERSION:-}" ]; then
    emulate -L sh -o no_unset
  else
    set -u
  fi

  local argv1=$1
  shift

  local no_check= no_install= uname_s= uname_m= gitstatus_dir=
  local opt= OPTARG= OPTIND=1

  while getopts ':s:m:d:fnh' opt "$@"; do
    case "$opt" in
      h)
        command cat <<\END
Usage: install [-s KERNEL] [-m ARCH] [-d DIR] [-f|-n] [-- CMD [ARG]...]

If positional arguments are specified, call this on success:

  CMD [ARG]... DAEMON VERSION INSTALLED

DAEMON is path to gitstatusd. VERSION is a glob pattern for the
version this daemon should support; it's supposed to be passed as
-G to gitstatusd. INSTALLED is 1 if gitstatusd has just been
downloaded and 0 otherwise.

Options:

  -s KERNEL  use this instead of lowercase `uname -s`
  -m ARCH    use this instead of lowercase `uname -m`
  -d DIR     use this instead of `dirname "$0"`
  -f         download gitstatusd even if there is one locally
  -n         do not download gitstatusd (fail instead)
END
        return
      ;;
      n)
        if [ -n "$no_install" ]; then
          >&2 echo "[gitstatus] error: duplicate option: -$opt"
          return 1
        fi
        no_install=1
      ;;
      f)
        if [ -n "$no_check" ]; then
          >&2 echo "[gitstatus] error: duplicate option: -$opt"
          return 1
        fi
        no_check=1
      ;;
      d)
        if [ -n "$gitstatus_dir" ]; then
          >&2 echo "[gitstatus] error: duplicate option: -$opt"
          return 1
        fi
        if [ -z "$OPTARG" ]; then
          >&2 echo "[error] incorrect value of -$opt: $OPTARG"
          return 1
        fi
        gitstatus_dir="$OPTARG"
      ;;
      m)
        if [ -n "$uname_m" ]; then
          >&2 echo "[gitstatus] error: duplicate option: -$opt"
          return 1
        fi
        if [ -z "$OPTARG" ]; then
          >&2 echo "[error] incorrect value of -$opt: $OPTARG"
          return 1
        fi
        uname_m="$OPTARG"
      ;;
      s)
        if [ -n "$uname_s" ]; then
          >&2 echo "[gitstatus] error: duplicate option: -$opt"
          return 1
        fi
        if [ -z "$OPTARG" ]; then
          >&2 echo "[error] incorrect value of -$opt: $OPTARG"
          return 1
        fi
        uname_s="$OPTARG"
      ;;
      \?) >&2 echo "[gitstatus] error: invalid option: -$OPTARG"           ; return 1;;
      :)  >&2 echo "[gitstatus] error: missing required argument: -$OPTARG"; return 1;;
      *)  >&2 echo "[gitstatus] internal error: unhandled option: -$opt"   ; return 1;;
    esac
  done

  shift "$((OPTIND - 1))"

  : "${gitstatus_dir:=$argv1}"

  if [ -n "$no_check" -a -n "$no_install" ]; then
    >&2 echo "[gitstatus] error: incompatible options: -f, -n"
    return 1
  fi

  if [ -z "$uname_s" ]; then
    uname_s="$(command uname -s)" || return
    uname_s="$(printf '%s' "$uname_s" | command tr '[A-Z]' '[a-z]')" || return
  fi
  if [ -z "$uname_m" ]; then
    uname_m="$(command uname -m)" || return
    uname_m="$(printf '%s' "$uname_m" | command tr '[A-Z]' '[a-z]')" || return
  fi

  local daemon="${GITSTATUS_DAEMON:-}"
  local cache_dir="${GITSTATUS_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/gitstatus}"

  if [ -z "$no_check" ]; then
    if [ -n "${daemon##/*}" ]; then
      >&2 echo "[gitstatus] error: GITSTATUS_DAEMON is not absolute path: $daemon"
      return 1
    fi
    if [ -z "$daemon" ]; then
      daemon="$gitstatus_dir"/usrbin/gitstatusd
      if [ ! -e "$daemon" ]; then
        daemon="$daemon"-"$uname_s"-"$uname_m"
        if [ ! -e "$daemon" ]; then
          daemon=
        fi
      fi
    fi
    if [ -n "$daemon" ]; then
      local gitstatus_version= libgit2_version=
      if ! . "$gitstatus_dir"/build.info; then
        >&2 echo "[gitstatus] internal error: failed to source build.info"
        return 1
      fi
      if [ -z "$gitstatus_version" ]; then
        >&2 echo "[gitstatus] internal error: empty gitstatus_version in build.info"
        return 1
      fi
      [ $# = 0 ] || "$@" "$daemon" "$gitstatus_version" 0
      return
    fi
  fi

  while IFS= read -r line; do
    line="${line###*}"
    [ -n "$line" ] || continue

    local uname_s_glob= uname_m_glob= file= version=
    eval "$line" || return

    if [ -z "$uname_s_glob" -o -z "$uname_m_glob" -o -z "$file" -o -z "$version" ]; then
      >&2 echo "[gitstatus] internal error: invalid install.info line: $line"
      return 1
    fi

    case "$uname_s" in
      $uname_s_glob) ;;
      *) continue;;
    esac
    case "$uname_m" in
      $uname_m_glob) ;;
      *) continue;;
    esac

    # Found a match. The while loop will terminate during this iteration.

    if [ -z "$no_check" ]; then
      # Check if a suitable gitstatusd already exists.
      local daemon="$cache_dir"/"$file"
      if [ -e "$daemon" ]; then
        [ $# = 0 ] || "$@" "$daemon" "$version" 0
        return
      fi
      daemon="$daemon"-"$uname_s"-"$uname_m"
      if [ -e "$daemon" ]; then
        local gitstatus_version= libgit2_version=
        if ! . "$gitstatus_dir"/build.info; then
          >&2 echo "[gitstatus] internal error: failed to source build.info"
          return 1
        fi
        if [ -z "$gitstatus_version" ]; then
          >&2 echo "[gitstatus] internal error: empty gitstatus_version in build.info"
          return 1
        fi
        [ $# = 0 ] || "$@" "$daemon" "$gitstatus_version" 0
        return
      fi
    fi

    # No suitable gitstatusd exists. Need to download.

    if [ -n "$no_install" ]; then
      >&2 echo "[gitstatus] error: no gitstatusd found and installation is disabled"
      return 1
    fi

    local daemon="$cache_dir"/"$file"

    if [ -n "${cache_dir##/*}" ]; then
      >&2 echo "[gitstatus] error: GITSTATUS_CACHE_DIR is not absolute: $cache_dir"
      return 1
    fi
    [ -d "$cache_dir" ] || mkdir -p -- "$cache_dir" || return

    local url="https://github.com/romkatv/gitstatus/releases/download/$version/$file.tar.gz"
    local archive="$cache_dir"/"$file".tmp.$$.tar.gz
    if command -v curl >/dev/null 2>&1; then
      if ! err="$(command curl -fsSLo "$archive" -- "$url" 2>&1)"; then
        >&2 echo "[gitstatus] error: failed to download gitstatusd: $url"
        >&2 printf "%s" "$err"
        return 1
      fi
    elif command -v wget >/dev/null 2>&1; then
      if ! err="$(command wget -O "$archive" -- "$url" 2>&1)"; then
        >&2 echo "[gitstatus] error: failed to download gitstatusd: $url"
        >&2 printf "%s" "$err"
        return 1
      fi
    else
      >&2 echo "[gitstatus] error: please install curl or wget"
      return 1
    fi

    (
      if [ -n "${ZSH_VERSION:-}" ]; then
        builtin cd -q -- "$cache_dir" || return
      else
        cd -- "$cache_dir" || return
      fi

      local old=
      if [ -e "$daemon" ]; then
        local i=1
        while :; do
          old="$daemon"."$i"
          [ -e "$old" ] || break
          i="$((i+1))"
        done
        command mv -f -- "$daemon" "$old" || return
      fi

      command tar -xzf "$archive"
      local ret=$?
      command rm -f -- "$archive"
      if [ -n "$old" ]; then
        if [ "$ret" = 0 ]; then
          command rm -f -- "$old" 2>/dev/null
        else
          command mv -f -- "$old" "$daemon"
        fi
      fi
      exit "$ret"
    ) || return

    [ $# = 0 ] || "$@" "$daemon" "$version" 1
    return
  done <"$gitstatus_dir"/install.info

  >&2 echo "[gitstatus] error: no gitstatusd found for $uname_s $uname_m"
  return 1
}

if [ -z "${0##*/*}" ]; then
  _gitstatus_install_main "${0%/*}" "$@"
else
  _gitstatus_install_main . "$@"
fi
