#!/bin/bash

## [Goal] Helper Functions for Bash Scripts of https://gitlab.bfr.bund.de/4nsz/scripts.git
## [Author] Holger Brendebach

## Logging --------------------------------------------------

# messaging template from https://betterdev.blog/minimal-safe-bash-script-template/

logfile="/dev/null" # logfile for detailed, parameter-specific logging
logfile_global="/dev/null" # logfile for script execution logging

msg() {
  # this function is meant to be used to print everything that is NOT a script output.
  echo >&2 -e "${1-}"
}

logexec() {
  # NOTE: call function payload in "" if several commands are concatenated with && or it contains {}
  local message="$*"
  printf -v logdate '%(%Y-%m-%d %H:%M:%S)T' -1
  echo "[$logdate] $message" | tee -a $logfile
  [[ ${dryrun:-true} == false ]] && eval "$message"
}

logentry() {
  local message="$*"
  printf -v logdate '%(%Y-%m-%d %H:%M:%S)T' -1
  echo "[$logdate] $message" | tee -a $logfile $logfile_global
}

logheader() {
  local message="${1-}"
  echo
  echo "${cyan}$message${normal}" | tee >(decolorize >> $logfile)
}

logwarn() {
  local message="${1-}"
  printf -v logdate '%(%Y-%m-%d %H:%M:%S)T' -1
  echo "[$logdate] ${yellow}[WARNING]${normal} $message"
  echo "[$logdate] ${yellow}[WARNING]${normal} $message" | decolorize | tee -a $logfile $logfile_global > /dev/null
}

die() {
  local message="${1-}"
  local code=${2-1} # default exit status 1
  printf -v logdate '%(%Y-%m-%d %H:%M:%S)T' -1
  echo "[$logdate] ${red}[ERROR]${normal} $message"
  echo "[$logdate] ${red}[ERROR]${normal} $message" | decolorize | tee -a $logfile $logfile_global > /dev/null
  exit "$code"
}

## Git --------------------------------------------------

RequireCleanWorkingTree() {
  ## [Rationale] Exit if the tree is not clean.

  ## Update the index
  git update-index -q --ignore-submodules --refresh
  err=0

  ## Disallow unstaged changes in the working tree
  if ! git diff-files --quiet --ignore-submodules --; then
    logwarn "You have unstaged changes."
    git diff-files --name-status -r --ignore-submodules -- >&2
    err=1
  fi

  ## Disallow uncommitted changes in the index
  if ! git diff-index --cached --quiet HEAD --ignore-submodules --; then
    logwarn "Your index contains uncommitted changes."
    git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
    err=1
  fi

  if [ $err = 1 ]; then
    die "You need to commit or stash changes before updating subtrees."
  fi
}

ExitOnMergeConflicts() {
  ## [Rationale] Exit if there are merge conflicts.

  git fetch
  CONFLICTS=$(git ls-files -u | wc -l)
  if [ "$CONFLICTS" -gt 0 ]; then
     git merge --abort
     die "There is a merge conflict. Aborting."
  fi
}

## Functions --------------------------------------------------

colorize() {
  ## Fancy echo
  bold=$(tput bold)
  normal=$(tput sgr0)
  red=$(tput setaf 1)
  green=$(tput setaf 2)
  yellow=$(tput setaf 3)
  blue=$(tput setaf 4)
  magenta=$(tput setaf 5)
  cyan=$(tput setaf 6)
  grey=$(tput setaf 7)
  inverted=$(tput rev)
}

decolorize() {
  sed -r 's,\x1B[[(][0-9;]*[a-zA-Z],,g'
}

body() {
  IFS= read -r header
  printf '%s\n' "$header"
  "$@"
}

dev_notes() {
  cat <<- DEV

${red}Development Notes:${normal}

${bold}Goal:${normal}
$(grep -ioP "(?<=\[Goal\] ).*$" "$1")

${bold}Rationales:${normal}
$(grep -ionP "(?<=\[Rationale\]).*$" "$1")

${bold}ToDos:${normal}
$(grep -ionP "(?<=# TODO:).*$" "$1")

${bold}Notes:${normal}
$(grep -ionP "(?<=# NOTE:).*$" "$1")

DEV
  exit
}

interactive_query() {
  read -p "Do you want to continue? [Yy] " -n 1 -r
  echo
  [[ ! $REPLY =~ ^[Yy]$ ]] && echo "Aborted" && exit 0
}

check_conda() {
  ## [Rationale] If shell variable CONDA_EXE is set or PATH points to a conda binary, check its version and config/info
  conda_env_name=$(basename "${1-}")
  script_path=${script_path-}  # initialize variable if it is not set

  logheader "Conda Environment:"
  printf "Checking CONDA installation... "
  conda_bin=${CONDA_EXE:-$(which conda)}
  [[ -x "$(command -v ${conda_bin:-conda})" ]] || ( echo "${red}Not Detected${normal}" && exit 1 )
  [[ -x "$(command -v ${conda_bin:-conda})" ]] && echo "${green}Detected${normal}" && conda_status=true
  conda_info=$($conda_bin info --json)
  conda_version=$(grep -Po '(?<="conda_version": ).*$' <<< $conda_info | tr -d '",\0')
  conda_base=$(grep -Po '(?<="root_prefix": ).*$' <<< $conda_info | tr -d '",\0')
  conda_env_active=$(grep -Po '(?<="active_prefix_name": ).*$' <<< $conda_info | tr -d '",\0')
  conda_envs=$(grep -Pzo '(?s)(?<="envs": \[\n).*?(?=])' <<< $conda_info | tr -d '",\0' | sed 's/^[[:space:]]*//')
  conda_env_path=$(grep -e "\/${conda_env_name}$" <<< $conda_envs)  # multiple envs with the same name may exist, e.g. from other users

  printf "Checking MAMBA installation... "
  mamba_bin=${MAMBA_EXE:-$(which mamba)}
  [[ -x "$(command -v ${mamba_bin:-mamba})" ]] || ( echo "${red}Not Detected${normal}" && mamba_status=false )
  [[ -x "$(command -v ${mamba_bin:-mamba})" ]] && echo "${green}Detected${normal}" && mamba_status=true
  [[ ${mamba_status} == true ]] && conda_version=$($mamba_bin --version)

  printf "Checking APK source........... "
  [[ ${script_path##$conda_base} != "$script_path" ]] && bioconda_status=true
  [[ ${bioconda_status:-false} == false ]] && echo "${bold}GitLab${normal}"
  [[ ${bioconda_status:-false} == true ]] && conda_env_name=$(basename $(dirname $(dirname $(dirname ${script_path})))) && echo "${bold}BioConda${normal}"

  logheader "Conda Parameters:"
  echo "
conda_env_name:|${conda_env_name}
conda_base:|${conda_base/$'\n'/' ; '}
conda_env_path:|${conda_env_path/$'\n'/' ; '}
conda_env_active:|$conda_env_active
conda_shell_level:|${CONDA_SHLVL-}
conda_version:|${conda_version/$'\n'/' ; '}
" | column -t -s "|" | tee -a $logfile
  echo

  [[ ${CONDA_BUILD_STATE:-} == TEST ]] && return 0
  [[ -z "$conda_env_path" && ! ${script_name:-} =~ "_setup.sh" ]] && die "The conda environment name was not found in the list of environments."
}

source_conda() {
  ## [Rationale] If $PATH or various default installation targets contain a conda binary, query conda for its base directory and source the conda initialisation to allow conda() activate
  conda_bin=${CONDA_EXE:-$(which conda)}
  [[ -x "$(command -v ${conda_bin:-conda})" ]] && conda_info=$($conda_bin info --json)
  [[ -z "${conda_info-}" ]] && conda_info=$($HOME/miniconda3/bin/conda info --json)
  [[ -z "${conda_info-}" ]] && conda_info=$($HOME/anaconda3/bin/conda info --json)
  [[ -z "${conda_info-}" ]] || conda_base=$(grep -Po '(?<="root_prefix": ).*$' <<< $conda_info | tr -d '",\0')
  [[ -z "${conda_base-}" ]] && die "conda binary not found"
  source $conda_base/etc/profile.d/conda.sh  # NOTE: this does not activate the conda base but provides the conda activate function
}

run_command_array() {
  cmd_array=($(echo $@))
  logentry "${cmd_array[@]}" | tee -a $logfile $logfile_global
  [[ "${norun:-false}" == true ]] && return
  echo
  eval "${cmd_array[@]}"
}

## Preload Functions --------------------------------------------------
colorize
