#!/bin/bash
#
# vmbinstallpkg
#
# Given a target package to install, include a list of dependent packages.
# We assume either:
#     - all dependent packages were created when building the target
#       i.e. all packages are available locally in directory REPODIR
#  or:
#     - all packages are available from some networked REPODIR
#
# Check with user before (re)installing all the packages listed in a dialog screen.

# ! Requires: hoorex > 0.10.1
#
if [ "$(printf "0.10.1\n%s\n" "$(hoorex --version)" |sort -V |tail -1)" = "0.10.1" ]; then
  echo "This version of HooRex ($(hoorex --version)) is not usable here."
  echo "Please update to hoorex-0.10.2 or later."
fi
HOOREX=/usr/bin/hoorex

# vmb{install,remove}pkg version
#
VERSION=%VERSION%

# The vmbinstallpkg command requires a single argument;
# either
#    the name of a package
#    or the -f (--force-update) directive
#    or the -v (--version) option
#
TARGETPKG=$1
[ -z "$TARGETPKG" ] && {
  echo "No target package supplied. Exiting now ..."
  exit 1
}

SL_RELEASE=${SL_RELEASE:-$(cut -d' ' -f 2 < /etc/slackware-version)}
SL_ARCH=${SL_ARCH:-$(uname -m)}
#echo "$SL_RELEASE $SL_ARCH"

# vmbinstallpkg's home
#
VMBINST_HOME="$HOME/.local/share/vmbinstallpkg"

# Temporary directory to do work
#
VMB_TEMPDIR=$(mktemp -d -t .vmb_tmp_dir-XXXXXX)
#echo "Created $VMB_TEMPDIR"

# Retrieve default settings
# vmb{install,remove}pkg share the same defaults file
#
if [ -r /etc/default/vmbinstallpkg ]; then
  . /etc/default/vmbinstallpkg
fi

# REPODATA_DIR is where we keep any downloaded repoData.{pkl,stamp}
# for use by vmbinstallpkg
#
REPODATA_DIR="$VMBINST_HOME/$SL_RELEASE"
TEMP_REPODATA_DIR="$REPODATA_DIR/tmp"
mkdir -p "$TEMP_REPODATA_DIR"

# REPODATA_DIR is location of a downloaded repoData.{pkl,stamp},
# and REPODATA_AGE_CHECK is the interval after which we check that
# our downloaded version is up to date.
#REPODATA_AGE_CHECK="5 min ago"
REPODATA_AGE_CHECK=${REPODATA_AGE_CHECK:-"3 days ago"}
#echo "REPODATA_AGE_CHECK = $REPODATA_AGE_CHECK"

# HOOREX_DB is the directory location of the source of the hoorex database
# (repoData.pkl) we'll be copying for us to use in REPODATA_DIR.
# The default HOOREX_DB location is in directory ~/.local/share/hoorex
# but for special needs the location may be set using -p option for hoorex e.g.
#     hoorex -f -p /var/cache/vmbuilder/.hoorex/15.0
# HOOREX_DB could be set at the command line to match any special location we
# want e.g.
#     HOOREX_DB=/var/cache/vmbuilder/.hoorex/15.0 vmbinstallpkg apackage
# but it's probably better/easier to set it in /etc/default/vmbinstallpkg.
# If not set there or left unset, set it here and copy to REPODATA_DIR.
#
[ -z "$HOOREX_DB" ] && HOOREX_DB=$HOME/.local/share/hoorex


download_repoData() {
# $1 should be source of latest repoData - some network location
# $2 should be destination for the new repoData
  #echo "download_repoData(): downloading data from $1 into $2"
  if [ -d "$2" ]; then
    cd "$2" || {
      mkdir -p "$2"
      cd "$2" || {
        echo "Can't cd into $2. Exiting now"
        exit 1
      }
    }
  fi
  #curl --silent -O "$HOOREX_DB"/repoData.stamp -O "$HOOREX_DB"/repoData.pkl
  curl -O "$HOOREX_DB"/repoData.stamp -O "$HOOREX_DB"/repoData.pkl
}
check_download_repoData() {
# $1 should be source of latest repoData - some network location
# $2 should be  pre-check destination for the downloaded repoData
# $3 should be post-check destination for the new repoData
  #echo "check_download_repoData(): downloading data from $1 into $2"
  if [ -d "$2" ]; then
    cd "$2" || {
      mkdir -p "$2"
      cd "$2" || {
        echo "Can't cd into $2. Exiting now"
        exit 1
      }
    }
  fi
  #curl --silent -O "$HOOREX_DB"/repoData.stamp
  curl -O "$HOOREX_DB"/repoData.stamp

  # Check whether new repoData is more recent that what we already have (in $3)
  local cur_stamp new_stamp
  cur_stamp=$(cat "$REPODATA_DIR"/repoData.stamp)
  new_stamp=$(cat "$2"/repoData.stamp)
  #echo "cur_stamp = $cur_stamp, new_stamp = $new_stamp"
  if (( $(echo "$new_stamp > $cur_stamp" |bc -l) )); then
    echo "Updating dependencies!"
    if [ -d "$3" ]; then
      cd "$3" || {
        echo "Can't cd into  $3. Exiting now"
        exit 1
      }
    fi
    #curl --silent -O "$HOOREX_DB"/repoData.stamp -O "$HOOREX_DB"/repoData.pkl
    curl -O "$HOOREX_DB"/repoData.stamp -O "$HOOREX_DB"/repoData.pkl
    echo "repoData updated"
  else
    echo "Current repoData is already up to date"
  fi
}


if [ "$TARGETPKG" = "-v" ] || [ "$TARGETPKG" = "--version" ]; then
  echo "$VERSION"
  exit
fi

# If repoData update forced at command line, just do it
# directly to REPODATA_DIR
if [ "$TARGETPKG" = "-f" ] || [ "$TARGETPKG" = "--force-update" ]; then
  if [[ "$HOOREX_DB" == "http"* ]]; then
    echo "Downloading repoData from $HOOREX_DB"
    download_repoData "$HOOREX_DB" "$REPODATA_DIR"

    # Confirm download was OK
    if [ -f "$REPODATA_DIR"/repoData.pkl ] && [ -f "$REPODATA_DIR"/repoData.stamp ]; then
      echo "Updated OK"
    else
      echo "Attempted update failed - $REPODATA_DIR/repoData.pkl or $REPODATA_DIR/repoData.stamp is missing after forced update"
      exit 1
    fi
  else
    echo "Don't know where to update repoData from. Set HOOREX_DB in /etc/defaults/vmbinstallpkg"
    echo "Exiting now"
    exit 2
  fi

  # Update REPODIR if it is remote
  if [[ "$REPODIR" == "http"* ]]; then
    cd "$VMBINST_HOME"/"$SL_RELEASE" || {
      echo "Can't cd to $VMBINST_HOME/$SL_RELEASE. Exiting now ..."
      exit
    }
    echo "Downloading PACKAGES.TXT from $REPODIR"
    #curl --silent -O "$REPODIR"/PACKAGES.TXT
    curl -O "$REPODIR"/PACKAGES.TXT
    result=$?
    if [ "$result" != "0" ]; then
      echo "Couldn't find PACKAGES.TXT in package repo at $REPODIR. Exiting now ..."
      exit
    fi
  fi    
  exit
fi

# If repoData originates remotely, check our local version is synced with it
#
if [[ "$HOOREX_DB" == "http"* ]]; then

  if [ -f "$REPODATA_DIR"/repoData.pkl ] && [ -f "$REPODATA_DIR"/repoData.stamp ]; then
    # We have repoData already but don't download every time - only if last download > REPODATA_AGE_CHECK ago
    [[ $(date +%s -r "$REPODATA_DIR"/repoData.stamp) -lt $(date +%s --date="$REPODATA_AGE_CHECK") ]] && {
      echo "Refreshing repoData"
      check_download_repoData "$HOOREX_DB" "$TEMP_REPODATA_DIR" "$REPODATA_DIR"
    }
  else
    # We don't have repoData.stamp so download it
    echo "No valid repoData so downloading"
    download_repoData "$HOOREX_DB" "$REPODATA_DIR"
  fi
else
  # Not network based i.e. using some local directory already loaded with repoData.{pkl,stamp}
  # and that directory specified in /etc/default/vmbinstallpkg or in environment

  # If HOOREX_DB has a relative address, it is relative to HooRex's default ($HOME/.local/share/hoorex)
  if [[ ! "$HOOREX_DB" = "/"* ]]; then
      echo "Relative"
    REPODATA_DIR=$HOME/.local/share/hoorex/$HOOREX_DB
  else
    echo "Absolute"
    REPODATA_DIR=$HOOREX_DB
  fi
fi

# Is REPODATA_DIR populated with something?
#
[ ! -f "$REPODATA_DIR/repoData.pkl" ] || [ ! -f "REPODATADIR_DIR/repoData.stamp" ] || {
  echo "        !!! ERROR !!! REPODATA_DIR is not populated correctly"
  echo "        Please set HOOREX_DB in /etc/default/vmbinstallpkg or set at the command line"
  echo "        HOOREX_DB should be set to the path of the directory containing existing hoorex database"
  echo "            or a remote location accessed by http"
  exit 3
}
#echo "REPODATA_DIR = $REPODATA_DIR"


# REPODIR must be set to some directory or remote location containing installable packages
# If REPODIR not set (in either /etc/default/vmbinstallpkg or environment), set it to some default (/tmp)
#
[ -z "$REPODIR" ] && REPODIR=/tmp
echo "REPODIR = $REPODIR"

# Similar to REPODATA_AGE_CHECK for HOOREX_DB,
# for a networked REPODIR i.e. using http/https,
# REPODIR_AGE_CHECK is the interval after which we
# check that any downloaded PACKAGES.TXT is up to date.
#REPODIR_AGE_CHECK="5 mins ago"
REPODIR_AGE_CHECK=${REPODIR_AGE_CHECK:-"24 hours ago"}
#echo "REPODIR_AGE_CHECK = $REPODIR_AGE_CHECK"


# Search for a package name in PACKAGES.TXT and, if found,
# use it's reported location in the repo to download.
#
download_package_from_repo() {
# $1 is name of package to download
  mkdir -p "$VMB_TEMPDIR"/packages/"$SL_ARCH"/"SL_RELEASE"
  cd "$VMB_TEMPDIR"/packages/"$SL_ARCH"/"SL_RELEASE" || {
    echo "Couldn't create directory to download packages. Exiting now ..."
    exit
  }

  # For any given package name, grep could produce multiple hits
  # e.g. looking for pyside2, grep also finds pyside2-tools
  #
  echo "Looking for $1"
  grep -n "$1"  "$VMBINST_HOME"/"$SL_RELEASE"/PACKAGES.TXT | sed 's/[[:blank:]]//g' | while read -r package_entry ; do
    # grep -n will return linenumber:guff:package
    # e.g. 1001:PACKAGENAME:pyside2-5.15.17-x86_64-1_uqv.txz
    #echo "package_entry = $package_entry"
    if [ "$(echo "$package_entry" |cut -d':' -f2)" != "PACKAGENAME" ]; then
      # Only consider results whose second field is "PACKAGENAME"
      continue
    fi
    package_name=$(echo "$package_entry" |cut -d':' -f3)
    # Check package_name is exactly matched rather than nearly matched
    exact_package_name="$(basename "$package_name" | rev | cut -d '-' -f 4- | rev)"
    #echo "exact_package_name = $exact_package_name"
    if [[ "$1" == "$exact_package_name" ]]; then
      #echo "Found package entry: $package_name"
      package_name_line=$(echo "$package_entry" |cut -d':' -f1)
      #echo "package_name_line = $package_name_line"
      # The following line in PACKAGES.TXT contains the location
      package_location_line=$(sed -n "$(echo "$package_name_line" + 1|bc)p" "$VMBINST_HOME"/"$SL_RELEASE"/PACKAGES.TXT |sed 's/[[:blank:]]//g')
      package_location=$(echo "$package_location_line" |cut -d':' -f2)
      #echo "package_location = $package_location"
      echo "Downloading package: $package_name"
      curl -O "$REPODIR"/"$package_location"/"$package_name"
      #curl --silent -O "$REPODIR"/"$package_location"/"$package_name"
    #else
    #  echo "Ignoring $exact_package_name (not an exact match for $1)"
    fi
  done
}

# If packages to install originate remotely,
# check we have PACKAGES.TXT (or download one),
# then retrieve packages to local store,
# then repoint REPODIR at local store
#
if [[ "$REPODIR" == "http"* ]]; then
  cd "$VMBINST_HOME"/"$SL_RELEASE" || {
    echo "Can't cd to $VMBINST_HOME/$SL_RELEASE. Exiting now ..."
    exit
  }

  # If we already have a PACKAGES.TXT file available,
  # check that it's recent enough (use REPODIR_AGE_CHECK)
  # and that it comes from the current REPODIR setting
  # (which may have changed).
  #
  # Otherwise download a new PACKAGES.TEXT from REPODIR
  #
  packages_txt_is_valid=false
  if [ -f PACKAGES.TXT ] && [ -f PACKAGES.TXT.stamp ]; then
    if [ "$(cat PACKAGES.TXT.stamp)" = "$REPODIR" ]; then
      [[ $(date +%s -r PACKAGES.TXT.stamp) -lt $(date +%s --date="$REPODIR_AGE_CHECK") ]] || {
        packages_txt_is_valid=true
      }
    fi
  fi

  if ! $packages_txt_is_valid ; then
    echo "Downloading PACKAGES.TXT from $REPODIR"
    #curl --silent -O "$REPODIR"/PACKAGES.TXT
    curl -O "$REPODIR"/PACKAGES.TXT
    result=$?
    if [ "$result" != "0" ]; then
      echo "Couldn't find PACKAGES.TXT in package repo at $REPODIR. Exiting now ..."
      exit
    fi
    echo "$REPODIR" > PACKAGES.TXT.stamp
  fi

  #download_package_from_repo "$TARGETPKG"
  DEPS=$(${HOOREX} -r1 -p "$REPODATA_DIR" "$TARGETPKG")
  for dep in $DEPS ; do
    download_package_from_repo "$dep"
  done

  REPODIR="$VMB_TEMPDIR"/packages/"$SL_ARCH"/"SL_RELEASE"

fi


# Check we have the target package available
arr=()
for app in $(cd "$REPODIR" && ls); do
  # First find a loose match (do filenames start the same way?)
  if [[ "$app" == "$TARGETPKG"* ]]; then
    # Now confirm exact match
    shortname="$(basename "$app" | rev | cut -d '-' -f 4- | rev)";
    if [ "$shortname" = "$TARGETPKG" ]; then
      # We have target package available
      arr+=( "$app" )
    fi
  fi
done

# Without the target package available, we have nothing more to do
if [ ${#arr[@]} -le 0 ]; then
  echo "No $TARGETPKG package is available in $REPODIR to install on Slackware $SL_RELEASE"
  echo "Is REPODIR set correctly?"
  exit 5
fi

# Look for dependent packages
#
echo "Checking availability of $TARGETPKG and its dependencies."
arr=()
arr_not_found=()
DEPS=$(${HOOREX} -r1 -p "$REPODATA_DIR" "$TARGETPKG")
#echo $DEPS
for dep in $DEPS ; do
  echo -n "Checking $dep ... "
  found_dep=false
  for app in $(cd "$REPODIR" && ls); do
    # First find a loose match (do filenames start the same way?)
    if [[ "$app" == "$dep"* ]]; then
      # Now confirm exact match
      shortname="$(basename "$app" | rev | cut -d '-' -f 4- | rev)";
      if [ "$shortname" = "$dep" ]; then
        # We have target package available
        arr+=( "$app" )
        found_dep=true
        continue
      fi
    fi
  done
  if ! $found_dep ; then
    arr_not_found+=( "$dep" )
    echo "NOT FOUND"
  else
    echo OK
  fi
done

# If anything missing, ask what to do
#
if [ ${#arr_not_found[@]} -gt 0 ]; then
  echo; echo "CAUTION! Missing package(s)"
  for i in "${arr_not_found[@]}" ; do echo "Required dependent package $i not found" ; done
  read -r -p 'Continue or stop to fix missing package(s). Continue? [y/N]: ' user_continue
  user_continue=${user_continue,,[YESNO]}
  case $user_continue in
    y|ye|yes) echo "OK, continuing anyway" ;;
      *) echo "Good choice - exiting now." ; exit ;;
  esac
fi

# Generate input list for dialog
rm -f "$VMB_TEMPDIR"/dialog.tmp
echo -n "dialog --title  \"Slackware $SL_RELEASE Packages to install\" --checklist \"Choose packages to Install\" 19 70 13 " >"$VMB_TEMPDIR"/dialog.tmp
for i in "${arr[@]}" ; do echo -n "$i \"\" on " >> "$VMB_TEMPDIR"/dialog.tmp ; done

# Display dialog
#
. "$VMB_TEMPDIR"/dialog.tmp 2>"$VMB_TEMPDIR"/error.log

# Process dialog result
#
for i in $(tr -d \" < "$VMB_TEMPDIR"/error.log) ; do
  printf 'Installing %s\n' "$REPODIR/$i"
  sudo /sbin/upgradepkg --terse --install-new --reinstall "$REPODIR"/"$i"
done

# Cleanup
[ "$VMB_TEMPDIR" = "/" ] || {
  #echo "delete $VMB_TEMPDIR"
  rm -rf "$VMB_TEMPDIR"
}

