#!/bin/bash

# Global readonly variables can't be shadowed by local variables so wrap
# our code in a function so we can declare all variables local
main() {
  # Customizable configuration constants
  local -r DEFAULT_MOD_LIST='cpu mem bl vol-amixer bat dt'
  local -r DEFAULT_RES=.25
  local -r DEFAULT_PRE=' '
  local -r DEFAULT_SEP_L='| '
  local -r DEFAULT_SEP_R=' '
  local -r DEFAULT_SUF=' '

  local -r mod_list="${1-${DEFAULT_MOD_LIST}}"
  local -r res="${2:-${DEFAULT_RES}}"
  local -r pre="${3-${DEFAULT_PRE}}"
  local -r sep_l="${4-${DEFAULT_SEP_L}}"
  local -r sep_r="${5-${DEFAULT_SEP_R}}"
  local -r suf="${6-${DEFAULT_SUF}}"

  local -r MOD_DIR="$(dirname "$0")"/module
  local -r ACTION_DIR=/tmp/xrsb-action
  local -i ACTION_DIR_LEN=${#ACTION_DIR}

  # Cache module values so we can reuse them without recomputing them
  local -A stat_cache
  # Since stat_cache is hash ordered, maintain the display order (as defined
  # by mod_list) of the keys so we can loop over the cache in display order
  # when generating the full status
  local -a stat_ordered_keys
  local mod mod_file key

  # Process the module list
  for mod in ${mod_list}; do
    # Check if the module file exists
    mod_file="${MOD_DIR}/${mod}"
    if [[ -r "${mod_file}" ]]; then
      # Source the module, compute its key and add it to the ordered array,
      # and call the module function and store the value in the cache
      # shellcheck source=/dev/null
      source "${mod_file}"
      key="${mod//-/_}"
      stat_ordered_keys+=("${key}")
      stat_cache["${key}"]="$("mod_${key}")"
    fi
  done

  draw_status() {
    local stat

    # Construct the status by looping over the cached values in display order
    for key in "${stat_ordered_keys[@]}"; do
      printf -v stat '%b%b%b%b' \
        "${stat}" "${sep_l}" "${stat_cache[${key}]}" "${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

  process_signal () {
    local -a actions
    local path do_redraw

    # Get the actions and purge the action directory
    readarray -d '' actions < \
      <(find "${ACTION_DIR}" -maxdepth 1 -type f -exec rm -f {} + -print0)

    # Process each action
    for path in "${actions[@]}"; do
      key="${path:$((ACTION_DIR_LEN + 1))}"
      # Call the module function if the cache entry for the module is defined
      if [[ -v stat_cache[${key}] ]]; then
        stat_cache["${key}"]="$("mod_${key}")"
        do_redraw=1
      fi
    done

    if [[ -v do_redraw ]]; then draw_status; fi
  }

  # Wait for signals
  mkdir -p "${ACTION_DIR}"
  trap process_signal SIGUSR1

  while :; do
    sleep "${res}"
  done
}

main "$@"