#!/bin/bash

#$1 is an action, like install
#$2 is app name, like Arduino

DIRECTORY="$(readlink -f "$(dirname "$0")")"

function error {
  echo -e "\e[91m$1\e[39m"
  exit 1
}

if [ -z "$1" ];then
  error "You need to specify an operation, and in most cases, which app to operate on."
fi

mkdir -p "${DIRECTORY}/data/status" "${DIRECTORY}/data/update-status"

#determine if host system is 64 bit arm64 or 32 bit armhf
if [ ! -z "$(file "$(readlink -f "/sbin/init")" | grep 64)" ];then
  arch=64
elif [ ! -z "$(file "$(readlink -f "/sbin/init")" | grep 32)" ];then
  arch=32
else
  error "Failed to detect OS CPU architecture! Something is very wrong."
fi

if [ "$1" == 'install' ];then
  #INSTALL
  #for this operation, a program name must be specified.
  if [ -z "$2" ];then
    error "For this operation, you must specify which app to operate on."
  elif [ ! -d "${DIRECTORY}/apps/$2" ];then
    error "${DIRECTORY}/apps/$2 does not exist!"
  fi
  #ensure an install script is not already running
  if ps -C install &>/dev/null || ps -C install-32 &>/dev/null || ps -C install-64 &>/dev/null;then       
    echo "An install script is already running.
Pi-Apps will wait until that one finishes before installing $2." | yad --text-info \
    --title="Waiting" --window-icon="${DIRECTORY}/icons/logo.png" --center --width=500 --height=100 \
    --button=OK!"${DIRECTORY}/icons/check.png":0 --fontname=12 --timeout=10 --timeout-indicator=top
    while ps -C install ;do sleep 1; done
  fi
  
  #if already installed then ask for confirmation
  if [ "$(cat "${DIRECTORY}/data/status/${2}" )" == 'installed' ];then
    yad --text="$2 is already installed. Are you sure you want to install it again?" \
    --text-align=center --center --title='Quick question' --window-icon="${DIRECTORY}/icons/logo.png" \
    --button=No!"${DIRECTORY}/icons/exit.png":1 --button=Yes!"${DIRECTORY}/icons/check.png":0 || exit 0
  fi
  
  #analytics
  if [ "$(cat "${DIRECTORY}/data/settings/Enable analytics")" == 'Yes' ];then
    #this is used to let Botspot see the daily installs for each app.
    #Botspot created an individual bitly link for each app. When you install an app, the cooresponding bit.ly link is "clicked" by a headless chromium-browser instance.
    #Chromium accesses the bit.ly link, then exits.
    #Allowing analytics does not identify you, or any personal information about you. Botspot can only see the number of "clicks" per day, as well as the top 10 countries of origin.
    
    bitlylink="https://bit.ly/pi-apps-install-$(echo "$2" | tr -d ' ' | sed 's/[^a-zA-Z]//g')"
    chromium-browser --headless "$bitlylink" &>/dev/null &
  fi
  
  #determine which script to run
  if [ -f "${DIRECTORY}/apps/${2}/install-32" ] && [ $arch == 32 ];then
    installscript="${DIRECTORY}/apps/${2}/install-32"
  elif [ -f "${DIRECTORY}/apps/${2}/install-64" ] && [ $arch == 64 ];then
    installscript="${DIRECTORY}/apps/${2}/install-64"
  elif [ -f "${DIRECTORY}/apps/${2}/install" ];then
    installscript="${DIRECTORY}/apps/${2}/install"
  else
    error "It appears $2 does not have an install-${arch} script suitable for your ${arch}-bit OS."
  fi
  
  lxterminal --title="Installing $2 ${arch}-bit" -e "
    cd $HOME
    echo 'corrupted' > \"${DIRECTORY}/data/status/${2}\"
    if \"$installscript\" ; then
      echo 'installed' > \"${DIRECTORY}/data/status/${2}\"
      echo -en '\n\e[42m\e[30mCommand succeeded.\e[39m\e[49m\nClosing in 10 seconds.'
      sleep 10
    else
      echo -en '\n\e[41m\e[30mCommand failed!\e[39m\e[49m\nClose this window to exit.'
      read enter #technically you could press Enter to exit.
    fi"
  #wait until script is done before this command exits
  sleep 2
  while ps -C install &>/dev/null || ps -C install-32 &>/dev/null || ps -C install-64 &>/dev/null;do sleep 1; done
  
elif [ "$1" == 'uninstall' ];then
  #UNINSTALL
  #for this operation, a program name must be specified.
  if [ -z "$2" ];then
    error "For this operation, you must specify which app to operate on."
  elif [ ! -d "${DIRECTORY}/apps/$2" ];then
    error "${DIRECTORY}/apps/$2 does not exist!"
  fi
  #ensure an uninstall script is not already running
  if ps -C uninstall ;then       
    echo "An uninstall script is already running.
Pi-Apps will wait until that one finishes before starting this one." | yad --text-info \
    --title="Waiting" --window-icon="${DIRECTORY}/icons/logo.png" --center --width=500 --height=100 \
    --button=OK!"${DIRECTORY}/icons/check.png":0 --fontname=12 --timeout=10 --timeout-indicator=top
    while ps -C uninstall ;do sleep 1; done
  fi
  
  #if already uninstalled then ask for confirmation
  if [ "$(cat "${DIRECTORY}/data/status/${2}" )" == 'uninstalled' ];then
    yad --text="$2 is already uninstalled. Are you sure you want to uninstall it again?" \
    --text-align=center --center --title='Quick question' --window-icon="${DIRECTORY}/icons/logo.png" \
    --button=No!"${DIRECTORY}/icons/exit.png":1 --button=Yes!"${DIRECTORY}/icons/check.png":0 || exit 0
  fi
  
  lxterminal --title="Uninstalling $2" -e "
    cd $HOME
    echo 'corrupted' > \"${DIRECTORY}/data/status/${2}\"
    if \"${DIRECTORY}/apps/${2}/uninstall\" ; then
      echo 'uninstalled' > \"${DIRECTORY}/data/status/${2}\"
      echo -en '\n\e[42m\e[30mCommand succeeded.\e[39m\e[49m\nClosing in 10 seconds..'
      sleep 10
    else
      echo 'corrupted' > \"${DIRECTORY}/data/status/${2}\"
      echo -en '\n\e[41m\e[30mCommand failed!\e[39m\e[49m\nClose this window to exit.'
      read enter #technically you could press Enter to exit.
    fi"
  #wait until script is done before this command exits
  sleep 2
  while ps -C uninstall >/dev/null;do sleep 1; done
elif [ "$1" == 'update' ];then
  #UPDATE
  #for this operation, a program name must be specified.
  if [ -z "$2" ];then
    error "For this operation, you must specify which app to operate on."
  fi
  
  #HIDDEN FEATURE - if $3 equals nofetch, then don't download github repo. Useful for updating multiple apps at maximum speed
  if [ "$3" == 'nofetch' ] && [ -d "${DIRECTORY}/update" ];then
    true
  else
    rm -rf "${DIRECTORY}/update" && mkdir "${DIRECTORY}/update" && cd "${DIRECTORY}/update" || error "failed to enter the update directory!"
    git clone "$(cat "${DIRECTORY}/etc/git_url")" || error "failed to clone repository!"
  fi
  
  newhash=$(find "${DIRECTORY}/update/pi-apps/apps/${2}" -type f -print0  | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}')
  oldhash=$(find "${DIRECTORY}/apps/${2}" -type f -print0 | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}')
  
  #detect which installation script exists and get the hash for that one
  
  if [ -f "${DIRECTORY}/update/pi-apps/apps/${2}/install-32" ];then
    scriptname='install-32'
  elif [ -f "${DIRECTORY}/update/pi-apps/apps/${2}/install-64" ];then
    scriptname='install-64'
  elif [ -f "${DIRECTORY}/update/pi-apps/apps/${2}/install" ];then
    scriptname='install'
  else
    error "No install script found for the $2 app! Please report this to Botspot."
  fi
  newinstallhash=$(sha1sum "${DIRECTORY}/update/pi-apps/apps/${2}/${scriptname}" | awk '{print $1}')
  oldinstallhash=$(sha1sum "${DIRECTORY}/apps/${2}/${scriptname}" | awk '{print $1}')
  
  #echo -e "newinstallhash: $newinstallhash\noldinstallhash: $oldinstallhash"
  #echo -e "newhash: $newhash\noldhash: $oldhash"
  
  if [ "$newhash" == "$oldhash" ];then
    echo "$2 is identical to the online version. Nothing to do!"
    exit 0
  else
    echo "$2 is not identical to the online version. Reinstalling..."
  fi
  #else
  installback=no
  #if install was changed
  if [ ! "$newinstallhash" == "$oldinstallhash" ];then
    #if installed already
    if [ "$(cat "${DIRECTORY}/data/status/${2}")" == 'installed' ];then
      installback=yes
      #uninstall it using a recursive script instance
      "${DIRECTORY}/manage" uninstall "$2"   
    fi
  fi
  
  #move old program to trash
  gio trash "${DIRECTORY}/apps/${2}" 2>/dev/null
  
  #failsafe
  [ -d "${DIRECTORY}/apps/${2}" ] && error "${DIRECTORY}/apps/${2} still exists, despite trying to delete it!"
  
  #copy new version to apps/
  cp -rf "${DIRECTORY}/update/pi-apps/apps/${2}" "${DIRECTORY}/apps/${2}"
  
  if [ "$installback" == 'yes' ] && [ "$(cat "${DIRECTORY}/data/settings/Reinstall after update")" != 'No' ];then
    echo "$2 was originally installed before updating. Reinstalling the new version now."
    #install it using a recursive script instance
    "${DIRECTORY}/manage" install "$2"
  fi
  echo -e "\e[92m${2} was updated successfully.\e[39m"
elif [ "$1" == 'check-all' ];then
  #CHECK-ALL
  #for this operation, a program name cannot be specified.
  
  #hidden flag: if $2 is 'installedonly', then only check for updates for those apps that are installed
  if [ "$2" == 'installedonly' ];then
    installedonly=1
  else
    installedonly=0
  fi
  
  rm -rf "${DIRECTORY}/update" && mkdir "${DIRECTORY}/update" && cd "${DIRECTORY}/update" || error "failed to enter the update directory!"
  git clone "$(cat "${DIRECTORY}/etc/git_url")" || error "failed to clone repository!"
  
  #generate entire app list, combine local apps and online apps to one list
  applist="$(echo -e "$(ls "${DIRECTORY}/update/pi-apps/apps")\n$(ls "${DIRECTORY}/apps")" | sort | uniq)"
  if [ $installedonly == 1 ];then
    #installedonly flag enabled. Remove apps that are uninstalled
    echo "installedonly flag set to 1" 1>&2
    PREIFS="$IFS"
    IFS=$'\n'
    for app in $applist
    do
      if [ ! -f "${DIRECTORY}/data/status/${app}" ] || [ "$(cat "${DIRECTORY}/data/status/${app}")" == 'uninstalled' ];then
        #if app is uninstalled, then remove it from the list.
        applist="$(echo "$applist" | grep -vx "$app")"
        echo "Removing ${app} from list because it is uninstalled." 1>&2
      fi
    done
    IFS="$PREIFS"
  fi
  
  applist="$(echo "$applist" | tr '\n' '|')"
  
  #echo "App list: $applist" 1>&2
  
  updatable=''
  PREIFS="$IFS"
  IFS="|"
  for app in $applist
  do
    #echo "app: $app"
    newhash=$(find "${DIRECTORY}/update/pi-apps/apps/${app}" -type f -print0 2>/dev/null | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}')
    oldhash=$(find "${DIRECTORY}/apps/${app}" -type f -print0 2>/dev/null | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}')
    #echo -e "newhash: $newhash\noldhash: $oldhash" 1>&2
    
    if [ "$newhash" == "$oldhash" ];then
      echo -e "${app} is identical\e[90m to the online version. Nothing to do!\e[39m" 1>&2
      echo 'latest' > "${DIRECTORY}/data/update-status/${app}"
    else
      if [ ! -d "${DIRECTORY}/apps/${app}" ];then
        echo -e "\e[97m${app} does not exist locally.\e[39m Adding to updatable list." 1>&2
        echo 'new' > "${DIRECTORY}/data/update-status/${app}"
        #in this case, add to updatable list
        updatable="${updatable}|${app}"
      elif [ ! -d "${DIRECTORY}/update/pi-apps/apps/${app}" ];then
        echo -e "\e[97m${app} only exists locally.\e[39m Will not add to updatable list." 1>&2
        echo 'local' > "${DIRECTORY}/data/update-status/${app}"
        #in this case, do not add to updatable list
      else
        echo -e "\e[97m${app} exists in both locations, but online version is newer\e[39m. Adding to updatable list." 1>&2
        echo 'updatable' > "${DIRECTORY}/data/update-status/${app}"
        #in this case, add to updatable list
        updatable="${updatable}|${app}"
      fi
      
    fi
  done 
  IFS="$PREIFS"
  
  #remove initial '|' character
  updatable="${updatable:1}"
  
  if [ -z "$updatable" ];then
    updatable='.'
  fi
  
  echo -e "\e[97mThese apps can be updated:\n${updatable}"
elif [ "$1" == 'update-all' ];then
  #UPDATE-ALL
  #for this operation, a program name cannot be specified.
  PREIFS="$IFS"
  IFS='|'
  updatable="$("${DIRECTORY}/manage" check-all | tail -1)"
  echo "Updatable: ${updatable}EOU"
  for updateapp in $updatable
  do
    echo "updating $updateapp"
    #update it using a recursive script instance
    echo "${DIRECTORY}/manage update $updateapp"
    "${DIRECTORY}/manage" update "$updateapp" || exit 1
  done 
  IFS="$PREIFS"
  echo -e '\e[92mOperation completed successfully!\e[39m'
else
  error "Did not understand ${1}. It must be either "\'"install"\'", "\'"uninstall"\'", "\'"update"\'", "\'"check-all"\'", or "\'"update-all"\'"."
fi