#!/bin/bash

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

###########################################################
## Utility to check dependecies of crwkit/crwdaq software.
###########################################################

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

#####################
# print help on usage
#####################
function usage(){
 local name="$scriptname";
 echo "$name version 1.0 from 20240307";
 if [ "$1" = "0" ]; then return; fi;
 echo "Copyright (c) 2024 Alexey Kuryakin daqgroup@mail.ru";
 echo "crwdep: crwkit dependencies checker.";
 echo "Usage:";
 echo " $name -h,--help    - show help";
 echo " $name --version    - show version";
 echo " $name -l,--list    - list dependencies";
 echo " $name -c,--check   - check dependencies";
 echo " $name -i,--install - install missed packages";
 echo " $name --history    - dpkg install history (list all)";
 echo " $name --log        - dpkg install history (from log)";
 echo "Examples:";
 echo " $name -c";
 echo " $name -l";
 echo " $name -i";
};

###############################
# program constants & variables
###############################
let fatal_notify_uses=0; # switch off notifications
declare -A mdebs;        # map of deb packages
declare -A muses;        # list of declared packages
declare -a ldeps=();     # list of dependencies
declare -a lgood=();     # list of Installed
declare -a lfail=();     # list of Not_Found
declare -i ngood=0;      # count found   packages
declare -i nfail=0;      # count missed  packages
declare -i nsumm=0;      # count checked packages

##############################################################
# include DEB packages map file produced by using --map option
##############################################################
function add_debmap(){ if [[ -n "$1" ]] && [[ -n "$2" ]]; then mdebs["$1"]="$2"; fi; };
source $scriptHOME/$scriptbase.in || fatal 1 "Error: not found source file: $scriptbase.in";

###############################
# print deb package descritpion
###############################
function deb_description(){
 if [ -n "$1" ]; then
  local line="$(dpkg -s "$1" 2>/dev/null | grep 'Description:' | sed 's/.*:\s*//' | tr "\140" "'")";
  if [ -n "$line" ]; then echo "$line"; return 0; fi;
  line="$(apt show "$1" 2>/dev/null | grep 'Description:' | sed 's/.*:\s*//' | tr "\140" "'")";
  if [ -n "$line" ]; then echo "$line"; return 0; fi;
 fi;
};

############################
# add dependency table of $1
# package or program:package
############################
function add_deps(){
 while [ -n "$1" ]; do
  local exe="$(echo "$1" | cut -f 1 -d ':')";
  local deb="$(echo "$1" | cut -f 2 -d ':')";
  if [ -n "$exe" ] && [ -n "$deb" ]; then
   if [ "${muses[$deb]}" = "1" ]; then return 0; fi;
   local com="${mdebs[$deb]}";
   if [ -z "$com" ]; then com="$(deb_description $deb)"; fi;
   ldeps+=("$exe" "$deb" "${com:-Package $deb}");
   muses[$deb]=1;
  fi;
  shift;
 done;
};

##############################
# create table of dependencies
##############################
function create_deps(){
 note "Archivators/installers.";
 ###############################
  add_deps makeself;
  add_deps zip;
  add_deps unzip;
  add_deps xz:xz-utils;
  add_deps 7za:p7zip;
 note "Duplicate files finder.";
 ###############################
  add_deps fdupes;
  add_deps jdupes;
 note "Useful user utilities.";
 ##############################
  add_deps gucharmap;
  add_deps kcalc;
  add_deps fontconfig;
 note "Image view/edit.";
 ########################
  add_deps gwenview;
  add_deps kolourpaint;
  add_deps imagemagick;
  add_deps graphicsmagick;
  add_deps ffmpeg;
  add_deps qmmp;
  add_deps vlc;
 note "COM port tools.";
 #######################
  add_deps socat;
  add_deps setserial;
  add_deps libmodbus5;
  add_deps mbpoll;
  add_deps minicom;
  add_deps gtkterm;
  add_deps cutecom;
 note "Common dialogs.";
 #######################
  add_deps zenity-common;
  add_deps zenity;
  add_deps dialog;
 note "Web server tools.";
 #########################
  add_deps /usr/sbin/lighttpd:lighttpd;
  add_deps spawn-fcgi;
 note "Text Editors/Viewers.";
 #############################
  add_deps kate;
  add_deps atom;
  add_deps geany;
  add_deps okular;
  add_deps xchm;
 note "Window manager tools.";
 #############################
  add_deps xdg-user-dirs;
  add_deps xdg-utils;
  add_deps wmctrl;
 note "File/process tools.";
 ###########################
  add_deps htop;
  add_deps csvtool;
  add_deps stress;
  add_deps strace;
  add_deps wdiff;
  add_deps diffstat;
  add_deps at;
 note "Terminal tools";
 ######################
  add_deps xterm;
  add_deps tmux;
 note "Network tools.";
 ######################
  add_deps remmina;
  add_deps vino;
  add_deps samba;
  add_deps winbind;
  add_deps smbclient;
  add_deps fly-admin-samba;
  add_deps samba-testsuite;
  #add_deps astra-sambadc fly-admin-ad-client;
  add_deps fly-admin-bluetooth;
  #add_deps fly-admin-bind;
  #add_deps fly-admin-dhcp;
  add_deps fly-admin-ftp;
 note "File managers.";
 ######################
  add_deps doublecmd:doublecmd-gtk;
  add_deps fly-fm;
 note "Text encode/decode/charset.";
 ###################################
  add_deps iconv:libc-bin;
  add_deps dos2unix;
  add_deps enca;
 note "Web/HTML/Markdown";
 #########################
  add_deps markdown;
  add_deps pandoc;
  add_deps firefox;
  add_deps firefox:firefox-astra;
  add_deps chromium;
 note "Debian package tools.";
 #############################
  add_deps dpkg;
  add_deps debconf;
  add_deps debhelper;
  add_deps lintian;
  add_deps devscripts;
  add_deps debsums;
  add_deps fakeroot;
 note "Notification system.";
 ############################
  add_deps daqgroup-dunst;
  add_deps qtnotifydaemon;
  #add_deps fly-notifications;
  #add_deps notification-daemon;
 note "Required for DIM usage/development.";
 ###########################################
  add_deps libmrm4;
  add_deps libxm4;
  add_deps libxpm4;
  add_deps libxmu-dev;
  add_deps libjpeg-dev;
  add_deps libmotif-common;
  add_deps libmotif-dev;
  add_deps dos2unix;
  add_deps tcsh;
  add_deps tcl;
  add_deps tk;
 note "Required for RHVoice";
 ############################
  add_deps scons;
  add_deps git;
  add_deps pkg-config;
  add_deps g++;
  add_deps gcc;
  add_deps expat;
  add_deps speech-dispatcher;
  add_deps flite;
  add_deps speech-dispatcher-flite;
  add_deps espeak-ng;
  add_deps flite1-dev;
  add_deps libunistring-dev;
  add_deps libsox-dev;
  add_deps libglibmm-2.4-dev;
  add_deps libao-dev;
  add_deps libpulse-dev;
  add_deps libspeechd-dev;
  add_deps portaudio19-dev;
 note "ROOT required packages.";
 ###############################
  add_deps dpkg-dev;
  add_deps cmake;
  add_deps g++;
  add_deps gcc;
  add_deps binutils;
  add_deps libx11-dev;
  add_deps libxpm-dev;
  add_deps libxft-dev;
  add_deps libxext-dev;
  add_deps python3;
  add_deps libssl-dev;
 note "ROOT optional packages.";
 ###############################
  add_deps gfortran       ;
  add_deps libpcre3-dev;
  #add_deps xlibmesa-glu-dev;
  add_deps libglu1-mesa-dev;
  add_deps libglew-dev;
  add_deps libftgl-dev;
  #add_deps libmysqlclient-dev;
  add_deps default-libmysqlclient-dev;
  add_deps libmariadbclient-dev-compat;
  add_deps libmariadbclient-dev-compat;
  add_deps libfftw3-dev;
  #add_deps libcfitsio-dev;
  add_deps graphviz-dev;
  add_deps libavahi-compat-libdnssd-dev;
  add_deps libldap2-dev;
  add_deps python3-dev;
  add_deps python3-numpy;
  add_deps libxml2-dev;
  add_deps libkrb5-dev;
  add_deps libgsl-dev qtwebengine5-dev;
  #add_deps nlohmann-json3-dev;
 note "Reguired for Lazarus";
 ############################
  add_deps make;
  add_deps binutils;
  add_deps build-essential;
  add_deps gdb;
  add_deps subversion;
  add_deps zip;
  add_deps unzip;
  add_deps libx11-dev;
  add_deps libgtk2.0-dev;
  add_deps libgdk-pixbuf2.0-dev;
  add_deps libcairo2-dev;
  add_deps libpango1.0-dev;
 note "Reguired for crwdaq";
 ###########################
  add_deps dns:daqgroup-dim;
  add_deps smiSM:daqgroup-smi;
  add_deps CrossMachine:daqgroup-diesel;
  add_deps RHVoice-test:daqgroup-rhvoice;
  add_deps daqgroup-root;
  add_deps gnuplot;
  add_deps busybox;
 note "Log viewers.";
 ####################
  add_deps glogg;
  add_deps klogg;
 note "Emulators.";
 ##################
  add_deps wine:wine-staging;
  add_deps winehq-staging;
  add_deps winetricks;
 note "e-library, e-books.";
 ###########################
  add_deps bibutils;
  #add_deps myrulib;
  add_deps cr3;
 note "Printing tools.";
 #######################
  add_deps fly-admin-printer;
  add_deps libpaper-utils;
  add_deps printer-driver-cups-pdf;
 note "Development tools.";
 ##########################
  add_deps meson;
  add_deps ninja-build;
  add_deps libwayland-bin;
  add_deps libwayland-dev;
  add_deps wayland-protocols;
  add_deps libsystemd-dev;
  add_deps libuwac0-0;
 note "Other tools.";
 #######################
  add_deps gnome-accessibility-themes;
  add_deps gnome-themes-extra;
  add_deps gnome-themes-standard;
 note "Reserved.";
 #################
  add_deps ;
  add_deps ;
  add_deps ;
};

#######################
# list dependency table
#######################
function list_deps(){
 local n=0;
 while [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ]; do
  if [ $n -eq 0 ]; then
   unix printc -c:+1f -f:%-28s "Program" -c:1b -f:%-28s "Package" -c:17 -f:%-60s "Comment" -n;
  fi;
  unix printc -c:+0f -f:%-28s "$1" -c:0b -f:%-28s "$2" -c:0 "\"$3\"" -n;
  shift; shift; shift;
  let n++;
 done;
};

###################################
# check executable file $1 is exist
###################################
function exe_found(){
 if unix which $1 >/dev/null 2>&1; then return 0; else return 1; fi;
};

###################################
# check deb package $1 is installed
###################################
function deb_found(){
 local status="$(dpkg -s $1 2>/dev/null | grep -iP "Status:\s*install\s*ok\s*installed")";
 if [ -n "$status" ]; then return 0; else return 1; fi;
};

###################
# check dependecies
# $1 executable
# $2 deb package
# $3 comment
###################
function check_deps(){
 local n=0;
 while [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ]; do
  if [ $n -eq 0 ]; then
   unix printc -c:+1f -f:%-28s "Program" -c:1b -f:%-28s "Package" -c:17 -f:%-9s "Status" -f:%-60s "Comment" -n;
  fi;
  local exe="$1"; local deb="$2"; local com="$3";
  local col="+0a"; local status="";
  if exe_found $exe || deb_found $deb; then
   let ngood++; col="+0a";
   status="installed";
   lgood+=($exe);
  else
   let nfail++; col="+0c";
   status="not_found";
   if [ "$exe" = "$deb" ]; then
    lfail+=("$exe");
   else
    lfail+=("$(echo $exe:$deb | sed 's/_://g')");
   fi;
  fi;
  unix printc -c:+0f -f:%-28s "$exe" -c:0b -f:%-28s "$deb" -c:$col "$status" -c:0 "\"${com:-Package $deb}\"" -n;
  shift; shift; shift;
  let nsumm++;
  let n++;
 done;
 if [ $nfail -eq 0 ]; then
  unix printc -c:+0a "checked:$nsumm," "installed:$ngood," "not_found:$nfail" -n;
  return 0;
 else
  unix printc -c:+0c "checked:$nsumm, installed:$ngood, not_found:$nfail" -n;
  unix printc -c:+0c "not_found: ${lfail[*]}" -n;
  return 1;
 fi;
};

function zen_checklist(){
 if [ $# -gt 0 ]; then
  zenity --title "$(langstr ru "Установка пакетов" en "Install packages") …" \
         --text "$(langstr ru "Выбирете пакеты для установки" en "Choose packages to install") …" \
         --timeout 180 --width 500 --height 600 \
         --list --checklist --print-column 2 --separator=" " \
         --column "$(langstr ru "Флаг" en "Flag")" --column "$(langstr ru "Пакет" en "Package")" \
         "$@" 2>/dev/null;
 fi;
};

function install_missed_deps(){
 check_deps "${ldeps[@]}";
 local checklist=();
 local selection="";
 for p in ${lfail[*]}; do checklist+=(true $p); done;
 if [ ${#checklist[@]} -gt 0 ]; then
  selection="$(zen_checklist ${checklist[@]})";
 fi;
 if [ -n "$selection" ]; then
  sudo apt install $selection;
 fi;
};

####################
# delete files in $*
####################
function delete_files(){
 while [ -n "$1" ]; do
  if [ -e "$1" ]; then rm -f "$1"; fi;
  shift;
 done;
};

##########################################################################################################
# https://unix.stackexchange.com/questions/12578/list-packages-on-an-apt-based-system-by-installation-date
##########################################################################################################
#############################################
# list deb package history from dpkg database
#############################################
function list_deb_install_history(){
 list1="$(mktemp)"; list2="$(mktemp)";
 find /var/lib/dpkg/info -name "*.list" -exec stat -c $'%n\t%y' {} \; | sed -e 's,/var/lib/dpkg/info/,,' -e 's,\.list\t,\t,' | sort > $list2;
 dpkg --get-selections | sed -ne '/\tinstall$/{s/[[:space:]].*//;p}' | sort > $list1;
 join -1 1 -2 1 -t $'\t' $list1 $list2 | awk '{print $2,$3,$4,$1}' | sort | sed 's/\.[[:digit:]]* [+-][[:digit:]]* / /';
 delete_files $list1 $list2;
};
##########################################
# list deb pachages history from log files
##########################################
function list_deb_log_history(){
 for x in $(ls -1t /var/log/dpkg.log*); do zcat -f $x | tac | grep -e " install " -e " upgrade " -e " remove "; done | awk -F ":a" '{print $1 " :a" $2}' | column -t | tac;
};

function list_deb_map(){
 for p in $(list_deb_install_history | awk '{print $3}' | sort | uniq | xargs); do
  printf " add_debmap %-48s \"%s\";\n" "$p" "$(deb_description $p)";
 done;
};

###############
# main function
###############
function main(){
 create_deps;
 case $1 in
  -version|--version)    usage 0; exit 0; ;;
  -h|-help|--help)       usage 1; exit 0; ;;
  -l|-list|--list)       list_deps "${ldeps[@]}"; ;;
  -c|-check|--check)     check_deps "${ldeps[@]}"; ;;
  -i|-install|--install) install_missed_deps "${ldeps[@]}"; ;;
  -history|--history)    list_deb_install_history; ;;
  -log|--log)            list_deb_log_history; ;;
  -map|--map)            list_deb_map; ;;
  '')                    fatal 1 "Error: empty arguments. Type $scriptname -h for help."; ;;
  -*)                    fatal 1 "Error: wrong option $1. Type $scriptname -h for help."; ;;
  *)                     fatal 1 "Error: unexpected parameter $1. Type $scriptname -h for help."; ;;
 esac;
};

main "$@";

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