#!/usr/bin/env bash
readonly SAVED_OPTS="$(
  shopt -po
  shopt -p
)"
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    SOURCE="$TARGET"
  else
    SELF_DIR="$(dirname "$SOURCE")"
    SOURCE="$SELF_DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
SELF_DIR="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)"
readonly SELF_DIR

# shellcheck disable=SC1090
source "${SELF_DIR}/../lib/common.sh" || {
  echo "Failed to load common.sh in ${SELF_DIR}"
  exit 1
}
# DEFAULTS may be overridden by calling environment.
readonly GRADLEW="${GRADLEW:-gradlew}"

declare -a __GNG_CONFIG
#NOTE: don't invoke this function in subshell
function load_config() {
  #If __GNG_CONFIG is readonly(unset will fail if it is not.), means we already load the cfg_file into this array
  if ! (unset __GNG_CONFIG 2>/dev/null); then
    return 0
  fi
  local curr_path="${1}"
  local cfg_file="${curr_path}/gradle/gng.cfg"
  [ -f "${cfg_file}" ] && {
    IFS=$'\n' read -r -d $'\0' -a __GNG_CONFIG < <(__load_cfg <"${cfg_file}" && printf '\0')
  }
  readonly __GNG_CONFIG
}

lookup() {
  local file="${1}"
  local curr_path="${2}"
  [[ -z "${curr_path}" ]] && curr_path="${PWD}"

  # Search recursively upwards for file.
  until [[ "${curr_path}" == "/" ]]; do
    if [[ -e "${curr_path}/${file}" ]]; then
      echo "${curr_path}/${file}"
      break
    else
      curr_path=$(dirname "${curr_path}")
    fi
  done
}

function cfg_get() {
  local key="${1}"
  for kv in "${__GNG_CONFIG[@]}"; do
    if [[ ${kv} =~ ^${key}= ]]; then
      trim "${kv#${key}=}"
    fi
  done
}

function __install_gw() {
  (
    # Restore shell options
    eval "${SAVED_OPTS}"
    local version="${1}"
    local type="${2}"
    local mirrorUrl="${3}"
    local dir="${4}"
    [ -d "${dir}" ] || mkdir "${dir}"
    mkdir -p "${dir}/gradle/wrapper"

    info "Installing Gradle Wrapper in ${dir}. (version=${version}, distributionType=${type}, mirrorUrl=${mirrorUrl:-<Not Specified>})"

    #Copy the embedded Gradle Wrapper
    local srcDir="${SELF_DIR}/.."
    cp -f "${srcDir}/gradle/gng.cfg" "${dir}/gradle/"
    cp -f "${srcDir}/gradle/gradlew" "${dir}/"
    cp -f "${srcDir}/gradle/gradlew.bat" "${dir}/"
    cp -f "${srcDir}/gradle/wrapper/gradle-wrapper.jar" "${dir}/gradle/wrapper/"

    local distributionUrl="${mirrorUrl:-https://services.gradle.org/distributions}"
    # shellcheck disable=SC2001
    #Escape characters, see java.util.Properties.saveConvert for details
    distributionUrl=$(echo "${distributionUrl}" | sed 's/[#\!=:]/\\&/g') || die "Failed to escape characters in distributionUrl(${distributionUrl}"
    distributionUrl="${distributionUrl}/gradle-${version}-${type}.zip"
    cat <<__TEXT__ >"${dir}/gradle/wrapper/gradle-wrapper.properties"
#Generated by GNG
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=${distributionUrl}
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
__TEXT__
  ) || die "Failed to install Gradle Wrapper!"
}

select_gradle() {
  local dir="${1:-}"
  # Use project's gradlew if found.
  local gradlew
  gradlew=$(lookup "${GRADLEW}" "${dir}")
  if [[ -z "${gradlew}" ]]; then
    die "No ${GRADLEW} set up for this project; Please use 'gng wrapper' installing a Gradle Wrapper'."
  else
    echo "${gradlew}"
    return 0
  fi
  return 1
}

gradle() {
  local gradle
  gradle=$(select_gradle "${PWD}") || die "Failed to find gradlew."
  debug "Using gradle at '${gradle}' to run"
  load_config "$(dirname "${gradle}")"
  (
    # Restore shell options
    eval "${SAVED_OPTS}"
    GRADLE_OPTS="$(cfg_get GRADLE_OPTS) ${GRADLE_OPTS:-}"
    export GRADLE_OPTS
    exec "sh" "${gradle}" "$@"
  )
}

bootstrap_help() {
  printf '%s\n' "Generates a Gradle Wrapper"
  printf 'Usage: gng wrapper [-v|--version <arg>] [-t|--distribution-type <arg>] [-m|--mirror <arg>] [-h|--help] [ -d|--destination-dir <arg>\n'
  printf '\t%s\n' "-v, --version: Gradle Version (default: 'latest', version information is from https://services.gradle.org/versions/current, visit https://services.gradle.org/versions/all for all available versions)"
  printf '\t%s\n' "-t, --distribution-type: Gradle Distribution Type (default: 'all')"
  printf '\t%s\n' "-m, --mirror: Gradle Distribution Mirror URL Prefix(Optional with no default value, The url prefix replaces https://services.gradle.org/distributions/)"
  printf '\t%s\n' "              It replaces the whole distributionUrl except the file part in a URL. For example, if specify '-m \"https://example.com/gradle/\"', then"
  printf '\t%s\n' "              \"https://services.gradle.org/distributions/gradle-6.8-all.zip\" will become \"https://example.com/gradle/gradle-6.8-all.zip\""
  printf '\t%s\n' "-d, --destination-dir : The folder location where generated Gradle Wrapper(default: 'Your current working directory retrieved using \${PWD}')"
  printf '\t%s\n' "-h, --help: Prints help"
}

function parse_gradle_version() {
    if command -v python3 &> /dev/null
    then
        python3 -c "import json,sys; obj=json.load(sys.stdin); print (obj['version']);"
        return
    fi
    if command -v python &> /dev/null
    then
        python_major_version=$(python -c 'import sys; print(sys.version_info[0])')
        if [ "$python_major_version" = "2" ]; then
          python -c "import json,sys; obj=json.load(sys.stdin); print obj[unicode('version', 'utf-8')];"
        else
          python -c "import json,sys; obj=json.load(sys.stdin); print (obj['version']);"
        fi
        return
    fi
    die "Cannot find python/python3 interpreter !"
}

function bootstrap() {
  local _arg_version _arg_distribution_type _arg_destination_dir
  _arg_version="latest"
  _arg_distribution_type="all"
  _arg_destination_dir="${PWD}"
  while test $# -gt 0; do
    _key="$1"
    case "$_key" in
    -v | --version)
      (($# < 2)) && die "Missing value for the argument '$_key'." 1
      _arg_version="$2"
      shift
      ;;
    -t | --distribution-type)
      (($# < 2)) && die "Missing value for the argument '$_key'." 1
      _arg_distribution_type="$2"
      shift
      ;;
    -m | --mirror)
      (($# < 2)) && die "Missing value for the argument '$_key'." 1
      _arg_mirror="$2"
      shift
      ;;
    -d | --destination-dir-)
      (($# < 2)) && die "Missing value for the argument '$_key'." 1
      _arg_destination_dir="$2"
      shift
      ;;
    -h | --help)
      bootstrap_help
      exit 0
      ;;
    *)
      die "Unknown argument ${_key}"
      ;;
    esac
    shift
  done

  if [[ "${_arg_version}" == "latest" ]]; then
    info "Fetching the latest Gradle version from services.gradle.org"
    _arg_version=$(curl -m 60 -sSL "https://services.gradle.org/versions/current" |
                       parse_gradle_version) || die "Failed to fetch the version info!"
    info "The latest Gradle version is ${_arg_version}"
  fi
  __install_gw "${_arg_version}" "${_arg_distribution_type}" "${_arg_mirror:-}" "${_arg_destination_dir}"
}

function trust() {
  local HOST="${1}"
  local PORT="${2:-443}"
  local KEYSTORE_FILE
  KEYSTORE_FILE="$(readlink -e "$(dirname "$(readlink -e "$(which keytool)")")")"/../lib/security/cacerts
  local KEYSTORE_PASS="${KEYSTORE_PASS:-changeit}"
  keytool -printcert -sslserver "${HOST}:${PORT}" -rfc | keytool -import -noprompt -alias "${HOST}" -keystore "${KEYSTORE_FILE}" -storepass "${KEYSTORE_PASS}"
}

readonly SCRIPT_NAME="$(basename "$0")"
if [ "gng" = "${SCRIPT_NAME}" ]; then
  case "${1:-}" in
  trust)
    shift
    trust "$@"
    ;;
  wrapper)
    shift
    bootstrap "$@"
    ;;
  *)
    gradle "$@"
    ;;
  esac
else
  case "${1:-}" in
  --bootstrap)
    die "--bootstrap is deprecated, please use 'gng wrapper' instead, type 'gng wrapper -h' for details."
    ;;
  *)
    gradle "$@"
    ;;
  esac
fi
