#!/bin/bash

##############################################################
## Copyright (c) 2002-2023 Alexey Kuryakin daqgroup@mail.ru ##
##############################################################

##############################################################
## General launcher for the "crwkit" package, also known as ##
## "UnixUtils" under Windows. That is common "wrapper" uses ##
## to call all other crwkit tools. The wrapper allows three ##
## synonym names: 1) crwkit 2) crwrun and 3) unix. The unix ##
## wrapper uses in UnixUtils and allowed for compatibility. ##
## For example:                                             ##
##  crwkit unixroot                                         ##
##  crwrun textmetadata                                     ##
##  unix dimtree                                            ##
##############################################################

##############################################################
## Script identification.                                   ##
##############################################################
readonly scriptfile="${BASH_SOURCE[0]}";
readonly versionstr="version 1.0  built 20230614";

##############################################################
## Print version, about, copyright, short help notes.       ##
##############################################################
function do_version(){
 local scriptname="$(basename $scriptfile)";
 echo "$scriptname $versionstr";
};

function do_about(){
 do_version;
 local scriptname="$(basename $scriptfile)";
 echo "Copyright (c) 2001-2023 Alexey Kuryakin daqgroup@mail.ru";
 echo "$scriptname - the program wrapper to run CRWKIT package tools.";
 if [[ -z "$1" ]]; then echo "Call $scriptname -h to get help."; fi;
};

function do_help(){
 do_about crwkit;
 local scriptname="$(basename $scriptfile)";
 echo "The crwkit package also known as UnixUtils (obsolete).";
 echo "The commands {crwkit,crwrun,unix} are synonyms.";
 echo "Usage:$scriptname [-options] [command [arguments]]";
 echo "Options:";
 echo " --version        : print version";
 echo " -h,--help        : print this help";
 echo " -l,--list        : list a commands";
 echo " -m,--manual      : open the manual";
 echo " -c,--command     : execute command as new  process";
 echo " -e,--execute     : execute command in same process";
 echo " -L,--dereference : dereference symlink for command";
 echo "Examples:";
 echo " unix -m";
 echo " unix -l";
 echo " unix -l";
 echo " unix lister";
 echo " unix -L smiGUI.tcl";
};

##############################################################
## Function to call on fatal errors to terminate execution. ##
##############################################################
function fatal(){
 1>&2 echo -ne "\n$1\n\n";
 exit 1;
};

##############################################################
## Check argument is valid crwkit root directory.           ##
##############################################################
function valid_root_dir(){
 local dir="$1";
 if [[ -z "$dir" ]]; then return 1; fi;
 local checklist="crwkit crwrun unix add/bin/dimsrv";
 for item in $checklist; do
  if [[ ! -e "$dir/$item" ]]; then return 1; fi;
 done;
 return 0;
};

##############################################################
## Check for simple call (no path or /usr/local/bin/ path). ##
##############################################################
function is_simple_call(){
 local checklist="crwkit crwrun unix";
 for item in $checklist; do
  if [[ "$scriptfile" = "$item" ]]; then return 0; fi;
 done;
 for item in $checklist; do
  if [[ "$scriptfile" = "/usr/local/bin/$item" ]]; then return 0; fi;
 done;
 return 1;
};

##################
## Just success ##
##################
function succeed(){
 return 0;
};

##############################################################
## Check/set UNIXROOT environ. Also for Crw32 compatibility ##
#  set CRWKIT_ROOT,CRWTOOLKIT_ROOT,CMDTOOLKIT_ROOT environ. ##
##############################################################
function check_unixroot(){
 ###########################################################
 ### Fast check for stardard location when uses simple call.
 ###########################################################
 if [[ -z "$UNIXROOT$CRWKIT_ROOT$CRWTOOLKIT_ROOT$CMDTOOLKIT_ROOT" ]]; then
  if valid_root_dir /opt/crwkit && is_simple_call; then
   export UNIXROOT="/opt/crwkit";
   export CMDTOOLKIT_ROOT="$UNIXROOT";
   export CRWTOOLKIT_ROOT="$UNIXROOT";
   export CRWKIT_ROOT="$UNIXROOT";
   return 0;
  fi;
 fi;
 ###############################
 ### Preference to $CRWKIT_ROOT.
 ###############################
 if valid_root_dir $CRWKIT_ROOT; then
  export UNIXROOT="$CRWKIT_ROOT";
 fi;
 ### Fallback variables.
 #######################
 if [[ -z "$UNIXROOT" ]]; then
  for dir in $CRWKIT_ROOT $CRWTOOLKIT_ROOT $CMDTOOLKIT_ROOT; do
   if valid_root_dir $dir; then export UNIXROOT="$dir"; break; fi;
  done;
 fi;
 ######################################
 ### Clear UNIXROOT if it is not valid.
 ######################################
 if valid_root_dir $UNIXROOT; then succeed; else export UNIXROOT=""; fi;
 ##############################
 ### Validate UNIXROOT if need.
 ##############################
 if [[ -z "$UNIXROOT" ]]; then
  local scriptFILE="$(realpath $scriptfile)";
  local scriptHOME="$(dirname  $scriptFILE)";
  export UNIXROOT="$(dirname $scriptHOME)";
 fi;
 #################################
 ### Final check to ensure all OK.
 #################################
 if valid_root_dir $UNIXROOT; then
  export CMDTOOLKIT_ROOT="$UNIXROOT";
  export CRWTOOLKIT_ROOT="$UNIXROOT";
  export CRWKIT_ROOT="$UNIXROOT";
  return 0;
 else
  fatal "Error: crwkit root directory not found.";
 fi;
};

###################################
## Add UNIXROOT directories to PATH
###################################
function add_dirs_to_path(){
 if [[ -n "$UNIXROOT" ]] && [[ -e "$UNIXROOT/add/bin/" ]]; then
  local TAIL="$UNIXROOT";
  for dir in bin add/bin; do
   if [[ -e "$UNIXROOT/$dir/" ]]; then
    TAIL="$TAIL:$UNIXROOT/$dir";
   fi;
  done;
  local p="${PATH//\//_}";
  local t="${TAIL//\//_}";
  if [[ "${p//:$t/:}" = "$p" ]]; then
   local NEWPATH="$PATH:$TAIL";
   export "PATH=$NEWPATH";
  fi;
 fi;
};

###############################
## find file $2 in directory $1
###############################
function find_in_dir(){
 local dir="$1"; local name="$2";
 if [[ -n "$name" ]] && [[ "${name:0:1}" != '/' ]] && [[ -n "$dir" ]] && [[ -e "$dir/" ]]; then
  local test="$(find $dir -name $name)";
  if [[ -n "$test" ]] && [[ -e "$test" ]] ; then
   name="$test";
  fi;
 fi;
 if [[ -n "$name" ]]; then
  echo "$name";
 fi;
};

#######################
## Handle --list option
#######################
function do_list(){
  for dir in . bin add/bin; do
   if [[ -e "$UNIXROOT/$dir/" ]]; then
    ls --color=auto $UNIXROOT/$dir;
   fi;
  done;
};

#########################
## Handle --manual option
#########################
function do_manual(){
 local md="$UNIXROOT/add/man/crwkit.md";
 local html="$md.html";
 if [ -e "$html" ]; then
  firefox --new-tab $html </dev/null >/dev/null 2>&1 & disown;
  exit;
 fi;
 if [ -e "$md" ]; then
  markdownviewer $md --browse;
  exit;
 fi;
};

#####################################################
## search *.wav in $UNIXROOT/add/wav, then play sound
#####################################################
function do_play(){
 local cmd="$1";
 if [[ "$cmd" = "play" ]]; then
  shift; local args="";
  for arg in "$@"; do
   case $arg in
    *.wav) args="$args $(find_in_dir $UNIXROOT/add/wav $arg)"; ;;
    *)     args="$args $arg"; ;;
   esac;
  done;
  $cmd $args;
 fi;
};

function do_notify_send(){
 local cmd="$1";
 if [[ "$cmd" = "notify-send" ]]; then
  shift; local args="";
  for arg in "$@"; do
   echo "$arg";
   case $arg in
    *.ico) args="$args $(find_in_dir $UNIXROOT/add/ico $arg)"; ;;
    *.png) args="$args $(find_in_dir $UNIXROOT/add/png $arg)"; ;;
    *)     args="$args $(printf %q "$arg")"; ;;
   esac;
  done;
  $cmd $args;
 fi;
};

################################
## Call command (as new process)
################################
function call(){
 "$@";
};

#####################
## Check $1 is option
#####################
function is_option(){
 local arg="$1";
 if [[ -z "$arg" ]]; then return 1; fi;
 if [[ "${arg:0:1}" != "-" ]]; then return 1; fi;
 return 0;
};

#######
## MAIN
#######

function main(){
 ################
 ## Check environ
 ################
 check_unixroot;
 add_dirs_to_path;
 #################################
 ## Execution mode: 0=call, 1=exec
 #################################
 local opt_exec=1;
 local opt_dereference=0;
 #####################
 ## Has any arguments?
 #####################
 if [[ -z "$1" ]]; then
  do_about;
  return 0;
 fi;
 #############
 ## Parse args
 #############
 while is_option "$1"; do
  case $1 in
   --version)        do_version; return 0; ;;
   -h|--help)        do_help;    return 0; ;;
   -m|--manual)      do_manual;  return 0; ;;
   -l|--list)        do_list;    return 0; ;;
   -c|--command)     shift; opt_exec=0; break; ;;
   -e|--execute)     shift; opt_exec=1; break; ;;
   -n|--exec)        shift; opt_exec=1; break; ;;
   -L|--dereference) opt_dereference=1; ;;
   *)                fatal "Error: invalid option $1"; ;;
  esac;
  shift;
 done;
 ###############################
 ## Handle some special commands
 ###############################
 case $1 in
  play)         do_play "$@"; return; ;;
# notify-send)  do_notify_send "$@"; return; ;;
  *) ;;
 esac;
 ##
 ## Dereference command if needed
 ##
 local cmd="";
 if [[ $opt_dereference -eq 1 ]]; then
  cmd="$(realpath $(which $1))";
  if [[ -e "$cmd" ]]; then
   shift;
  else
   fatal 1 "Error: could not dereference $1";
  fi;
 fi;
 ################################
 ## Execute command: call or exec
 ## call: run in new  process
 ## exec: run in same process
 ################################
 if [[ $opt_exec -eq 1 ]]; then
  exec $cmd "$@";
 else
  call $cmd "$@";
 fi;
};

main "$@";

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