#!/bin/bash

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

###########################################################
## Find window by title [ and maybe class, pid, exe, host].
## Then kill(close) found window by sending SIGTERM signal.
###########################################################

###########################################################
source $(crwkit which crwlib_base.sh); # Use base library #
source $(crwkit which crwlib_file.sh); # Use file library #
###########################################################

let fatal_notify_uses=0;

function do_help(){
 local name="$(basename ${BASH_SOURCE[0]})";
 echo "$name version 1.0";
 if [ $1 -eq 0 ]; then return 0; fi;
 echo "Copyright (c) 2002-2024 Alexey Kuryakin daqgroup@mail.ru";
 echo "$name - find window by title [ class, pid, exe, host ].";
 echo "Then kill (close) found window by sending SIGTERM signal."
 echo "Usage:";
 echo " $name [-options] [parameters]";
 echo "Options:";
 echo " --help           - print help.";
 echo " --version        - print version.";
 echo " -i,--ignore-case - ignore char case";
 echo " -c,--class c     - also match window class (c)";
 echo " -p,--pid p       - also match process PID  (p)";
 echo " -e,--exe e       - also match executable   (e)";
 echo " -h,--host h      - also match host name    (h)";
 echo " -s,--sig s       - kill by sending signal  (s)";
 echo "                    default signal is SIGTERM"
 echo " -m,--multi       - allow kill multiple window(s).";
 echo " -x,--xkill       - use xkill program instead of kill.";
 echo " --               - end of options, followed params";
 echo "Parameters:";
 echo " title - target window title (mandatory)";
 echo " class - target window class (optional)";
 echo " exe   - target exe or pid   (optional)";
 echo "Notes:";
 echo " 1) -opt and --opt options are equivalent.";
 echo "    for example: -pid and --pid are synonyms.";
 echo " 2) title,class,host may be a regexp like '.*'";
 echo " 3) exe must be a basename of executable";
 echo " 4) pid must be a number = process PID";
 echo "Examples:";
 echo " unix $name --help";
 echo " unix $name --version";
 echo " unix $name 'KCalc'";
 echo " unix $name -x KCalc";
 echo " unix $name -p 9812 KCalc";
 echo " unix $name -e kcalc KCalc";
 echo " unix $name -s SIGINT KCalc";
 echo " unix $name -i -c 'kcalc.kcalc' 'kcalc'";
 echo " unix $name 'KCalc' 'kcalc.kcalc' kcalc";
 echo " unix $name -i -e kcalc -c 'kcalc.kcalc' -h $(hostname) 'KCalc'";
};

declare opt_i="";
declare host=".*";
declare pids=".*";
declare class=".*";
declare -i opt_m=0;
declare -i opt_x=0;
declare sig="";

# closewindow prefer option -x by default
if str_starts_with close $scriptbase; then let opt_x=1; fi;

function findwindow(){
 local title="$1"; 
 if [ -n "$title" ]; then
  local win="$(wmctrl -lpx | grep $opt_i " $title" | grep $opt_i " $class " | grep $opt_i " $host " | grep -P " $pids " | grep '0x')";
  if [ -n "$win" ]; then
   echo "$win";
  else
   fatal 1 "Error: not found window: \"$title\".";
  fi;
 else
  fatal 1 "Error: invalid arguments: $@.";
 fi;
};

function exe_pids(){
 if [ -n "$1" ]; then
  pgrep -lf $opt_i "$1" | awk '{print $1}' | xargs;
 fi;
};

function do_opt_i(){
 opt_i="-i";
};

function do_opt_c(){
 if [ -n "$1" ]; then
  class="$1";
 fi;
};

function do_opt_p(){
 local p="$1";
 if [ -n "$p" ]; then
  p="${p//,/ }"; p="${p//:/ }";
  p="$(echo "$p" | xargs)";
  pids="(${p// /|})";
 fi;
};

function do_opt_e(){
 if [ -n "$1" ]; then
  do_opt_p "$(exe_pids "$1")";
 fi;
};

function do_opt_h(){
 if [ -n "$1" ]; then
  host="$1";
 fi;
};

function do_opt_s(){
 if [ -n "$1" ]; then
  sig="$(echo "$1" |  tr '[:lower:]' '[:upper:]')";
  if [ -z "$(kill -l | grep -i "\b$sig\b")" ]; then fatal 1 "Error: unknown signal $sig."; fi;
  if is_number "$sig"; then
   sig="-n $sig";
  else
   sig="-s $sig";
  fi;
 fi;
};

function call(){
 if [ $# -gt 0 ]; then
  echo "$@";
  eval "$@";
 fi;
};

function kill_pids(){
 local pidlist="$*"; local n=$(echo $pidlist | wc -w);
 case $n in
  0) fatal 1 "Error: specified window not found."; ;;
  1) for pid in $pidlist; do call $(echo "kill $sig $pid" | xargs); done; ;;
  *) if [ $opt_m -eq 0 ]; then fatal 1 "Error: $n window(s) found. Specify window more presicely."; fi;
     for pid in $pidlist; do call $(echo "kill $sig $pid" | xargs); done; ;;
 esac;
};

function kill_wnds(){
 local wndlist="$*"; local n=$(echo $wndlist | wc -w);
 case $n in
  0) fatal 1 "Error: specified window not found."; ;;
  1) for wnd in $wndlist; do call xkill -id $wnd; done; ;;
  *) if [ $opt_m -eq 0 ]; then fatal 1 "Error: $n window(s) found. Specify window more presicely."; fi;
     for wnd in $wndlist; do call xkill -id $wnd; done; ;;
 esac;
};

function uses_xkill(){
 if which xkill >/dev/null 2>&1 && [ $opt_x -eq 1 ] && [ -z "$sig" ]; then return 0; fi;
 return 1;
};

function handle_arguments(){
 if [ -n "$2" ]; then do_opt_c "$2"; fi;
 if [ -n "$3" ]; then if is_number "$3"; then do_opt_p "$3"; else do_opt_e "$3"; fi; fi;
 if uses_xkill; then
  kill_wnds $(findwindow "$1" 2>/dev/null | awk '{print $1}' | xargs);
 else
  kill_pids $(findwindow "$1" 2>/dev/null | awk '{print $3}' | xargs);
 fi;
};

function fail_on_empty(){
 if [ -z "$1" ]; then
  fatal 1 "Error: missed arguments.\nType unix $(basename ${BASH_SOURCE[0]}) --help for help.";
 fi;
};

function fail_on_missed(){
 while [ -n "$1" ]; do
  if which $1 >/dev/null 2>&1; then
   true;
  else
   fatal 1 "Error: $1 not found.\nPlease install it first.";
  fi;
  shift;
 done;
};

function main(){
 fail_on_empty "$@";
 fail_on_missed wmctrl grep pgrep xargs awk wc;
 while [ -n "$1" ]; do
  case $1 in
   -version|--version)              do_help 0; exit 0; ;;
   -help|--help)                    do_help 1; exit 0; ;;
   -i|-ignore-case|--ignore-case)   do_opt_i; ;;
   -c|-class|--class)               shift; do_opt_c "$1"; ;;
   -p|-pid|--pid)                   shift; do_opt_p "$1"; ;;
   -e|-exe|--exe)                   shift; do_opt_e "$1"; ;;
   -h|-host|--host)                 shift; do_opt_h "$1"; ;;
   -s|-sig|--sig)                   shift; do_opt_s "$1"; ;;
   -m|-multi|--multi)               let opt_m=1; ;;
   -x|-xkill|--xkill)               let opt_x=1; ;;
   --)                              shift; handle_arguments "$@"; break; ;;
   -*)                              fatal 1 "Error: unknown option $1."; ;;
   *)                               handle_arguments "$@"; break; ;;
  esac;
  shift;
  fail_on_empty "$@";
 done;
};

main "$@";

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