#!/bin/bash

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

###########################################################
## Find window by title [ and maybe class, pid, exe, host].
## Then change state of found window by using -b option(s).
## So window state become to specified parameters.
###########################################################

###########################################################
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 change state of found window by using -b options.";
 echo "So window state become to specified parameters.";
 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 " -m,--multi       - allow change multiple windows.";
 echo " -b,--become s    - change window state by arg (s).";
 echo "                    The format of the argument is:";
 echo "                    (remove|add|toggle),<PROP1>[,<PROP2>]";
 echo "                    The EWMH specification defines the";
 echo "                    following properties:";
 echo "                    modal, sticky, maximized_vert, maximized_horz,";
 echo "                    shaded, skip_taskbar, skip_pager, hidden,";
 echo "                    fullscreen, above, below";
 echo "                    MODAL - makes the window into a modal";
 echo "                    STICKY - makes the window appear on all workspaces";
 echo "                    MAXIMIZED_VERT - sizes the window maximized vertically";
 echo "                    MAXIMIZED_HORZ - sizes the window maximized horizontally";
 echo "                    ABOVE - Show window above all others (always on top)";
 echo "                    BELOW - Show window below all others";
 echo "                    SKIP_TASKBAR - hides the window from the taskbar";
 echo "                    SKIP_PAGER - hides the window from the window pager";
 echo "                    FULLSCREEN - makes window fullscreen";
 echo "                    HIDDEN - unmaps the window";
 echo "                    SHADED - rolls the window up";
 echo "                    DEMANDS_ATTENTION - marks window urgent or needing attention";
 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 -b remove,hidden 'KCalc'";
 echo " unix $name -b remove,hidden 'KCalc' 'kcalc.kcalc' kcalc";
 echo " unix $name -b add,maximized_vert,maximized_horz -p 9812 KCalc";
 echo " unix $name -b add,hidden -i -e kcalc -c 'kcalc.kcalc' -h $(hostname) 'KCalc'";
 echo " unix $name -b remove,hidden -b remove,maximized_vert,maximized_horz -e kcalc KCalc";
};

declare opt_i="";
declare host=".*";
declare pids=".*";
declare class=".*";
declare -i opt_m=0;
declare -a starg=();
declare -i numops=0;
declare -i numerr=0;

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_b(){
 if [ -n "$1" ]; then
  starg+=("$1");
 fi;
};

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

function ch_win(){
 local win="$1";
 if [ -n "$win" ]; then
  for arg in "${starg[@]}"; do
   if call wmctrl -i -r $win -b $arg; then let numops++; else let numerr++; fi;
  done;
 fi;
};

function handle_wnd(){
 case $1 in
  '') ;;
  0x*) ch_win "$1"; ;;
  *) fatal 1 "Error: invalid window handle $1."; ;;
 esac;
};

function handle_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 handle_wnd $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 handle_wnd $wnd; done; ;;
 esac;
 echo "$numops operation(s) done, $numerr error(s) found.";
 if [[ $numops -gt 0 ]] && [[ $numerr -eq 0 ]]; then return 0; else return 1; fi;
};

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;
 handle_wnds $(findwindow "$1" 2>/dev/null | awk '{print $1}' | xargs);
};

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"; ;;
   -b|-become|--become)             shift; do_opt_b "$1"; ;;
   -m|-multi|--multi)               let opt_m=1; ;;
   --)                              shift; handle_arguments "$@"; break; ;;
   -*)                              fatal 1 "Error: unknown option $1."; ;;
   *)                               handle_arguments "$@"; break; ;;
  esac;
  shift;
  fail_on_empty "$@";
 done;
};

main "$@";

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