#!/bin/bash

function get_from_gh() {
  if [[ "${GH_TOKEN:-}" ]]; then
    # User has provided a Personal Access Token to mitigate rate-limiting issues
    if [[ -z "${oAuthScopes}" ]]; then
      oAuthScopes=$(curl -s -H "Authorization: token $GH_TOKEN" https://api.github.com/users/codertocat -I | grep x-oauth-scopes)
    fi
    if [[ ! "$oAuthScopes" =~ ^x-oauth-scopes:[[:space:]]*$ ]]; then
      # Don't use what you don't have to...
      log "ERROR: GH_TOKEN has permissions it doesn't need. Recreate or update this personal access token and disable ALL scopes."
      exit 1
    else
      curl -fsSL -H "Authorization: token $GH_TOKEN" "${@:2}" "$1"
    fi
  else
    curl -fsSL "${@:2}" "$1"
  fi
}

function join_by() {
  local d=$1
  shift
  echo -n "$1"
  shift
  printf "%s" "${@/#/$d}"
}

function get_major_version() {
  version=$1
  echo "$version" | cut -d. -f 1-2
}

function isURL() {
  local value=$1

  if [[ ${value:0:8} == "https://" || ${value:0:7} == "http://" || ${value:0:6} == "ftp://" ]]; then
    return 0
  else
    return 1
  fi
}

function isValidFileURL() {
  suffix=${1:?Missing required suffix arg}
  url=${2:?Missing required url arg}

  [[ "$url" == http*://*.${suffix} || "$url" == http*://*.${suffix}\?* ]]
}

function resolveEffectiveUrl() {
  url="${1:?Missing required url argument}"
  if ! curl -Ls -o /dev/null -w "%{url_effective}" "$url"; then
    log "ERROR failed to resolve effective URL from $url"
    exit 2
  fi
}

function getFilenameFromUrl() {
  url="${1:?Missing required url argument}"
  strippedOfQuery="${url%\?*}"
  basename "$strippedOfQuery"
}

function isTrue() {
  case "${1,,}" in
  true | on | 1)
    return 0
    ;;
  *)
    return 1
    ;;
  esac
}

function isFalse() {
  case "${1,,}" in
  false | off | 0)
    return 0
    ;;
  *)
    return 1
    ;;
  esac
}

function isDebugging() {
  if isTrue "${DEBUG:-false}"; then
    return 0
  else
    return 1
  fi
}

function handleDebugMode() {
  if isDebugging; then
    set -x
  fi
}

function debug() {
  if isDebugging; then
    log "DEBUG: $*"
  fi
}

function logn() {
  echo -n "[init] $*"
}

function log() {
  local oldState
  # The  return  status  when listing options is zero if all optnames are enabled, non- zero otherwise.
  oldState=$(shopt -po xtrace || true)
  shopt -u -o xtrace

  if isDebugging || isTrue "${LOG_TIMESTAMP:-false}"; then
    ts=" $(date --rfc-3339=seconds)"
  else
    ts=
  fi
  echo "[init]${ts} $*"
  eval "$oldState"
}

function logAutopause() {
  echo "[Autopause loop] $*"
}

function logAutopauseAction() {
  echo "[$(date -Iseconds)] [Autopause] $*"
}

function logAutostop() {
  echo "[Autostop loop] $*"
}

function logAutostopAction() {
  echo "[$(date -Iseconds)] [Autostop] $*"
}

function logRcon() {
  echo "[Rcon loop] $*"
}

function normalizeMemSize() {
  local scale=1
  case ${1,,} in
  *k)
    scale=1024
    ;;
  *m)
    scale=1048576
    ;;
  *g)
    scale=1073741824
    ;;
  esac

  val=${1:0:-1}
  echo $((val * scale))
}

function versionLessThan() {
  # Use if-else since strict mode might be enabled
  if mc-image-helper compare-versions "${VERSION}" lt "${1?}"; then
    return 0
  else
    return 1
  fi
}

requireVar() {
  if [ ! -v "$1" ]; then
    log "ERROR: $1 is required to be set"
    exit 1
  fi
  if [ -z "${!1}" ]; then
    log "ERROR: $1 is required to be set"
    exit 1
  fi
}

requireEnum() {
  var=${1?}
  shift

  for allowed in "$@"; do
    if [[ ${!var} = "$allowed" ]]; then
      return 0
    fi
  done

  log "ERROR: $var must be set to one of $*"
#  exit 1
}

function writeEula() {
  if ! echo "# Generated via Docker
# $(date)
eula=${EULA,,}
" >/data/eula.txt; then
    log "ERROR: unable to write eula to /data. Please make sure attached directory is writable by uid=${UID}"
    exit 2
  fi
}

function removeOldMods {
  if [ -d "$1" ]; then
    log "Removing old mods including:${REMOVE_OLD_MODS_INCLUDE} excluding:${REMOVE_OLD_MODS_EXCLUDE}"
    args=(
      --delete
      --type file
      --min-depth=1 --max-depth "${REMOVE_OLD_MODS_DEPTH:-16}"
      --name "${REMOVE_OLD_MODS_INCLUDE:-*}"
      --exclude-name "${REMOVE_OLD_MODS_EXCLUDE:-}"
    )
    if ! isDebugging; then
      args+=(--quiet)
    fi
    mc-image-helper find "${args[@]}" "$1"
  fi
}

function get() {
  mc-image-helper get "$@"
}

function get_silent() {
  local flags=(-s)
  if isTrue "${DEBUG_GET:-false}"; then
    flags+=("--debug")
  fi
  mc-image-helper "${flags[@]}" get "$@"
}

function isFamily() {
  for f in "${@}"; do
    if [[ ${FAMILY^^} == "${f^^}" ]]; then
      return 0
    fi
  done
  return 1
}

function isType() {
  for t in "${@}"; do
    # shellcheck disable=SC2153
    if [[ $TYPE == "$t" ]]; then
      return 0
    fi
  done
  return 1
}

function evaluateJavaCompatibilityForForge() {
  javaRelease=$(mc-image-helper java-release)
  if versionLessThan 1.18 && (( javaRelease > 8 )); then
    log "**********************************************************************"
    log "WARNING: Some mods and modpacks may require Java 8."
    log "         Please use itzg/minecraft-server:java8"
    log "**********************************************************************"
    sleep 5
  fi
}

function extract() {
  src=${1?}
  destDir=${2?}

  type=$(file -b --mime-type "${src}")
  case "${type}" in
    application/zip)
      unzip -o -q -d "${destDir}" "${src}"
      ;;
    application/x-tar|application/gzip|application/x-gzip|application/x-bzip2)
      tar -C "${destDir}" -xf "${src}"
      ;;
    application/zstd|application/x-zstd)
      tar -C "${destDir}" --use-compress-program=unzstd -xf "${src}"
      ;;
    *)
      log "ERROR: unsupported archive type: $type"
      return 1
      ;;
  esac
}

function getDistro() {
  grep -E "^ID=" /etc/os-release | cut -d= -f2 | sed -e 's/"//g'
}

function checkSum() {
  local sum_file=${1?}

  # Get distro
  distro=$(getDistro)

  if [ "${distro}" == "debian" ] && sha1sum -c "${sum_file}" --status 2> /dev/null; then
    return 0
  elif [ "${distro}" == "ubuntu" ] && sha1sum -c "${sum_file}" --status 2> /dev/null; then
    return 0
  elif [ "${distro}" == "alpine" ] && sha1sum -c "${sum_file}" -s 2> /dev/null; then
    return 0
  elif [ "${distro}" == "ol" ] && sha1sum -c "${sum_file}" --status 2> /dev/null; then
    return 0
  else
    return 1
  fi
}

function usesMods() {
  case "$FAMILY" in
    FORGE|FABRIC|HYBRID|SPONGE)
      return 0
  esac
  return 1
}

function usesPlugins() {
  case "$FAMILY" in
    SPIGOT|HYBRID)
      return 0
  esac
  return 1
}

function resolveVersion() {
  givenVersion="$VERSION"
  # shellcheck disable=SC2153
  if ! VERSION=$(mc-image-helper resolve-minecraft-version "$VERSION"); then
    exit 2
  fi
  log "Resolved version given ${givenVersion} into ${VERSION}"
}

function resolveFamily() {
  case "$TYPE" in
    PAPER|SPIGOT|BUKKIT|CANYON|PUFFERFISH|PURPUR)
      FAMILY=SIGOT
      ;;
    FORGE)
      FAMILY=FORGE
      ;;
    FABRIC|QUILT)
      FAMILY=FABRIC
      ;;
  esac
  export FAMILY
}