#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

#set -x
#exec >  >(tee -a "/home/user/msgprogressbarlog")
#exec 2> >(tee -a "/home/user/msgprogressbarlog" >&2)

set -e
set -o pipefail
set -o errtrace

source /usr/libexec/helper-scripts/light_sleep.bsh

scriptname="$(basename -- "${BASH_SOURCE[0]}")"

timeout_command=("timeout" "--kill-after" "1" "2")

disable_traps() {
   trap - ERR
   trap - SIGHUP
   trap - SIGTERM
   trap - SIGINT
}

error_handler() {
   local exit_code="$?"

   disable_traps

   local msg="\
###############################################################################
## $scriptname script bug.
## No panic. Nothing is broken. Just some rare condition has been hit.
## Try again later. There is likely a solution for this problem.
## Please see Whonix News, Whonix Blog and Whonix User Help Forum.
## Please report this bug!
##
## BASH_COMMAND: $BASH_COMMAND
## exit_code: $exit_code
###############################################################################\
"
   printf '%s\n' "$msg" >&2
   if [ ! -d ~/".msgcollector" ]; then
      "${timeout_command[@]}" mkdir --parents -- ~/".msgcollector"
   fi
   printf '%s\n' "$0: BASH_COMMAND: $BASH_COMMAND | exit_code: $exit_code" | "${timeout_command[@]}" tee -a -- ~/".msgcollector/msgdispatcher-error.log" >/dev/null
   exit 1
}

parse_cmd_options() {
   ## Thanks to:
   ## http://mywiki.wooledge.org/BashFAQ/035

   while true; do
       case $1 in
           --verbose)
               set -x
               verbose="1"
               shift
               ;;
           --identifier)
               identifier="$2"
               shift 2
               ;;
           --progressbaridx)
               progressbaridx="$2"
               shift 2
               ;;
           --progressbartitlex)
               progressbartitlex="$2"
               shift 2
               ;;
           --animate)
               animate="1"
               shift
               ;;
           --message)
               message="$2"
               shift 2
               if [ "$message" = "" ]; then
                  printf '%s\n' "$0: ERROR: variable message is empty."
                  exit 1
               fi
               ;;
           --)
               shift
               break
               ;;
           -*)
               printf '%s\n' "$0: unknown option: $1" >&2
               exit 1
               ;;
           *)
               break
               ;;
       esac
   done

   ## If there are input files (for example) that follow the options, they
   ## will remain in the "$@" positional parameters.
}

preparation() {
   fifo="${msgcollector_run_dir}/${identifier}_${progressbaridx}_fifo"
   progress_txt_file="${msgcollector_run_dir}/${identifier}_${progressbaridx}_progresstxt"
   parent_pid_file="${msgcollector_run_dir}/${identifier}_${progressbaridx}_parentpid"
   yad_progress_pid_file="${msgcollector_run_dir}/${identifier}_${progressbaridx}_yadprogresspid"
}

cleanup_self() {
   true "$0: Check if yad_progress_pid $yad_progress_pid is still running..."
   if kill -0 -- "$yad_progress_pid" &>/dev/null; then
      true "$0: cleanup_self: yad still running: yes"
      true "$0: killing yad_progress_pid $yad_progress_pid..."
      if ! kill -s sigterm -- "$yad_progress_pid" &>/dev/null; then
         kill -s sigkill -- "$yad_progress_pid" &>/dev/null
      fi
   else
      true "$0: cleanup_self: yad still running: no"
   fi
   "${timeout_command[@]}" safe-rm --force -- "$fifo"
   "${timeout_command[@]}" safe-rm --force -- "$progress_txt_file"
   "${timeout_command[@]}" safe-rm --force -- "$yad_progress_pid_file"
   "${timeout_command[@]}" safe-rm --force -- "$parent_pid_file"
}

output_ex() {
   ## This function is called when the cancel button in yad gets pressed or
   ## when $progress_txt_file contains 100.

   ## Debugging.
   #printf '%s\n' "progress: $progress | caller: $PPID | $caller | killing $yad_progress_pid" >> /home/user/progresslog

   if [ -f "$parent_pid_file" ]; then
      "${timeout_command[@]}" "/usr/libexec/msgcollector/msgprogressbar_kill_helper" "${identifier}" "${progressbaridx}"
   else
      true "$output_signal_caught caught. Not killing parentpid $parentpid, \
because $parent_pid_file does not exist."
   fi

   cleanup_self
   exit 0
}

output_sighup() {
   output_signal_caught="SIGHUP"
   output_ex
}

output_sigint() {
   output_signal_caught="SIGINT"
   output_ex
}

output_sigterm() {
   output_signal_caught="SIGTERM"
   output_ex
}

start_progress_bar() {
   if [ "$message" = "" ]; then
      message="Progress bar message is empty, please report this msgcollector bug!"
   fi

   if [ "$progressbartitlex" = "" ]; then
      progressbartitlex="Variable progressbartitlex is empty. Please report this bug!"
   fi

   if [ -f "${msgcollector_run_dir}/${identifier}_icon" ]; then
      local icon
      icon="$("${timeout_command[@]}" cat -- "${msgcollector_run_dir}/${identifier}_icon")"
   fi

   if [ "$icon" = "" ]; then
      ## TODO: write error log
      local icon="/usr/share/icons/gnome/24x24/status/info.png"
   fi

   if [ -f "$parent_pid_file" ]; then
      true "parent_pid_file $parent_pid_file exists."
   else
      true "parent_pid_file $parent_pid_file does not exist."
   fi

   if [ -f "$progress_txt_file" ]; then
      ## Sometimes there is a race condition preventing the progressbar
      ## being closed. Let's use this alternative approach.
      progress_txt_file_progress="$("${timeout_command[@]}" cat -- "$progress_txt_file")"
      if [ "$progress_txt_file_progress" = "100" ]; then
         true "Already 100%. Not even opening progress bar."
         cleanup_self
         exit 0
      fi
   fi

   ## Sanity test for $progress_txt_file_progress, because we use it without
   ## quotes as argument for yad below.
   ## Also matches when $progress_txt_file_progress is unset.
   if [[ "$progress_txt_file_progress" != *[!0-9]* ]]; then
      true "'$progress_txt_file_progress' is strictly numeric."
   else
      error "'$progress_txt_file_progress' is NOT strictly numeric!"
      exit 1
   fi

   if [ ! "$progress_txt_file_progress" = "" ]; then
      percentage_maybe_add="--percentage $progress_txt_file_progress"
   fi

   ## Clean up eventual old progress bar.
   "${timeout_command[@]}" safe-rm --force -- "$fifo"

   "${timeout_command[@]}" mkfifo -- "$fifo"

   ## sanity test
   test -p "$fifo"

   ## Do not use '<' or '>' inside 'yad' progress messages, 'yad' will mess that up.
   ##
   ## Broken:
   ##    yad --auto-kill will result in yad sending a sighup signal upon
   ##    pressing of cancel button.
   ##    Therefore using '--no-buttons'.
   yad \
      --timeout "86400" \
      --no-markup \
      --auto-kill \
      --auto-close \
      --no-buttons \
      --window-icon "$icon" \
      --title="$progressbartitlex" \
      --progress \
      $percentage_maybe_add \
      --text "$message" \
      < "$fifo" & yad_progress_pid="$!"

   exec 3>"$fifo"

   printf '%s\n' "$yad_progress_pid" > "$yad_progress_pid_file"

   while true "$0: main loop"; do
      true "$0: main loop started"
      if [ -f "$progress_txt_file" ]; then
         true "$0: progress_txt_file exists"
         ## Sometimes there is a race condition preventing the progressbar
         ## being closed. Let's use this alternative approach.
         progress_txt_file_progress="$("${timeout_command[@]}" cat -- "$progress_txt_file")"
         if [ "$progress_txt_file_progress" = "100" ]; then
            break
         fi
      fi
      ## Alternatively (most cases) waiting for SIGHUP.
      ## Happens when 'yad' reached 100% or when cancel button is pressed.
      ## This will call the output_sighup function.

      if kill -0 -- "$yad_progress_pid" &>/dev/null; then
         true "$0: loop: yad still running: yes"
      else
         true "$0: loop: yad still running: no"
         break
      fi

      light_sleep "2"
   done
}

trap "error_handler" ERR
trap "output_sighup" SIGHUP
trap "output_sigterm" SIGTERM
trap "output_sigint" SIGINT

source /usr/libexec/msgcollector/msgcollector_shared
## sets: ${msgcollector_run_dir}
folder_init

## provides: check, is_whole_number
source /usr/libexec/msgcollector/check

source /usr/libexec/msgcollector/msgfallbacks
fallbacks ## provided by /usr/libexec/msgcollector/msgfallbacks
parse_cmd_options "$@"

check "$identifier"
check "$progressbaridx"

preparation
start_progress_bar
cleanup_self
