#!/bin/bash

#this generates a yad-friendly app list. This is run every time the gui script is executed.
#if this script detects nothing has changed since last run, then it will echo back the app list that was generated last time.

if [ -z "$DIRECTORY" ];then
  DIRECTORY="$(readlink -f "$(dirname "$0")")"
fi

mktimestamps() {
  #these directories are checked for changes
  checkfiles="${DIRECTORY}/apps
${DIRECTORY}/data/settings
${DIRECTORY}/data/status
${DIRECTORY}/etc
${DIRECTORY}/icons/categories
${DIRECTORY}/preload
${DIRECTORY}/api
${DIRECTORY}/data/category-overrides"
  local IFS=$'\n'
  for i in $checkfiles
  do
    printf "$i $(stat -c %Y "${i}" 2>/dev/null) "
    
    if [ -d "$i" ];then
      find "$i" -type f -printf '%T@ %p\n' | sort -rn | head -n1
    else
      printf '\n'
    fi
  done
}

[ "$1" == source ] && return 0

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

#yad or xlunch format
guimode="$1"

if [ -z "$guimode" ];then
  format=yad
elif [[ "$guimode" = xlunch* ]];then
  format=xlunch
elif [[ "$guimode" = yad* ]];then
  format=yad
elif [ "$guimode" != 'yad' ] && [ "$guimode" != 'xlunch' ];then
  error "Unknown list format '$format'!"
fi

#specifies a category to preload (can be left empty to load main page)
prefix="$2"

if [ "$prefix" == '/' ];then
  prefix=
fi

# if the timestamp in the settings folder has changed, we need to unset the text_color variable since the GTK_THEME may have changed and we need to re-generate it
timestamp_settings="$( cat ${DIRECTORY}/data/preload/timestamps- | grep "^${DIRECTORY}/data/settings" )"
timestamps="$(mktimestamps)"
echo "$timestamps" | grep -q "$timestamp_settings" || unset text_color

#For systems with older versions of yad, the text color column cannot be left blank. This python script determines the default text color from GTK bindings.
if [ -z "${text_color+x}" ] && [ "$format" == yad ];then
  #0400 is the latest version
  yad_version="$(zcat /usr/share/doc/yad/NEWS.gz | head -n 1 | tr -cd '0123456789\n')"
  if [ $yad_version -lt 0400 ]; then
    if command -v python3 &>/dev/null; then
      python_version="python3"
    else
      python_version="python2"
    fi
    #the GTK_THEME may not be current depending on when the API script was last sourced
    #to avoid setting the text to the wrong color, check it again now right
    #set the system GTK theme for yad windows
    guimode="$(cat "${DIRECTORY}/data/settings/App List Style" 2>/dev/null || echo yad-default)"
    if [ "$guimode" == yad-default ];then
      export GTK_THEME=''
    elif [[ "$guimode" = yad* ]];then
      export GTK_THEME=${guimode//yad-/}
    elif [ "$guimode" == xlunch-light-3d ];then
      export GTK_THEME=''
    elif [ "$guimode" == xlunch-dark-3d ];then
      export GTK_THEME=Adwaita-dark
    elif [ "$guimode" == xlunch-dark ];then
      export GTK_THEME=Adwaita-dark
    fi
    export text_color=$(echo "import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
tv = Gtk.TextView()
style = tv.get_style_context()
textcolor = style.get_color(Gtk.StateType.NORMAL)
print(Gdk.RGBA.to_string(textcolor))
" | $python_version -)
  else
    export text_color=""
  fi
fi

timestampfile="${DIRECTORY}/data/preload/timestamps-$(echo "$prefix" | tr -d '/')"
listfile="${DIRECTORY}/data/preload/LIST-$(echo "$prefix" | tr -d '/')"

mkdir -p "${DIRECTORY}/data/preload"

reloadlist=0

if [ -f "$timestampfile" ];then
  #get modified timestamps for files/directories in the pi-apps folder
  timestamps="$(mktimestamps)"
  
  if [ "$timestamps" == "$(cat "$timestampfile")" ];then
    #if current timestamps and saved timestamps match, then don't reload the list
    reloadlist=0
    echo "Timestamps match." 1>&2
  else
    #timestamps don't match, so reload the list
    reloadlist=1
    echo "Timestamps don't match" 1>&2
    #echo -e "original file: $(cat "$timestampfile")\nnew timestamp: $timestamps" 1>&2
  fi
else
  #timestamp file not found
  reloadlist=1
fi

if [ ! -f "$listfile" ] || [ ! -s "$listfile" ];then
  echo "list file for $prefix does not exist." 1>&2
  reloadlist=1
else
  list_length=$(wc -l "$listfile" | awk '{print $1}')
fi

if [ -n "$list_length" ] && [ "$format" == yad ] && (( $list_length % 5 )) ; then
  echo "yad lists are a multiple of 5, $prefix does not have a list a multiple of 5 so it may be corrupted and needs regenerating." 1>&2
  reloadlist=1
fi

#If updates available, show special Updates category (returned separately to avoid re-preloading after update-check finishes)
if [ -z "$prefix" ] && [ "$format" == yad ] && ([ -s "${DIRECTORY}/data/update-status/updatable-files" ] || [ -s "${DIRECTORY}/data/update-status/updatable-apps" ]);then
  #yad format
  echo "${DIRECTORY}/icons/categories/Updates.png
Updates
Updates/
Pi-Apps updates are available. Click here to update your apps.
$text_color"
elif [ -z "$prefix" ] && [ "$format" == xlunch ] && ([ -s "${DIRECTORY}/data/update-status/updatable-files" ] || [ -s "${DIRECTORY}/data/update-status/updatable-apps" ]);then
  #xlunch format
  echo "Updates;${DIRECTORY}/icons/categories/Updates-64.png;:exec "\""echo Updates/"\"""
fi

if [ $reloadlist == 1 ];then
  echo "Generating list for '$prefix'..." 1>&2
  
  #for app_prefix_categories() and app_status() functions
  if ! command -v app_prefix_category >/dev/null ;then
    source "${DIRECTORY}/api"
  fi
  
  if [ ! -z "$prefix" ];then
    echo "Showing apps within $prefix/" 1>&2
    vfiles="$(app_prefix_category "$prefix" | grep . | sort -f | uniq | sed "s+$prefix/++g")" #generate a virtual file system with apps in folders represented as subdirectories
  else
    vfiles="$(app_prefix_category "" | grep . | sort -f | uniq | grep -v '^hidden/')"
  fi
  
  #remove apps within categories - show this layer of stuff only.
  vfiles="$(sed 's+/.*+/+g' <<<"$vfiles" | uniq)"
  
  #get list of apps - excluding folders and apps that are incompatible with CPU architecture
  APPS="$(grep -v '/' <<<"$vfiles" | list_intersect "$(list_apps cpu_installable)")"
  
  #get list of folders - excluding apps.
  DIRS="$(grep '/' <<<"$vfiles" | tr -d '/')"
  
  #shuffle the list if enabled
  if [ "$(< "${DIRECTORY}/data/settings/Shuffle App list")" == 'Yes' ];then
    APPS="$(echo "$APPS" | shuf)"
    DIRS="$(echo "$DIRS" | shuf)"
  fi
  
  if [ "$format" == yad ];then
    IFS=$'\n'

    listfile_tmp="$listfile-$(mktemp -u | sed 's:/tmp/tmp.::g')"
    
    #initial value of listfile: if within a prefix, start with a Back button
    if [ ! -z "$prefix" ];then
      echo "${DIRECTORY}/icons/back.png
Back
$(dirname "$prefix" | sed 's+^\.$++g')/
Return to the previous location
$text_color" | tee "$listfile_tmp" &>/dev/null
    else
      echo -n '' > "$listfile_tmp"
    fi

    declare -A dir_lookup
    dir_lookup=( ["Browsers"]="Internet browsers." ["All Apps"]="All Pi-Apps Applications in one long list." ["Appearance"]="Applications and Themes which modify the look and feel of your OS." ["System Management"]="Apps that help you keep track of system resources and general system management." ["Games"]="Games and Emulators" ["Installed"]="All Pi-Apps Apps that you have installed." ["Internet"]="Browsers, Chat Clients, Email Clients, and so much more." ["Multimedia"]="Video playback and creation, audio playback and creation, and streaming alternatives." ["Packages"]="Simple Apps that install directly from APT repos." ["Tools"]="An assortment of helpful programs that don't already fit into another category." ["Terminals"]="Alternative terminal programs built for the modern age as well as to replicate your old vintage computer." ["Programming"]="Code editors, IDEs, and other applications to help you write and make other programs." ["Creative Arts"]="Drawing, Painting, and Photo and Movie Editors" ["Engineering"]="3D Printing slicers, CAD/modeling, and general design software" ["Office"]="Office suites (document and slideshow editors), and other office tools." ["Emulation"]="Applications that help you run non-ARM or non-Linux software." ["Communication"]="Internet messaging, calling, video chatting, and email clients.")
    for dir in $DIRS
    do
      if [ -f "${DIRECTORY}/icons/categories/${dir}.png" ];then
        diricon="${DIRECTORY}/icons/categories/${dir}.png"
      else
        diricon="${DIRECTORY}/icons/categories/default.png"
      fi

      if [ ! -z "$prefix" ];then
        add="$diricon
$dir
$prefix/$dir/
"${dir_lookup["$dir"]}"
$text_color"
      else
        add="$diricon
$dir
$dir/
"${dir_lookup["$dir"]}"
$text_color"
      fi
      add="${add//[&]/&amp;}"
      echo "$add"
    done >> "$listfile_tmp"
    #finished preloading categories
    
    #preload apps
    for app in $APPS
    do
      #get installation status of app
      unset status
      read -r 2>/dev/null status <"${DIRECTORY}/data/status/${app}"
      
      #determine app icon
      if [ -f "${DIRECTORY}/apps/${app}/icon-24.png" ];then
        add="${DIRECTORY}/apps/${app}/icon-24.png"$'\n'
      else
        add="${DIRECTORY}/icons/none-24.png"$'\n'
      fi
      
      #rest of list attributes
      if [ -z "$status" ];then
        read -r 2>/dev/null line <"${DIRECTORY}/apps/${app}/description" || line="Description unavailable"
        add+="$app
$prefix/$app
$line"$'\n'
      else
        read -r 2>/dev/null line <"${DIRECTORY}/apps/${app}/description" || line="Description unavailable"
        add+="$app
$prefix/$app
"\("$status"\)" $line"$'\n'
      fi
      
      #determine status text-color for app name (green for installed, red for uninstalled, yellow for corrupted)
      if [ -z "$status" ];then
        add+="$text_color"
      elif [ "$status" == installed ];then
        add+="#00AA00"
      elif [ "$status" == uninstalled ];then
        add+="#CC3333"
      elif [ "$status" == corrupted ];then
        add+="#888800"
      elif [ "$status" == disabled ];then
        add+="#FF0000"
      else
        # fallback incase unexpected status
        add+="$text_color"
      fi
      #output finished app lines
      add="${add//[&]/&amp;}"
      echo "$add"
    done >> "$listfile_tmp"

    # write to the pipe all at once instead of in breaking chunks. this prevents errors when user clicks the back button too quickly when the list file is still generating
    mv "$listfile_tmp" "$listfile"
    # specifically use cat here so that all data enters the pipe at once
    cat "$listfile"
    echo "Finished preload for '$prefix'" 1>&2
    #finished preloading apps
    
  elif [ "$format" == xlunch ];then
    #XLUNCH list format

    listfile_tmp="$listfile-$(mktemp -u | sed 's:/tmp/tmp.::g')"
    
    #initial value of listfile: if within a prefix, start with a Back button, or if on main page start with Updates
    if [ ! -z "$prefix" ];then
      echo "Back;${DIRECTORY}/icons/back-64.png;:exec "\""echo /"\""" | tee "$listfile_tmp" &>/dev/null
    else
      echo -n '' > "$listfile_tmp"
    fi
    
    IFS=$'\n'
    for dir in $DIRS
    do
      if [ -f "${DIRECTORY}/icons/categories/${dir}-64.png" ];then
        diricon="${DIRECTORY}/icons/categories/${dir}-64.png"
      else
        diricon="${DIRECTORY}/icons/categories/default-64.png"
      fi
      
      echo "${dir};${diricon};:exec "\""echo '${prefix}${dir}/'"\"""
    done >> "$listfile_tmp"
    
    for app in $APPS
    do
      if [ -f "${DIRECTORY}/data/status/${app}" ];then
        echo "${app} ($(while read line; do echo $line; done 2>/dev/null < "${DIRECTORY}/data/status/${app}"));${DIRECTORY}/apps/${app}/icon-64.png;:exec "\""echo '$prefix/${app}'"\"""
      else
        #status file missing - app has never been installed
        echo "${app};${DIRECTORY}/apps/${app}/icon-64.png;:exec "\""echo '$prefix/${app}'"\"""
      fi
    done >> "$listfile_tmp"

    # write to the pipe all at once instead of in breaking chunks. this prevents errors when user clicks the back button too quickly when the list file is still generating
    mv "$listfile_tmp" "$listfile"
    # specifically use cat here so that all data enters the pipe at once
    cat "$listfile"
    echo "Finished preload for $prefix" 1>&2
    
  fi
  
  #save timestamps to file too
  if [ -z "$timestamps" ];then
    timestamps="$(mktimestamps)"
  fi
  echo "$timestamps" > "$timestampfile"
else
  echo "Reading list file for '$prefix'..." 1>&2
  # specifically use cat here so that all data enters the pipe at once
  cat "$listfile"
fi

#preload all categories in background
if [ "$RUNNING_FROM_DAEMON" != 1 ];then
  "${DIRECTORY}/etc/preload-daemon" "$format" >/dev/null &
fi
