#!/usr/bin/env bash

script_name="$(basename $(realpath $0))"
working_dir="$(dirname $(realpath $0))"

conf_file="${working_dir}/conf/${script_name%%.*}.conf"

LOCK_DIR="/tmp/${script_name%%.*}.LOCK"
log_file="${LOCK_DIR}/${script_name%%.*}.log"

# Sending alert messages
#
declare -a alert_email_arr=()

backup_date="$(date +%Y-%m-%d-%H%M)"
declare -a files_backuped_arr=()
declare -a dirs_backuped_arr=()

# ----------
# Base Function(s)
# ----------

usage() {


   [[ -n  "$1" ]] && error "$1"


   [[ $terminal ]] && echo -e "
\033[1mUsage:\033[m

   $(basename $0) [OPTION [OPTION ..

\033[1mDescription\033[m

   <Some Description>

\033[1mOptions\033[m

   <List Options>

\033[1mFiles\033[m

   $conf_file: Configuration file

\033[1mExample:\033[m
   
   <description example>

      $(basename $0) .. <comand example>

   <description another example>
   
      $(basename $0) .. <command another example>

"

   clean_up 1

}

clean_up() {

   # Perform program exit housekeeping
   rm -rf "$LOCK_DIR"
   blank_line
   exit $1
}


echononl(){
   if $terminal ; then
      echo X\\c > /tmp/shprompt$$
      if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then
         echo -e -n "   $*\\c" 1>&2
      else
         echo -e -n "   $*" 1>&2
      fi
      rm /tmp/shprompt$$
   fi
}


fatal(){
   echo ""
   if $terminal ; then
      echo -e "   [ \033[31m\033[1mFatal\033[m ]: $*"
      echo ""
      echo -e "   \033[31m\033[1mScript was interupted\033[m!"
   else
      echo "   [ Fatal ]: $*"
      echo ""
      echo "              Script was terminated...."
   fi
   echo ""
   clean_up 1
}
error (){
   echo ""
   if $terminal ; then
      echo -e "   [ \033[31m\033[1mError\033[m ]: $*"
   else
      echo "[ Error ]: $*"
   fi
   echo ""
}

warn (){
   echo ""
   if $terminal ; then
      echo -e "   [ \033[33m\033[1mWarning\033[m ]: $*"
   else
      echo "[ Warning ]: $*"
   fi
   echo ""
}

warn_only_terminal () {
   if $terminal ; then
      echo ""
      echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*"
      echo ""
   fi
}

info (){
   if $terminal ; then
      echo ""
      echo -e "   [ \033[32m\033[1mInfo\033[m ]  $*"
      echo ""
   fi
}

ok (){
   if $terminal ; then
      echo ""
      echo -e "   [ \033[32m\033[1mOk\033[m ]    $*"
      echo ""
   fi
}

echo_done() {
   if $terminal ; then
      echo -e "\033[75G[ \033[32mdone\033[m ]"
   fi
}
echo_ok() {
   if $terminal ; then
      echo -e "\033[75G[ \033[32mok\033[m ]"
   fi
}
echo_warn() {
   if $terminal ; then
      echo -e "\033[75G[ \033[33mwarn\033[m ]"
   fi
}
echo_failed(){
   if $terminal ; then
      echo -e "\033[75G[ \033[1;31mfailed\033[m ]"
   fi
}
echo_skipped() {
   if $terminal ; then
      echo -e "\033[75G[ \033[90m\033[1mskipped\033[m ]"
   fi
}
echo_wait(){
   if $terminal ; then
      echo -en "\033[75G[ \033[5m\033[1m...\033[m ]"
   fi
}

trim() {
    local var="$*"
    var="${var#"${var%%[![:space:]]*}"}"   # remove leading whitespace characters
    var="${var%"${var##*[![:space:]]}"}"   # remove trailing whitespace characters
    echo -n "$var"
}

blank_line() {
   if $terminal ; then
      echo ""
   fi
}

is_number() {

   return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1);

   # - also possible
   # -
   #[[ ! -z "${1##*[!0-9]*}" ]] && return 0 || return 1
   #return $([[ ! -z "${1##*[!0-9]*}" ]])
}

reboot_system() {

#   content_type='Content-Type: text/plain;\n charset="utf-8"'
#   datum="$(date +"%d.%m.%Y")"
#   sender_address="root@$(hostname --long)"
#   msg="S*"
#
#
#   for _email in ${alert_email_arr[@]} ; do
#
#      echo -e "To:${_email}\n${content_type}\nSubject:[Fatal Error] - Reboot System\n${msg}" \
#         | sendmail -F "Error `hostname -f`" -f $sender_address $_email
#   done

   sleep 10
   /sbin/reboot -f > /dev/null 2>&1

}

# - Backup file or directory
# -
backup() {

   is_directory=false

   if [[ -z "$1" ]] ; then
      error "No file/directory for backup given!"
      return
   elif [[ -h "$1" ]] ; then
      _source="$(realpath $1)"
      warn "'$1' is a symlink to $_source."
   elif [[ ! -d "$1" ]] && [[ ! -f "$1" ]] ; then
      warn "'$1' not found. Backup skipped."
      return
   else
      _source="$(realpath $1)"
   fi

   if [[ -d "$_source" ]]; then
      is_directory=true
      echononl "Backup directory '$_source' .."
   else
      echononl "Backup file '$_source' .."
   fi

   cp -a "$_source" "${_source}.$backup_date" > $log_file 2>&1
   if [[ $? -eq 0 ]] ; then
      echo_ok
      if $is_directory ; then
         dirs_backuped_arr+=("$_source")
      else
         files_backuped_arr+=("$_source")
      fi
   else
      echo_failed
      error "$(cat "$log_file")"
   fi
}


# - Remove script generated backups, if source wasn't changed
# -
rm_unchanged_backup() {

   if [[ ${#files_backuped_arr[@]} -gt 0 ]] ; then
      for _file in "${files_backuped_arr[@]}" ; do
         if  $(diff "$_file" "${_file}.$backup_date" > /dev/null 2>&1) ; then
            echononl "File '$(basename "${_file}")' wasn't changed.\n   Delete the previous generated backup. .."
            rm "${_file}.$backup_date" > "$log_file" 2>&1
            if [[ $? -eq 0 ]]; then
               echo_ok
            else
               echo_failed
               error "$(cat "$log_file")"
            fi
            blank_line
         fi
      done
   fi
   if [[ ${#dirs_backuped_arr[@]} -gt 0 ]] ; then
      for _dir in "${dirs_backuped_arr[@]}" ; do
         if $(diff -Nur "$_dir" "${_dir}.$backup_date" > /dev/null 2>&1) ; then
            echononl "Directory '$(basename "${_dir}")' wasn't changed.\n   Delete the previous generated backup. .."
            rm -rf "${_dir}.$backup_date" > "$log_file" 2>&1
            if [[ $? -eq 0 ]]; then
               echo_ok
            else
               echo_failed
               error "$(cat "$log_file")"
            fi
            blank_line
         fi
      done
   fi
}


# ----------
# - Some checks ..
# ----------

# - Running in a terminal?
# -
if [[ -t 1 ]] ; then
   terminal=true
else
   terminal=false
fi

# -Is systemd supported on this system?
# -
systemd_supported=false
systemd=$(which systemd)
systemctl=$(which systemctl)

if [[ -n "$systemd" ]] && [[ -n "$systemctl" ]] ; then
   systemd_supported=true
fi

# - Print help?
# -
if [[ "$(trim $*)" = "-h" ]] || [[ "$(trim $*)" = "--help" ]] ; then
    usage
fi

if [[ -z "$(which basename)" ]]; then
   fatal 'It seems "basename" is not installed, but needed!'
fi

if [[ -z "$(which realpath)" ]]; then
   fatal 'It seems "realpath" is not installed, but needed!'
fi


# ==========
# - Begin Main Script
# ==========

# ----------
# - Headline
# ----------

if $terminal ; then
   echo ""
   echo -e "\033[1m----------\033[m"
   echo -e "\033[32m\033[1mRunning script \033[m\033[1m$script_name\033[32m .. \033[m"
   echo -e "\033[1m----------\033[m"
fi


# ----------
#  Read Configurations from $conf_file
# ----------


# - Give your default values here
# -
BATCH_MODE=false

PARAMETER_1="WERT_1"
PARAMETER_2="WERT_2"

DEFAULT_CONFLICTING_SCRIPTS=""

DEFAULT_COMPANY="O.OPEN"
DEFAULT_CONTENT_TYPE='Content-Type: text/plain;\n charset="utf-8"'
DEFAULT_SENDER_ADDRESS="${script_name%%.*}@$(hostname -f)"
DEFAULT_ALERT_EMAIL_ADDRESSES="ckubu@oopen.de"


if [[ -f "$conf_file" ]]; then
   source "$conf_file"
else
   warn_only_terminal "No configuration file '$conf_file' present.\n
                     Loading default values.."
fi


# ----------
# - Read commandline parameter
# ----------

while getopts bh opt ; do
   case $opt in
      b)    BATCH_MODE=true
            ;;
      h)    usage
            ;;
      \?)   usage ;;
   esac
done


# ----------
# - Read in parameters given by user
# ----------

if $terminal && ! $BATCH_MODE ; then

   echo ""
   echo ""
   echo -e "   \033[1mRead in parameters given by user ..\033[m"
   echo ""

   echo ""
   echo -e "\033[32m--\033[m"
   echo ""
   echo "   PARAMETER 1 ...."
   echo ""
   echo -e "   Type \033[33m<return>\033[m to accept the default."
   echo ""
   echo ""
   _PARAMETER_1=$PARAMETER_1
   PARAMETER_1=""
   echononl "PARAMETER 1 [\033[1m$_PARAMETER_1\033[m]: "
   read PARAMETER_1
   if [[ -z "$PARAMETER_1" ]] ; then
      PARAMETER_1=$_PARAMETER_1
   fi

   echo ""
   echo -e "\033[32m--\033[m"
   echo ""
   echo "   Blank separated list of PARAMETER 2"
   echo ""
   if [[ -n "$PARAMETER_2" ]]; then

      echo -e "   Type \033[33m<return>\033[m to accept the default."
      echo ""
      echo ""

      _PARAMETER_2=$PARAMETER_2
      PARAMETER_2=""

      echononl "PARAMETER 2 [\033[1m$_PARAMETER_2\033[m]: "
      read PARAMETER_2
      if [[ -z "$PARAMETER_2" ]] ; then
         PARAMETER_2=$_PARAMETER_2
      fi
   else
      echo ""

      echononl "\033[1mPARAMETER 2:\033[m "
      read PARAMETER_2
      while [[ -z "$(trim "$PARAMETER_2")" ]]; do
         echo "Entry for  PARAMETER 2 is required! Try again.."
         echononl "\033[1mPARAMETER 2: \033[m"
         read PARAMETER_2
      done
   fi

   echo ""
   echo ""
   echo -e "   \033[32m\033[1mParameter Summary:\033[m"
	echo ""

   echo "   PARAMETER 1................: $PARAMETER_1"
   echo "   PARAMETER 2................: $PARAMETER_2"

   echo ""
   echononl "Continue with this parameters? [\033[1myes/no\033[m]: "
   read OK
   while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do
      echononl "\033[33mWrong entry!\033[m [\033[1myes/no\033[m]: "
      read OK
   done
   [[ "${OK,,}" = "yes" ]] || fatal "Canceled by user input."

fi


# ----------
# Set Parameter values 
# ----------

[[ -n "$CONFLICTING_SCRIPTS" ]] || CONFLICTING_SCRIPTS="${DEFAULT_CONFLICTING_SCRIPTS}"

[[ -n "$(trim $alert_email_addresses)" ]] ||  alert_email_addresses=("${DEFAULT_ALERT_EMAIL_ADDRESSES[@]}")

if [[ ${#alert_email_addresses} -gt 0 ]] ; then
   for _email in $alert_email_addresses ; do
      alert_email_arr+=("$_email")
   done
fi

[[ -n "$sender_address" ]] || sender_address="${DEFAULT_SENDER_ADDRESS}"
[[ -n "$content_type" ]] || content_type="${DEFAULT_CONTENT_TYPE}"
[[ -n "$company" ]] || company="${DEFAULT_COMPANY}"


# -------------
# - Job is already running?
# -------------

# - If job already runs, stop execution..
# -
if mkdir "$LOCK_DIR" 2> /dev/null ; then

   ## - Remove lockdir when the script finishes, or when it receives a signal
   trap "clean_up 1" SIGHUP SIGINT SIGTERM

else

   datum="$(date +"%d.%m.%Y %H:%M")"

   msg="[ Error ]: A previos instance of \"`basename $0`\" seems already be running.\n\n           Exiting now.."

   echo ""
   echo "[ Error ]: A previos instance of that script \"`basename $0`\" seems already be running."
   echo ""
   echo -e "           Exiting now.."
   echo ""

   for _email in ${alert_email_arr[@]} ; do
      echo -e "To:${_email}\n${content_type}\nSubject:Error cronjob `basename $0` -- $datum\n${msg}\n" \
         | sendmail -F "Error `hostname -f`" -f $sender_address $_email
   done

   exit 1

fi


## ----------
## - Jobhandling 
## ----------
#
#LOCK_DIR="/tmp/${script_name%%.*}.$$.LOCK"
#
## - Run 'clean_up' for signals SIGHUP SIGINT SIGTERM
## -
#trap clean_up SIGHUP SIGINT SIGTERM
#
## - Create lock directory '$LOCK_DIR"
##
#mkdir "$LOCK_DIR"


# ----------
# - Stop here, if these give scripts are running
# ----------

if [[ ${#CONFLICTING_SCRIPTS} -gt 0 ]] ; then

   # - Try using a random start delay to prevent (or at least have a small chance) that
   # - conflicting scripts will both/all abort if they start at the same time.
   # -
   # - !! Notice !!
   # - This only makes sense if a fixed LOCK directory is used, otherwise the process list
   # - (and NOT the LOCK-directory) is used to look for scripts running in parallel.
   # -
   # - Skip delay if running in an terminal (from copnsole)
   # -
   if ! $terminal ; then
      if [[ "$LOCK_DIR" = "/tmp/${script_name%%.*}.LOCK" ]]; then
         _shift="$(( $RANDOM % 10 + 1 ))"
         sleep  $(( $RANDOM % 25 + $_shift ))
      fi
   fi

   _stop_running=false
   for _val in $CONFLICTING_SCRIPTS ; do

      IFS=':' read -a _val_arr <<< "${_val}"

      _script_name="$(basename ${_val_arr[0]})"

      if [[ -n "${_val_arr[1]}" ]] ; then

         if [[ "${_val_arr[1]}" = "CHECK_PROCESS_LIST" ]] ; then

            check_string_ps="${_val_arr[0]}"
            if ps -e f | grep -E "\s+${check_string_ps}" | grep -v grep | grep -v -E "\s+vim\s+" > /dev/null ; then
               _stop_running=true
            fi

         elif [[ -d "${_val_arr[1]}" ]] ; then
            _stop_running=true
         fi

      elif [[ -d "/tmp/${_script_name%%.*}.LOCK" ]]; then
         _stop_running=true
      fi

      if $_stop_running ; then

         warn_only_terminal "\033[1m${_script_name}\033[m is currently running, but it conflicts with this script.

                      Exiting now.."

         clean_up 1

      fi # if $_stop_running ; then

   done # for _val in $CONFLICTING_SCRIPTS ; do

fi # if [[ ${#CONFLICTING_SCRIPTS} -gt 0 ]] ; then


# ----------
# - Some pre-script tasks ..
# ----------

if $terminal ; then
   echo ""
   echo ""
   echo -e "   \033[1mDoing some pre-script tasks ..\033[m"
   echo ""
fi

echononl "-- All is fine --"
echo_ok


# ----------
# - Main part of script
# ----------

if $terminal ; then
   echo ""
   echo ""
   echo -e "   \033[1mMain part of script ..\033[m"
   echo ""
fi


# ----------
# - Some post-script tasks ..
# ----------

if $terminal ; then
   echo ""
   echo ""
   echo -e "   \033[1mDoing some post-script tasks ..\033[m"
   echo ""
fi


# - Remove generated backup file if source wasn't changed.
# -
rm_unchanged_backup

clean_up 0
