#!/usr/bin/env bash

usage() {
  echo "Usage: $( basename $0 ) [-h] [ [-d] [-m arch] [-c channel] | [-i filename] ] [-a folder] [-u] [-U] [-S] [-P] [-R]"
  echo ""
  echo "  This script downloads and installs Jalview in macOS, or installs from a previously downloaded Jalview DMG file."
  echo "  One of -d or -i should be specified, whichever comes last is used."
  echo "  Default behaviour with -d is to download the latest release version of Jalview for your architecture and install"
  echo "  the .app in /Applications with user-space updates enabled."
  echo "  These can be adjusted with the following options."
  echo ""
  echo "  -h           Show help"
  echo "  -d           Download the latest DMG for the channel"
  echo "  -m arch      (with -d) Download specific JVM architecture. Should be one of 'arm64' or 'x86_64' (defaults to \`uname -m\`)"
  echo "  -c channel   (with -d) Download from channel. Should be one of 'release', 'test-release', 'develop' (defaults to 'release')."
  echo "  -i filename  Use filename as DMG image file instead of downloading."
  echo "  -a folder    Install the application .app bundle in folder (default '/Applications')."
  echo "  -v           Verbose output."
  echo "  -y           Assume 'yes' to confirmation."
  echo "  -q           No output other than errors.  Assumes -y."
  echo ""
  echo "  Advanced options:"
  echo ""
  echo "  -u template  Use template for user-space updates path (defaults to '~/Library/Application Support/Jalview-Desktop')."
  echo "               template should contain one of: a leading '~/', '%u' (user's username) or '%h' (user's home path)."
  echo "  -U           Disable user-space updates (updates will be attempted in the installation folder)."
  echo "  -S           Disable all user-space and installation updates."
  echo "  -P           Do not add the jalview command-line path to /etc/paths.d."
  echo "  -R           Do not perform the check for root privileges."
  echo "  -t tmpdir    Use tmpdir for installation files (defaults to /tmp)"
}

# check OS
UOS="$(uname -s)"
if [ "$UOS" != "Darwin" ]; then
  echo "This install script is for macOS."
  echo "Jalview can be installed on other OSes using the installers found at"
  echo "https://www.jalview.org/download/"
  exit 1
fi

# set defaults
DOWNLOAD=0
UARCH=$(uname -m)
CHANNEL="release"
DMGFILE=""
INSTALLERNAME="$(basename $0)"
APPLICATIONFOLDER="/Applications"
VERBOSE=0
YES=0
QUIET=0
USERAPPDIRTEMPLATE=""
DISABLEUSERAPPDIR=0
DISABLEALLUPDATES=0
DISABLEGLOBALPATH=0
DISABLEROOTCHECK=0
USETEMPDIR=""
TMP="/tmp"

CURLOPT=""
RSYNCOPT=""
DITTOOPT=""
HDIUTILOPT="-quiet"

# set options
while getopts "hdi:a:vyqu:c:USPR" opt; do
  case ${opt} in
    h)
      usage
      exit 0
      ;;
    d)
      DOWNLOAD=1
      ;;
    m)
      UARCH="${OPTARG}"
      ;;
    c)
      CHANNEL="${OPTARG}"
      ;;
    i)
      DMGFILE="${OPTARG}"
      DOWNLOAD=0
      ;;
    a)
      APPLICATIONFOLDER="${OPTARG}"
      ;;
    v)
      VERBOSE=1
      CURLOPT="-v"
      RSYNCOPT="-v"
      DITTOOPT="-V"
      HDIUTILOPT="-verbose"
      ;;
    y)
      YES=1
      ;;
    q)
      QUIET=1
      CURLOPT="-s"
      RSYNCOPT="-q"
      HDIUTILOPT="-quiet"
      YES=1
      ;;
    u)
      USERAPPDIRTEMPLATE="${OPTARG}"
      ;;
    U)
      DISABLEUSERAPPDIR=1
      ;;
    S)
      DISABLEALLUPDATES=1
      ;;
    P)
      DISABLEGLOBALPATH=1
      ;;
    R)
      DISABLEROOTCHECK=1
      ;;
    t)
      TMP="${OPTARG}"
      ;;
    *)
      echo "Unrecognised option. Run with -h for help."
      exit 2
      ;;
  esac
done

myecho() {
  if [ "${QUIET}" != 1 ]; then
    echo "* $1"
  fi
}

# no -d or -i arguments
if [ "${DOWNLOAD}" = 0 -a -z "${DMGFILE}" ]; then
  echo "Please use one of -d or -i options"
  echo ""
  usage
  exit 0
fi

# root permissions check
if [ "${DISABLEROOTCHECK}" != 1 -a "${EUID}" != 0 ]; then
  echo "This script should be run with root permissions, or disable this root check with -R."
  exit 5
fi

# check channel
if [ ! -z "$DMGFILE" -a -e "$DMGFILE" ]; then
  myecho "Using DMG file '${DMGFILE}'"
  CHANNEL="dmg"
else
  case "$CHANNEL" in
    release|test-release|develop)
      myecho "Using channel ${CHANNEL}"
      ;;
    *)
      echo "-c channel must be one of 'release', 'test-release' or 'develop'"
      exit 3
      ;;
  esac
fi

# convert uarch to jarch
case $UARCH in
  x86_64)
    JARCH=x64
    ;;
  arm64)
    JARCH=aarch64
    ;;
  *)
    echo "Unknown architecture '$UARCH'. Exiting."
    exit 4;
  ;;
esac

# dir for downloads and volume mount
TEMPDIR=$(mktemp -d -p "${TMP}" -t "${INSTALLERNAME%.sh}_${CHANNEL}")
if [ "${DOWNLOAD}" = 1 ]; then
  myecho "Using directory '${TEMPDIR}' to download and mount disk image"
else
  myecho "Using directory '${TEMPDIR}' to mount disk image"
fi
FILEBASE="${TEMPDIR}/jalview-${CHANNEL}-latest-macos-${JARCH}"
VOLUMEDIR="${FILEBASE}.vol"

# Confirmation of what's about to happen
if [ "${YES}" != 1 ]; then
  if [ "${DOWNLOAD}" = 1 ]; then
    myecho "This script will download Jalview from the '${CHANNEL}' channel and install it into the '${APPLICATIONFOLDER}' folder."
  else
    myecho "This script will install Jalview from the '${DMGFILE}' disk image file into the '${APPLICATIONFOLDER}' folder."
  fi

  read -r -p "Continue? [y/N] " response
  case $(echo "${response}" | tr '[:upper:]' '[:lower:]') in
    yes|y)
      myecho "Excellent! Continuing."
      ;;
    *)
      echo "Aborting due to negative confirmation." && exit
      ;;
  esac
fi

if [ "${DOWNLOAD}" = 1 ]; then
  DMGFILE="${FILEBASE}.dmg"
  SHA256=""

  URL="https://www.jalview.org/downloads/${CHANNEL}/installer/macos-${JARCH}"

  myecho "Downloading '${URL}' to '${DMGFILE}'"
  curl ${CURLOPT} -f -L -o "${DMGFILE}" "${URL}"
  [ $? != 0 ] && echo "Could not download '$URL' to '$DMGFILE'" && exit 6
  SHA256=$( curl ${CURLOPT} -f -s -L "${URL}.sha256" )
  [ $? != 0 ] && echo "Could not download '$URL.sha256'" && exit 7

  CHECK=$( shasum -a 256 "${DMGFILE}" | cut -d" " -f1 )
  if [ "${CHECK}" = "${SHA256}" ]; then
    myecho "Downloaded file '$DMGFILE' checksum matches downloaded checksum '$SHA256'"
  else
    echo "Downloaded file '$DMGFILE' checksum does not match downloaded checksum '$SHA256'"
    exit 8
  fi
fi

# mount the DMG image
myecho "Mounting disk image '${DMGFILE}' on '${VOLUMEDIR}'"
hdiutil attach ${HDIUTILOPT} -mountpoint "${VOLUMEDIR}" "${DMGFILE}"
[ $? != 0 ] && echo "Could not mount '${DMGFILE}' on mount point '${VOLUMEDIR}'" && exit 9

myecho "MOUNTED"

# difficult to use a wildcard for a non-user-readable folder without inserting sudo directly into the command
# i.e. what I really want to do here is
# APP=$( sudo sh -c "ls -1d ${VOLUMEDIR}/Jalview*.app" | head -1 )
# but don't want to put sudo in the script because of the -R option.  We'll loop through instead.
VOLUMEFILES=$( ls -1 ${VOLUMEDIR} )
while IFS= read -r VOLUMEFILE; do
  FILE=$( basename "${VOLUMEFILE}" )
  if [ "$FILE" != "${FILE#Jalview}" -a "$FILE" != "${FILE%.app}" ]; then
    APP="$FILE"
    break
  fi
done <<< "$VOLUMEFILES"
[ -z "$APP" ] && echo "Could not find Jalview\*.app in the volume '${VOLUMEDIR}'" && exit 10

APPNAME=$( basename "$APP" )
myecho "Found application '${APP}'"

# ensure no trailing slash on APPNAME or APPLICATIONFOLDER (important for rsync)
while [ "${APPLICATIONFOLDER}" != "${APPLICATIONFOLDER%/}" -a "${APPLICATIONFOLDER}" != "/" ]; do
  myecho "Removing trailing slash from APPLICATIONFOLDER='${APPLICATIONFOLDER}'"
  APPLICATIONFOLDER="${APPLICATIONFOLDER%/}"
done
while [ "${APPNAME}" != "${APPNAME%/}" ]; do
  myecho "Removing trailing slash from APPNAME='${APPNAME}'"
  APPNAME=${APPNAME%/}
done
[ -z "$APPNAME" -o "${APPNAME%.app}" = "$APPNAME" ] && echo "Could not find suitable Jalview\*.app in the volume '${VOLUMEDIR}'" && exit 11

## rsync .app from mounted volume to application folder
#myecho "Rsyncing '${VOLUMEDIR}/${APPNAME}' to '${APPLICATIONFOLDER}/${APPNAME}'"
#rsync ${RSYNCOPT} -ah --delete "${VOLUMEDIR}/${APPNAME}" "${APPLICATIONFOLDER}/"
#[ $? != 0 ] && echo "Possible problem when rsyncing '${APP}' to '${APPLICATIONFOLDER}'" && exit 12
#myecho "Finished rsync"

# using ditto
APPPATH="${APPLICATIONFOLDER}/${APPNAME}"
OLDAPPPATH="${APPPATH}.old"
myecho "Copying '${VOLUMEDIR}/${APPNAME}' to '${APPPATH}'"
if [ -e "$APPPATH" ]; then
  if [ -e "$OLDAPPPATH" ]; then
    rm -Rf "$OLDAPPPATH"
  fi
  mv "$APPPATH" "$OLDAPPPATH"
fi
ditto ${DITTOOPT} "${VOLUMEDIR}/${APPNAME}" "$APPPATH" && [ -e "$OLDAPPPATH" ] && rm -Rf "$OLDAPPPATH"
myecho "Finished copying '${APPNAME}'"

EXIT=0
declare -a WARNINGS=()
addwarning() {
  local W=$1
  local N=$2
  myecho "${W}"
  WARNINGS=( "${WARNINGS[@]}" "${W}" )
  # exit with the first warning value
  [ "${EXIT}" = 0 ] && EXIT=$N
}

# unmount the DMG image
myecho "Unmounting '${VOLUMEDIR}'"
hdiutil detach ${HDIUTILOPT} "${VOLUMEDIR}" || addwarning "Possible problem when unmounting/deleting '${VOLUMEDIR}'.  I'm continuing the install but you should look at it later." 13

# delete the image file and temp dir
if [ "${DOWNLOAD}" = 1 ]; then
  myecho "Deleting downloaded disk image file '${DMGFILE}'"
  rm "${DMGFILE}"
fi
myecho "Removing temporary directory '${TEMPDIR}'"
rmdir "${TEMPDIR}"

VMOPTIONS="${APPLICATIONFOLDER}/${APPNAME}/Contents/vmoptions.txt"

VMOPTIONS_START=""
if [ -e "${VMOPTIONS}" ]; then
  while IFS= read -r line; do
    VMOPTIONS_START="${VMOPTIONS_START}${line}"$'\n'
    if [ "${line}" != "${line/Jalview/}" ]; then # i.e. $line contains "Jalview"
      break
    fi
  done < "${VMOPTIONS}"
fi

LONGHASH=$( printf %s "${APPLICATIONFOLDER}/${APPNAME}/Contents/Resources/app" | shasum -a 256 -b )
HASH=${LONGHASH:0:8}
NAME=${APPNAME%.app}

myecho "Writing vmoptions files '${VMOPTIONS}'"
VMOPTIONS_COMMENT=$( cat << EOM
# Jalview options added by $( basename $0 ) at $( date +"%F %T")
EOM
)

# -Dnouserdefaultappdir=false
if [ "${DISABLEUSERAPPDIR}" = 1 ]; then
  myecho "- User-space updates DISABLED"
  VMOPTIONS_USERAPPDIR=$( cat << EOM
#
# Comment out the following line to allow user-space updates
-Dnouserdefaultappdir=true
EOM
)
else
  myecho "- User-space updates enabled"
  VMOPTIONS_USERAPPDIR=$( cat << EOM
#
# Uncomment the following line to disable user-space updates
#-Dnouserdefaultappdir=true
EOM
)
fi

# -Dsetuserappdirpath
if [ -z "${USERAPPDIRTEMPLATE}" ]; then
  myecho "- Default user-space updates path (~/Library/Application Support/Jalview-Desktop/${NAME}/${HASH})"
  VMOPTIONS_USERAPPDIRPATH=$( cat << EOM
#
# Uncomment the below line to set a custom path for user-space updates -- use with caution.
# A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.
# If not set, the default is ~/Library/Application Support/Jalview-Desktop for macOS
#-Dsetuserappdirpath=/tmp/jalview/%u
EOM
)
else
  myecho "- CUSTOMISED user-space updates path (${USERAPPDIRTEMPLATE})"
  VMOPTIONS_USERAPPDIRPATH=$( cat << EOM
#
# The below line sets a custom path for user-space updates -- use with caution.
# A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.
# If unset, the default is ~/Library/Application Support/Jalview-Desktop for macOS
-Dsetuserappdirpath=${USERAPPDIRTEMPLATE}
EOM
)
fi

# -Dsilent=noupdate
if [ "${DISABLEALLUPDATES}" = 1 ]; then
  myecho "- All automatic updates DISABLED"
  VMOPTIONS_UPDATES=$( cat << EOM
#
# Comment out the following line to enable updates
-Dsilent=noupdate
EOM
)
else
  if [ "${DISABLEUSERAPPDIR}" = 1 ]; then
    myecho "- Automatic updates enabled IN INSTALLATION"
  else
    myecho "- Automatic updates enabled in user-space"
  fi
  VMOPTIONS_UPDATES=$( cat << EOM
#
# Uncomment the following line to also disable all updates
#-Dsilent=noupdate
EOM
)
fi

printf "%s\n%s\n%s\n%s\n%s\n" "${VMOPTIONS_START}" "${VMOPTIONS_COMMENT}" "${VMOPTIONS_USERAPPDIR}" "${VMOPTIONS_USERAPPDIRPATH}" "${VMOPTIONS_UPDATES}" > "${VMOPTIONS}" || addwarning "Possible problem adding options to '${VMOPTIONS}'" 14

# set a global path in /etc/paths.d
if [ "${DISABLEGLOBALPATH}" != 1 ]; then
  US_NAME="${NAME// /_}"
  PATHFILE="/etc/paths.d/${US_NAME}-${HASH}"
  myecho "Writing global PATH to '${PATHFILE}'"
  echo "${APPLICATIONFOLDER}/${APPNAME}/Contents/MacOS" > "${PATHFILE}" || addwarning "Possible problem writing path file '${PATHFILE}'" 15
fi

# show accumulated warnings
if [ "${#WARNINGS[@]}" != 0 ]; then
  myecho "-----------------"
  myecho "Installation complete."
  myecho "Warnings summary:"
  myecho "$( printf -- '- %s\n' "${WARNINGS[@]}" )"
else
  myecho "Successful installation!"
fi

exit $EXIT
