#!/bin/bash USAGE=" USAGE: avdd [='cpu mem bl vol-amixer bat dt'] [
=' '] [='| '] [=' '] [=' ']

        mod_list
                A comma or space separated list of modules that define both
                the order and the content of the status bar.

        pre     The prefix prepended to the beginning of the status bar.

        sep_l   The left separator between status bar sections.

        sep_r   The right separator between status bar sections.

        suf     The suffix appended to the end of the status bar.

EXAMPLES:

        Any of these will display this help message.

                avdd -h
                avdd -help
                avdd --help

        Run the daemon in the background to create a status bar with the
        default sections, prefix, separators, and suffix.

                avdd &

        Run the daemon in the background to create a status with only the
        volume and date/time sections, with the entire status between square
        brackets, and each section surrounded by angle brackets. Note that
        the first left separator and the last right separator are stripped
        from the output, so if you want them, simply include them in the
        prefix and suffix as shown here.

                avdd 'vol-amixer dt' '[<' '<' '>' '>]' &
"
DEFAULT_MOD_LIST='cpu mem bl vol-amixer bat dt'
DEFAULT_PRE=' '
DEFAULT_SEP_L='| '
DEFAULT_SEP_R=' '
DEFAULT_SUF=' '
MOD_DIR="$(dirname "$0")"/mod
FIFO=/tmp/avdd-fifo

mod_list="${1-${DEFAULT_MOD_LIST}}"
pre="${2-${DEFAULT_PRE}}"
sep_l="${3-${DEFAULT_SEP_L}}"
sep_r="${4-${DEFAULT_SEP_R}}"
suf="${5-${DEFAULT_SUF}}"

# Map the module file name to the module function
mod_to_fn() {
  printf 'mod_%s' "${1//-/_}"
}

# Check if the user needs help
if [[ "${mod_list}" =~ ^(-h|-(-)?help)$ ]]; then
  printf '%s' "${USAGE}" 1>&2
  exit 0
fi

# For each module in the list, if the module file exists then source it,
# add its name to the ordered array, and call its function and cache the value
declare -A stat_cache
IFS=', ' read -r -a mods <<< "${mod_list}"
for mod in "${mods[@]}"; do
  mod_file="${MOD_DIR}/${mod}"
  if [[ -r "${mod_file}" ]]; then
    # shellcheck source=/dev/null
    source "${mod_file}"
    stat_cache_ordered_mods+=("${mod}")
    stat_cache["${mod}"]="$(eval "$(mod_to_fn "${mod}")")"
  fi
done

# Construct and display the status by looping over the cached values in  order
draw_status() {
  local mod stat
  for mod in "${stat_cache_ordered_mods[@]}"; do
    printf -v stat '%b%b%b%b' \
      "${stat}" "${sep_l}" "${stat_cache[${mod}]}" "${sep_r}"
  done

  # Trim the leading left separator and trailing right separator, and display
  # the status
  local -ri offset=${#sep_l}
  local -ri len=$((${#stat} - offset - ${#sep_r}))
  xsetroot -name "${pre}${stat:${offset}:${len}}${suf}"
}

# Draw the initial status
draw_status

# If the module value is in the cache, indicating that the module controls
# part of the status bar, execute the module function and redraw the status
# bar if that part of the status bar has changed
process_cmd () {
  local -r mod="$1"
  if [[ -v stat_cache[${mod}] ]]; then
    local -r new_val="$(eval "$(mod_to_fn "${mod}")")"
    if [[ "${new_val}" != stat_cache["${mod}"] ]]; then
      stat_cache["${mod}"]="${new_val}"
      draw_status
    fi
  fi
}

# Setup the named pipe to receive commands
if [[ ! -p "${FIFO}" ]]; then mkfifo "${FIFO}"; fi
trap "rm -f ${FIFO}" EXIT

# Each time the pipe is emptied out, the inner while loop will finish, so
# wrap it in an infinte loop to keep blocking until there is data on the pipe
while :; do
  while read -r cmd; do
    case "${cmd}" in
      res_quit)
        exit 0
        ;;
      res_*)
        ;;
      *)
        process_cmd "${cmd}"
        ;;
    esac
  done < "${FIFO}"
done