#!/bin/bash

set -o nounset -o noglob +o braceexpand -o posix
shopt -s lastpipe
unset CDPATH TMOUT

########################################
############### variables ##############
########################################

declare -r -i number_of_background_jobs_to_create=8

declare -r -i max_time_to_wait=2
declare -r -i min_time_to_wait=1

declare -r valid_result_text="All ${number_of_background_jobs_to_create} background jobs have been waited"
declare -r wrong_result_text="!!! - Something went wrong with the waiting of the background jobs - !!!"
declare -r failed_result_text="!!! - The wait function did return a 127 return code - !!!"
declare -r wait_error_text="But also the wait command did return the following error message(s)"

declare -r red_color_ascii_code='\033[0;31m'
declare -r green_color_ascii_code='\033[1;32m'
declare -r blue_color_ascii_code='\033[1;34m'
declare -r color_reset_ascii_code='\033[0m'

########################################
####### background job functions #######
########################################

sleep_function () {
  sleep -- "$(( SRANDOM % max_time_to_wait + min_time_to_wait ))"
}

########################################
## managing background jobs functions ##
########################################

parallel_jobs_without_array () {
  local -i background_job_return_code
  local -i loop_iterations=0

  local -a pid_set=()
  local -i process_id
  local has_failed='false'
  local this_wait_error_message
  local wait_error_message=''

  for (( i = 0; i < number_of_background_jobs_to_create; ++i )) do
    sleep_function &
    pid_set[${!}]=''
  done

#  this_wait_error_message=''
#  while wait -p process_id -n; do
#  while wait -p process_id -n 2>&1; do
#  while this_wait_error_message="${ wait -p process_id -n; }"; do
  while this_wait_error_message="${ wait -p process_id -n 2>&1; }"; do
    background_job_return_code="${?}"
    unset pid_set[process_id]
    if [[ -n ${this_wait_error_message} ]] then
      wait_error_message+="        ${wait_error_message//$'\n'/$'\n        '}"$'\n'
    fi
    printf '%s\n' "    ${process_id} finished with the following status : 0"
    (( ++loop_iterations ))
  done
  background_job_return_code="${?}"
  if [[ -n ${this_wait_error_message} ]] then
    wait_error_message+="        ${wait_error_message//$'\n'/$'\n        '}"$'\n'
  fi

  printf '\n'
  printf '%s\n' "    Waited ${loop_iterations} times for $(( number_of_background_jobs_to_create - ${#pid_set[@]} )) background processes."
  if (( ${#pid_set[@]} > 0 )) then
    printf '%s\n' "    ${#pid_set[@]} background process(es) were never waited for."
  fi

  if [[ -n ${wait_error_message} ]] then
    printf '\n'
    printf '%s\n' "      ${wait_error_text}"
    printf '\n'
    printf '%s\n' "${wait_error_message}"
  fi

}

########################################

parallel_jobs_with_array () {
  local -i background_job_return_code
  local -i number_of_waited_background_jobs=0

  local -a pid_set=()
  local -i process_id
  local has_failed='false'
  local this_wait_error_message
  local wait_error_message=''

  for (( i = 0; i < number_of_background_jobs_to_create; i++ )) do
    sleep_function &
    pid_set[${!}]=''
  done

  while (( ${#pid_set[@]} > 0 )) do
    this_wait_error_message="${ wait -p process_id -n "${!pid_set[@]}" 2>&1; }"
    background_job_return_code="${?}"
    if [[ -n ${this_wait_error_message} ]] then
      wait_error_message+="        ${wait_error_message//$'\n'/$'\n        '}"$'\n'
    fi
    printf '%s\n' "    ${process_id} finished with the following status : ${background_job_return_code}"
    if (( background_job_return_code != 0 )) then
      has_failed='true'
      break
    fi
    unset pid_set[process_id]
    (( ++number_of_waited_background_jobs ))
  done

  printf '\n'
  if [[ ${has_failed} == 'false' ]] then
    if (( number_of_waited_background_jobs == number_of_background_jobs_to_create )) then
      printf '%s\n' "    ${valid_result_text}"
    else
      printf '%s\n' "    ${wrong_result_text}"
    fi
  else
    printf '%s\n' "    ${failed_result_text}"
  fi

  if [[ -n ${wait_error_message} ]] then
    printf '\n'
    printf '%s\n' "      ${wait_error_text}"
    printf '\n'
    printf '%s\n' "${wait_error_message}"
  fi

}

########################################
####### ways of calling functions ######
########################################

in_shell_parallel_jobs_without_array () {
  parallel_jobs_without_array
}

command_substitution_parallel_jobs_without_array () {
  printf '%s\n' "$( parallel_jobs_without_array )"
}

subshell_parallel_jobs_without_array() {
  ( parallel_jobs_without_array )
}

command_substitution_and_subshell_parallel_jobs_without_array () {
  printf '%s\n' "$( subshell_parallel_jobs_without_array )"
}

########################################

in_shell_parallel_jobs_with_array () {
  parallel_jobs_with_array
}

command_substitution_parallel_jobs_with_array () {
  printf '%s\n' "$( parallel_jobs_with_array )"
}

subshell_parallel_jobs_with_array () {
  ( parallel_jobs_with_array )
}

command_substitution_and_subshell_parallel_jobs_with_array () {
  printf '%s\n' "$( subshell_parallel_jobs_with_array )"
}

########################################
############ main functions ############
########################################

main_function () {
  printf '\n'
  printf '%s\n' "All the following functions should create and wait on ${number_of_background_jobs_to_create} background jobs !"
  printf '\n'

  printf '%b\n' "${green_color_ascii_code}###################################################${color_reset_ascii_code}"
  printf '%s\n' "Functions executing wait -n without an array of pids"
  printf '%b\n' "${green_color_ascii_code}###################################################${color_reset_ascii_code}"

  printf '\n'
  printf '%s\n' "  Function executed within the shell"
  printf '\n'
  printf '%b\n' "  ${green_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  in_shell_parallel_jobs_without_array

  printf '\n'
  printf '%b\n' "  ${blue_color_ascii_code}##################################################${color_reset_ascii_code}"

  printf '\n'
  printf '%s\n' "  Function executed in command substitution"
  printf '\n'
  printf '%b\n' "  ${red_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  command_substitution_parallel_jobs_without_array

  printf '\n'
  printf '%b\n' "  ${blue_color_ascii_code}##################################################${color_reset_ascii_code}"

  printf '\n'
  printf '%s\n' "  Function executed in an explicit subshell"
  printf '\n'
  printf '%b\n' "  ${green_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  subshell_parallel_jobs_without_array

  printf '\n'
  printf '%b\n' "  ${blue_color_ascii_code}##################################################${color_reset_ascii_code}"

  printf '\n'
  printf '%s\n' "  Function executed in an explicit subshell inside of command substitution"
  printf '\n'
  printf '%b\n' "  ${red_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  command_substitution_and_subshell_parallel_jobs_without_array

  printf '\n'
  printf '%b\n' "${green_color_ascii_code}################################################${color_reset_ascii_code}"
  printf '%s\n' "Functions executing wait -n with an array of pids"
  printf '%b\n' "${green_color_ascii_code}################################################${color_reset_ascii_code}"

  printf '\n'
  printf '%s\n' "  Function executed within the shell"
  printf '\n'
  printf '%b\n' "  ${green_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  in_shell_parallel_jobs_with_array

  printf '\n'
  printf '%b\n' "  ${blue_color_ascii_code}###############################################${color_reset_ascii_code}"

  printf '\n'
  printf '%s\n' "  Function executed in command substitution"
  printf '\n'
  printf '%b\n' "  ${red_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  command_substitution_parallel_jobs_with_array

  printf '\n'
  printf '%b\n' "  ${blue_color_ascii_code}###############################################${color_reset_ascii_code}"

  printf '\n'
  printf '%b\n' "  Function executed in an explicit subshell"
  printf '\n'
  printf '%b\n' "  ${green_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  subshell_parallel_jobs_with_array

  printf '\n'
  printf '%b\n' "  ${blue_color_ascii_code}###############################################${color_reset_ascii_code}"

  printf '\n'
  printf '%s\n' "  Function executed in an explicit subshell inside of command substitution"
  printf '\n'
  printf '%b\n' "  ${red_color_ascii_code}The job management works reliably here${color_reset_ascii_code}"
  printf '\n'

  command_substitution_and_subshell_parallel_jobs_with_array

  printf '\n'
}

########################################
############# execute main #############
########################################

main_function
