#!/bin/bash

##########################################################
# Copyright (c) 2001-2025 Alexey Kuryakin daqgroup@mail.ru
##########################################################
# crwlib_base.sh library to be included into bash scripts.
##########################################################
# usage:
#  . $(crwkit which crwlib_base.sh)
# or
#  source $(crwkit which crwlib_base.sh)
##########################################################

##########################################################
# Check this script included but not executing directly.
##########################################################
if [[ "${FUNCNAME[0]}" = "main" ]] || [[ -z "${BASH_SOURCE[1]}" ]]; then
 1>&2 echo -ne "\n\nError: $(basename ${BASH_SOURCE[0]}) designed as included file but not for direct execution.\n\n";
 exit 1;
fi;

############################################
# Extract last word in space separated list.
############################################
function extract_last_word(){
 local last="";
 if [[ -n "$*" ]]; then
  for word in $*; do last="$word"; done;
  if [[ -n "$last" ]]; then echo "$last"; fi;
 fi;
};

################################
# Echo BASH_SOURCE[@] last item.
# It's top level running script.
################################
function BASH_SOURCE_last(){
 local n=0; # last item index
 let "n=${#BASH_SOURCE[@]}-1";
 echo "${BASH_SOURCE[$n]}";
};

##################################
# Top level script identification.
##################################
readonly startupdir="$(pwd -LP)";
readonly scriptfile="$(BASH_SOURCE_last)";
readonly scriptname="$(basename $scriptfile)";
readonly scriptbase="$(basename $scriptfile .sh)";
readonly scripthome="$(dirname  $scriptfile)";
readonly scriptFILE="$(realpath $scriptfile)";
readonly scriptHOME="$(dirname  $scriptFILE)";

# Obsolete version
# readonly startupdir="$(pwd -LP)";
# readonly scriptfile="$(extract_last_word ${BASH_SOURCE[*]})";
# readonly scriptname="$(basename $scriptfile)";
# readonly scriptbase="$(basename $scriptfile .sh)";
# readonly scripthome="$(readlink -f $(dirname $scriptfile))";
# readonly scriptFILE="$(readlink -f $scriptfile)";
# readonly scriptHOME="$(readlink -f $(dirname $scriptFILE))";

#######################
# Comments and remarks.
#######################
function note(){ return; };
function skip(){ return; };

#############################
# First argument is a nimber?
#############################
function is_number(){
 case $1 in
  ''|*[!0-9]*)  return 1; ;;
  *)            return 0; ;;
 esac;
};

#############################
# First argument is a option?
#############################
function is_option(){
 case $1 in
  -*) return 0; ;;
  *)  return 1; ;;
 esac;
};

##################################
# Uses language dependent strings?
##################################
declare -i uses_langstr=1;

################################
# Language dependent string echo
# langstr en 'Hello' ru 'Привет'
################################
function langstr(){
 local lng="en"; local msg="";
 if [[ "$uses_langstr" = "1" ]]; then lng="${LANG:0:2}"; fi;
 while [[ -n "$lng" ]] && [[ -n "$1" ]] && [[ -n "$2" ]]; do
  if [[ "$lng" = "$1" ]]; then msg="$2"; break; fi;
  if [[ "$lng" = "en" ]]; then msg="$2"; fi;
  if [[ "$lng" = "us" ]]; then msg="$2"; fi;
  if [[ "$lng" = "uk" ]]; then msg="$2"; fi;
  shift; shift;
 done;
 if [[ -n "$msg" ]]; then echo "$msg"; fi;
};

#############################################################################################################################
# https://stackoverflow.com/questions/1763891/is-it-possible-to-make-stdout-and-stderr-output-be-of-different-colors-in-xterm
#############################################################################################################################
function __colorize_norm__()( set -o pipefail; ( "$@" 2>&1>&3 | sed $'s,.*,\e[31m&\e[m,'   >&2 ) 3>&1 | sed $'s,.*,\e[32m&\e[m,'   );
function __colorize_bold__()( set -o pipefail; ( "$@" 2>&1>&3 | sed $'s,.*,\e[1;31m&\e[m,' >&2 ) 3>&1 | sed $'s,.*,\e[1;32m&\e[m,' );
function __colorize_head__()( set -o pipefail; ( "$@" 2>&1>&3 | sed $'s,.*,\e[1;35m&\e[m,' >&2 ) 3>&1 | sed $'s,.*,\e[1;37m&\e[m,' );
function __colorize_warn__()( set -o pipefail; ( "$@" 2>&1>&3 | sed $'s,.*,\e[1;33m&\e[m,' >&2 ) 3>&1 | sed $'s,.*,\e[1;32m&\e[m,' );
function __colorize_info__()( set -o pipefail; ( "$@" 2>&1>&3 | sed $'s,.*,\e[1;34m&\e[m,' >&2 ) 3>&1 | sed $'s,.*,\e[1;36m&\e[m,' );
function __colorize_none__()( set -o pipefail; ( "$@" 2>&1>&3 | cat                        >&2 ) 3>&1 | cat                        );

declare -i uses_colorize_term=1; # flag to enable colorize terminal, i.e. colorize_xxxx features

function colorize_norm(){ if [[ $uses_colorize_term -gt 0 ]] && [[ -t 1 ]] && [[ -t 2 ]]; then __colorize_norm__ "$@"; else __colorize_none__ "$@"; fi; };
function colorize_bold(){ if [[ $uses_colorize_term -gt 0 ]] && [[ -t 1 ]] && [[ -t 2 ]]; then __colorize_bold__ "$@"; else __colorize_none__ "$@"; fi; };
function colorize_head(){ if [[ $uses_colorize_term -gt 0 ]] && [[ -t 1 ]] && [[ -t 2 ]]; then __colorize_head__ "$@"; else __colorize_none__ "$@"; fi; };
function colorize_warn(){ if [[ $uses_colorize_term -gt 0 ]] && [[ -t 1 ]] && [[ -t 2 ]]; then __colorize_warn__ "$@"; else __colorize_none__ "$@"; fi; };
function colorize_info(){ if [[ $uses_colorize_term -gt 0 ]] && [[ -t 1 ]] && [[ -t 2 ]]; then __colorize_info__ "$@"; else __colorize_none__ "$@"; fi; };
function colorize_none(){ __colorize_none__ "$@"; };

########################################################
# wait_any_key n - wait upto n sec until any key pressed
# only if stdin and stdout is connected to terminal tty.
########################################################
function wait_any_key(){
 if [[ -z "$1" ]]; then return 0; fi;
 if [[ "$1" = "0" ]]; then return 0; fi;
 if [[ -t 0 ]] && [[ -t 1 ]] && [[ -t 2 ]]; then
  local msg="$2";
  if [[ -z "$msg" ]]; then
   msg="$(langstr ru "Для продолжения нажмите любую клавишу" en "Press any key to continue") …";
  fi;
  colorize_head echo "$msg";
  local ans; read -s -n 1 -t $1 ans;
 fi;
};

##################################
# check - is user has sudo access?
##################################
function is_iam_sudo(){
 unix sudoit -v 2>/dev/null;
};

#####################
# check user is root?
#####################
function is_iam_root_whoami(){
 if [[ "$(whoami)" != "root" ]]; then true; else false; fi;
};
function is_iam_root(){
 local uid="${EUID:-$UID}";
 if [[ -z "$uid" ]]; then uid="$(id -u)"; fi;
 if [[ "x$uid" == "x0" ]]; then return 0; fi;
 return 1;
};

##########################
# octal file mode like 755
##########################
function oct_file_mode(){
 if [[ -n "$1" ]] && [[ -e "$1" ]]; then
  stat -c "%a %n" $1 | cut -d ' ' -f 1;
 fi;
};

########################
# Echo to stderr stream.
########################
function echo_to_stderr(){
 1>&2 echo "$@";
};
function print_to_stderr(){
 1>&2 echo -n "$@";
};

########################
# Echo to stdout stream.
########################
function echo_to_stdout(){
 echo "$@";
};
function print_to_stdout(){
 echo -n "$@";
};

###########################
# Flags for fatal function:
###########################
declare -i fatal_notify_uses=1;            # uses notify-send for fatal
declare -i fatal_notify_crwdaq=0;          # uses send2crwdaq for fatal
declare -i fatal_notify_colorized=1;       # fatal message is colorized
declare -i fatal_notify_timeout=60000;     # timeout use by notify-send
declare fatal_notify_icon="dialog-error";  # icon name for notify-send

################################################
# Fatal error message: print a message and exit.
# Examples:
#  fatal   "Error: bla-bla-bla";
#  fatal 1 "Error: bla-bla-bla";
################################################
function fatal(){
 local code=1; local msg="$1"; if is_number "$1"; then code="$1"; msg="$2"; fi;
 if [[ "$fatal_notify_uses" = "1" ]]; then
  local date="$(date +%Y.%m.%d-%H:%M:%S)"; local script="$scriptname";
  if which notify-send >/dev/null 2>&1; then notify-send -t $fatal_notify_timeout -i "$fatal_notify_icon" "$date: $script" "$msg"; fi;
 fi;
 if [[ "$fatal_notify_crwdaq" = "1" ]] && [[ -n "$CRW_DAQ_SYS_EXE_PID" ]]; then
  local date="$(date +%Y.%m.%d-%H:%M:%S)"; local script="$scriptname";
  echo "@silent @echo $date => $script: $msg" | unix send2crwdaq;
 fi;
 if [[ "$fatal_notify_colorized" = "1" ]] && [[ -t 1 ]]; then
  colorize_bold echo_to_stderr -ne "\n$msg\n\n";
 else
  echo_to_stderr -ne "\n\n$msg\n\n";
 fi;
 exit $code;
};

###########################
# Flags for abort function:
###########################
declare -i abort_notify_uses=1;            # uses notify-send for abort
declare -i abort_notify_crwdaq=0;          # uses send2crwdaq for abort
declare -i abort_notify_colorized=1;       # abort message is colorized
declare -i abort_notify_timeout=60000;     # timeout use by notify-send
declare abort_notify_icon="dialog-error";  # icon name for notify-send

################################################
# Fatal error message: print a message and exit.
# Examples:
#  abort   "Error: bla-bla-bla";
#  abort 1 "Error: bla-bla-bla";
################################################
function abort(){
 local code=1; local msg="$1"; if is_number "$1"; then code="$1"; msg="$2"; fi;
 if [[ "$abort_notify_uses" = "1" ]]; then
  local date="$(date +%Y.%m.%d-%H:%M:%S)"; local script="$scriptname";
  if which notify-send >/dev/null 2>&1; then notify-send -t $abort_notify_timeout -i "$abort_notify_icon" "$date: $script" "$msg"; fi;
 fi;
 if [[ "$abort_notify_crwdaq" = "1" ]] && [[ -n "$CRW_DAQ_SYS_EXE_PID" ]]; then
  local date="$(date +%Y.%m.%d-%H:%M:%S)"; local script="$scriptname";
  echo "@silent @echo $date => $script: $msg" | unix send2crwdaq;
 fi;
 if [[ "$abort_notify_colorized" = "1" ]] && [[ -t 1 ]]; then
  colorize_bold echo_to_stderr -ne "\n$msg\n\n";
 else
  echo_to_stderr -ne "\n\n$msg\n\n";
 fi;
 exit $code;
};

############################
# case conversion functions.
############################
function upper_case_tr() { echo "$1" | tr '[:lower:]' '[:upper:]'; };
function lower_case_tr() { echo "$1" | tr '[:upper:]' '[:lower:]'; };
function upper_case() { echo "${1^^}"; };
function lower_case() { echo "${1,,}"; };

########################
# is $1 hidden dot file?
########################
function is_dot_file(){
 local fn="$(basename "$1")";
 local c1="$(cut -c 1 <<< $fn)";
 test "$c1" = ".";
}; 

###################################
# print argumets as quoted strings.
# use "$@","array[@]" to pass args.
###################################
function print_quoted_strings(){
 local n=$#;
 local s="";
 if [[ $n -gt 0 ]]; then
  for i in $(seq 1 $n); do
   line="$1"; shift;
   if [[ "$line" != "${line// /}" ]]; then line="\"$line\""; fi;
   if [[ $i -eq 1 ]]; then s="$line"; else s="$s $line"; fi;
  done;
  echo "$s";
 fi;
};

##########################################################
# return true if word $1 is in list of followed arguments.
# example:
# if word_is_in_list "apple" "orange apple grape"; then ..
##########################################################
function word_is_in_list(){
 local match="$1"; shift;
 if [[ -n "$match" ]]; then
  for word in $*; do
   if [[ "$word" = "$match" ]]; then return 0; fi;
  done;
 fi;
 return 1;
};

# https://stackoverflow.com/questions/5031764/position-of-a-string-within-a-string-using-linux-shell-script

#####################################################################################
# str_pos $1 $2 return true  and print 1-based position of substring $1 in string $2.
# str_pos $1 $2 return false and print 0 if substring $1 was not found  in string $2.
#####################################################################################
function str_pos(){
 local sub="$1"; local str="$2"; local tmp="${str%%"$sub"*}"; local pos=0;
 if [[ "x$tmp" = "x$str" ]]; then let "pos=0"; else let "pos=${#tmp}+1"; fi;
 echo "$pos"; if [[ $pos -le 0 ]]; then return 1; else return 0; fi;
};
##############################################################
# str_rpos is reversed (started from back) version of str_pos.
##############################################################
function str_rpos(){
 local sub="$1"; local str="$2"; local tmp="${str%"$sub"*}"; local pos=0;
 if [[ "x$tmp" = "x$str" ]]; then let "pos=0"; else let "pos=${#tmp}+1"; fi;
 echo "$pos"; if [[ $pos -le 0 ]]; then return 1; else return 0; fi;
};
##########################################################################
# str_starts_with $1 $2 return true if string $2 starts from substring $1.
##########################################################################
function str_starts_with(){
 local sub="$1"; local str="$2"; local tmp="${str#"$sub"}";
 if [[ "x$tmp" = "x$str" ]]; then return 1; else return 0; fi;
};
########################################################################
# str_ends_with $1 $2 return true if string $2 starts from substring $1.
########################################################################
function str_ends_with(){
 local sub="$1"; local str="$2"; local tmp="${str%"$sub"}";
 if [[ "x$tmp" = "x$str" ]]; then return 1; else return 0; fi;
};

#############
# END OF FILE
#############
