# Filename:      /usr/share/ngcp-ngcpcfg/functions/main
# Purpose:       helper functions for ngcpcfg
################################################################################

# support for testsuite, assume defaults if unset
FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}"

if ! [ -r "${FUNCTIONS}"/logs ] ; then
  printf "Error: %s/logs could not be read. Exiting.\n" "${FUNCTIONS}" >&2
  exit 1
fi
. "${FUNCTIONS}"/logs

if ! [ -r "${FUNCTIONS}"/init ] ; then
  log_error "%s/init could not be read. Exiting." "${FUNCTIONS}"
  exit 1
fi
set -o allexport
. "${FUNCTIONS}"/init
set +o allexport

## HA / carrier features {{{
if [ -r /usr/share/ngcp-ngcpcfg/functions/ha_features ] ; then
  . /usr/share/ngcp-ngcpcfg/functions/ha_features
  set_ha_file # set ${HA_FILE} for usage in generate_template_list
fi

if [ -r /usr/share/ngcp-ngcpcfg/functions/carrier_features ] ; then
  . /usr/share/ngcp-ngcpcfg/functions/carrier_features
  set_host_and_pair_files # set ${HOST_FILE} + ${PAIR_FILE} for usage in generate_template_list
fi
## }}}

## functions {{{
main_action() {
  local rc=0
  ACTION="$1"
  shift

  [ -z "${SUMMARY_ONLY:-}" ] && SUMMARY_ONLY=0

  if [ "${SUMMARY_ONLY}" == "1" ]; then
    log_debug "${SCRIPTS}/${ACTION} $* >(logger -t ngcpcfg --id=\"${NGCPCFG_PID}\") >/dev/null 2>&1"
    "${SCRIPTS}/${ACTION}" "$@" >(logger -t ngcpcfg --id="${NGCPCFG_PID}") >/dev/null 2>&1 || rc=$?
  else
    log_debug "${SCRIPTS}/${ACTION} $*"
    "${SCRIPTS}/${ACTION}" "$@" || rc=$?
  fi

  if [ "${rc}" == "0" ]; then
    local msg="Successfully executed '${ACTION}' on '${HNAME}'"
    [ "${SUMMARY_ONLY}" == "1" ] || log_only "${msg}"
    [ "${SUMMARY_ONLY}" == "0" ] || log_info "${msg}"
  else
    local msg="Failed to call action '${ACTION}' on '${HNAME}' (see logs on '${HNAME}')"
    [ "${SUMMARY_ONLY}" == "1" ] || log_only "Error: ${msg}"
    [ "${SUMMARY_ONLY}" == "0" ] || log_error "${msg}"
    exit "${rc}"
  fi
}

hook_setup() {
  log_debug "hook_setup ${1:-}"

  if ! [ -d "${HOOKS}" ] ; then
    log_debug "Directory ${HOOKS} does not exist."
    return 0
  fi

  local target_directory="$1"

  if [ -z "${1:-}" ] ; then
    log_error "Missing argument for target directory in hook_setup. Exiting."
    exit 1
  fi

  if ! [ -d "${target_directory}" ] ; then
    if [ "${NGCP_TESTSUITE:-false}" = "true" ]; then
      log_info "Hook target directory ${target_directory} not a directory. Creating it."
      mkdir -p "${target_directory}"
    else
      log_error "Hook target directory ${target_directory} not a directory. Exiting."
      exit 1
    fi
  fi

  for hook in "${HOOKS}"/* ; do
    [ -r "${hook}" ] || continue
    log_debug "Creating symlink for ${hook} in ${target_directory}"
    ln -sf "$(readlink -f "${hook}")" "${target_directory}"/
  done
}

compare_active_branch() {
  log_debug "get_active_branch ${1:-}"

  log_debug "cd ${NGCPCTL_MAIN}"
  cd "${NGCPCTL_MAIN}"

  local current_branch
  current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
  log_debug "current_branch = ${current_branch}"
  echo "${current_branch}"
}

get_branch_status() {
  log_debug "cd ${NGCPCTL_MAIN}"
  cd "${NGCPCTL_MAIN}"

  log_debug "git rev-parse HEAD"
  local LOCAL=$(git rev-parse HEAD)

  log_debug "git rev-parse @{u}"
  local REMOTE=$(git rev-parse @{u})

  log_debug "git merge-base HEAD @{u}"
  local BASE=$(git merge-base HEAD @{u})

  if [ "${LOCAL}" = "${REMOTE}" ]; then
    # Up-to-date
    return 0
  elif [ "${LOCAL}" = "${BASE}" ]; then
    # Need to pull
    return 1
  elif [ "${REMOTE}" = "${BASE}" ]; then
    # Need to push
    return 2
  else
    # Diverged
    return 3
  fi
}

generate_template_list() {
  [ -n "${TEMPLATE_POOL_BASE}" ] || return 1

  local filelist_allfiles=$(mktemp)
  local filelist_prepared=$(mktemp)
  local filelist_final=$(mktemp)
  local filelist_sorted=$(mktemp)
  declare -a dirs_to_process=()

  for dir in ${CONFIG_POOL} ; do
    [ -n "${dir}" ] || ( log_warn "${dir} doesn't exist" ; continue )
    dirs_to_process+=("${TEMPLATE_POOL_BASE}/${dir}")
  done

  # find all files we should process
  find "${dirs_to_process[@]}" \
    -name \*.tt2 \
    -o -name \*.tt2"${HA_FILE:-}" \
    -o -name \*.tt2"${HOST_FILE:-}" \
    -o -name \*.tt2"${PAIR_FILE:-}" \
      > "${filelist_allfiles}"

  # argument(s) (file list/pattern) provided via cmdline
  if [ -z "${1:-}" ] ; then
    cat "${filelist_allfiles}" >> "${filelist_prepared}"
  else
    # limit processing to requested file(s) only
    for arg in "$@"; do
      grep -- "${arg}" "${filelist_allfiles}" >> "${filelist_prepared}"
    done
  fi

  # remove all filenames where a preferred filename exists
  # foo.customtt.tt2.hostname > foo.customtt.tt2.pairname > foo.customtt.tt2.spX > foo.customtt.tt2 > foo.tt2.hostname > foo.tt2.pairname > foo.tt2.spX > foo.tt2
  for line in $(cat ${filelist_prepared}); do

    # ignoring foo.patchtt.tt2.* completely (it is not a tt2 template to be built)
    if [[ "${line}" =~ .*\.patchtt\.tt2(.*)?$ ]]; then
      log_debug "Ignored patchtt file '${line}'"
      continue
    fi

    normalized_filename="${line}"

    if [ -n "${HA_FILE:-}" ] ; then
      normalized_filename="${normalized_filename%${HA_FILE}}"
    fi

    if [ -n "${HOST_FILE:-}" ] ; then
      normalized_filename="${normalized_filename%${HOST_FILE}}"
    fi

    if [ -n "${PAIR_FILE:-}" ] ; then
      normalized_filename="${normalized_filename%${PAIR_FILE}}"
    fi

    normalized_filename="${normalized_filename%.customtt.tt2}"
    normalized_filename="${normalized_filename%.tt2}"

    # skipping all further checks IF we have only one entry in 'filelist_prepared' (90% cases)
    if [[ "$(grep -Ec "^${normalized_filename}" "${filelist_prepared}")" == "1" ]]; then
      log_debug "Using the only one available file '${line}' matching on '${normalized_filename}'"
      echo "${line}" >> "${filelist_final}"
      continue
    fi

    # foo.custom.tt2.hostname
    if [ -n "${HOST_FILE:-}" ] ; then
      if grep -q -- "^${normalized_filename}.customtt.tt2${HOST_FILE}$" "${filelist_prepared}" ; then
        echo "${normalized_filename}.customtt.tt2${HOST_FILE}" >> "${filelist_final}"
        continue
      fi
    fi

    # foo.custom.tt2.pairname
    if [ -n "${PAIR_FILE:-}" ] ; then
      if grep -q -- "^${normalized_filename}.customtt.tt2${PAIR_FILE}$" "${filelist_prepared}" ; then
        echo "${normalized_filename}.customtt.tt2${PAIR_FILE}" >> "${filelist_final}"
        continue
      fi
    fi

    # foo.customtt.tt2.sp{1,2}
    if [ -n "${HA_FILE:-}" ] ; then
      if grep -q -- "^${normalized_filename}.customtt.tt2${HA_FILE:-}" "${filelist_prepared}" ; then
        echo "${normalized_filename}.customtt.tt2${HA_FILE:-}" >> "${filelist_final}"
        continue
      fi
    fi

    # foo.customtt.tt2
    if grep -q -- "^${normalized_filename}.customtt.tt2$" "${filelist_prepared}" ; then
      echo "${normalized_filename}.customtt.tt2" >> "${filelist_final}"
      continue
    fi

    # foo.tt2.hostname
    if [ -n "${HOST_FILE:-}" ] ; then
      if grep -q -- "^${normalized_filename}.tt2${HOST_FILE}" "${filelist_prepared}" ; then
        echo "${normalized_filename}.tt2${HOST_FILE}" >> "${filelist_final}"
        continue
      fi
    fi

    # foo.tt2.pairname
    if [ -n "${HOST_FILE:-}" ] ; then
      if grep -q -- "^${normalized_filename}.tt2${PAIR_FILE}" "${filelist_prepared}" ; then
        echo "${normalized_filename}.tt2${PAIR_FILE}" >> "${filelist_final}"
        continue
      fi
    fi

    # foo.tt2.sp{1,2}
    if [ -n "${HA_FILE:-}" ] ; then
      if grep -q -- "^${normalized_filename}.tt2${HA_FILE}" "${filelist_prepared}" ; then
        echo "${normalized_filename}.tt2${HA_FILE}" >> "${filelist_final}"
        continue
      fi
    fi

    # another file not matching any previous checks
    echo "${line}" >> "${filelist_final}"
  done

  sort -u ${filelist_final} >${filelist_sorted}

  # Output file list, make sure we provide the file names just once, and
  # special case the ngcp-service files, as they are a second stage source
  # of data required during configuration file building, which depends at
  # the same time on the main YAML files.
  grep ngcp-service ${filelist_sorted} || true
  grep -v ngcp-service ${filelist_sorted} || true

  if [ -n "${DEBUG:-}" ] ; then
    # send to stderr since stdout is used from outside
    log_debug "Not removing temporary filelist files since we are in debug mode:" >&2
    log_debug "  filelist_allfiles = ${filelist_allfiles}" >&2
    log_debug "  filelist_prepared = ${filelist_prepared}" >&2
    log_debug "  filelist_final    = ${filelist_final}" >&2
    log_debug "  filelist_sorted   = ${filelist_sorted}" >&2
  else
    rm -f "${filelist_allfiles}" "${filelist_prepared}" "${filelist_final}" "${filelist_sorted}"
  fi

  unset filelist_allfiles filelist_prepared filelist_final dirs_to_process
}

record_commit_id() {
  log_debug "cd ${NGCPCTL_MAIN}"
  cd "${NGCPCTL_MAIN}"

  log_debug "mkdir -p ${STATE_FILES_DIR}"
  mkdir -p "${STATE_FILES_DIR}"

  # if there are uncommitted changes then record it as such
  if git status --porcelain | grep -q . ; then
    echo "dirty" > "${STATE_FILES_DIR}/build"
  else
    local latest_commit=$(git log -1 --format="%H")
    log_debug "echo ${latest_commit} > ${STATE_FILES_DIR}/build"
    echo "${latest_commit}" > "${STATE_FILES_DIR}/build"
  fi
}

is_git_clean() {
  log_debug "call 'git status --porcelain=v2'. it must have no output"
  if [ -z "$(git status --porcelain=v2)" ]; then
    return 0
  else
    return 1
  fi
}
## }}}

## END OF FILE #################################################################
