#!/bin/bash

#############################################
#		MINT-FM2-FUNCTION-LIBRARY
#--------------------------------------------
# Contributors: Shane Joe Lazar <shane AT archlinux DOT us>
# 		Kendall Tristan Weaver <kendalltweaver@gmail.com>
# Description: This is the function library file for mint-fm2, written for Linux Mint 9 Fluxbox CE.
# License: GPL v2 or later.
#		/MINT-FM2-FUNCTION-LIBRARY
############################################# 

#############################################
#		FUNCTIONS
#--------------------------------------------

# USAGE INSTRUCTIONS
utilsUsage () {
  echo "#--------------------------------------------"
  echo "# Usage: mint-fm2"
  echo "# 	mint-fm2 [generate|generate-gui|update|start|restart|stop|debug]"
  echo "#"
  echo "# - generate: Generates a new menu tree while preserving user changes."
  echo "# - generate-gui: Generates a new menu tree while preserving user changes with a GUI progress bar."
  echo "# - update: Updates existing menu tree to current set of installed applications."
  echo "# - start: Starts the user daemon that monitors for application changes"
  echo "# - restart: Restarts the user daemon that monitors for application changes"
  echo "# - stop: Stops the user daemon that monitors for application changes"
  echo "# - debug: Launches the debug prompt for expert users"
  echo "#--------------------------------------------"
}

# UPDATE EXISTING MENU
utilsUpdate () {
  if [[ -f $FBMENU && -f $LISTCURRENT ]]
  then
    timerStart
    loadEssentials
    getChanges
    processAddQ
    processRemoveQ
    updateCurCache
    createCategoryMenu > $FBCAT
    debug timerEnd
  else
    utilsGenerateWithZenity
  fi
  }

# GENERATE MENU PRESERVING CUSTOM ENTRIES
utilsGenerate () {
  timerStart
  ZENPROGRESS=0
  zenityProgress
  cleanUp
  ZENPROGRESS=5
  zenityProgress
  loadEssentials
  ZENPROGRESS=10
  zenityProgress
  debug debugMenuRoot
  createMenuRoot > $FBMENU
  ZENPROGRESS=20
  zenityProgress
  createPreservedSubmenu
  updateCurCache
  ZENPROGRESS=95
  zenityProgress
  debug debugCatMenu
  createCategoryMenu > $FBCAT
  ZENPROGRESS=100
  zenityProgress
  debug timerEnd
}

utilsGenerateWithZenity () {
  checkX=$(tty | grep "pts" | cat)
  if [[ -n "$checkX" ]]
  then
    ZEN="ON"
    utilsGenerate | zenity --progress --title="mint-fm2" --text="$(getTranslation "system-config-printer" "Please wait")" --percentage=0 --auto-close 
  else
    ZEN=""
    utilsGenerate
  fi
}

# DAEMON CONTROL
daemonControl () {
#  loadEssentials
  $USRDAEMON $@ &> /dev/null 
  exec 3>&-
  exec 2>&-
  exec 1>&-
  exit 0
}

# LOAD USER SETTINGS
loadUserConfig () {
  source $USRCONFIG
}

# LOAD DEBUGGING LIBRARY IF REQUIRED
loadDebugLibrary () {
  if [ "$DBG" = "ON" ]
  then
    echo "Importing debugLibrary."
    source $APPDIR/debugLibrary
    echo ""
  fi
}

# MAIN DEBUG FUNCTION
debug () {
  if [[ "$DBG" = "ON" ]]
  then
    declare -F $1 &>/dev/null && $@
  fi
}

# LOAD ESSENTIAL CONFIGS AND RUN ESSENTIAL FUNCTIONS
loadEssentials () {
  checkFileAndDirectories
  loadDebugLibrary
  debug debugFilesNConfigs
  createIconDB # only created if the DBs are required
  createIconLocate
  createMenuCache
  createCacheArrays $LISTNEW
}

# DELETE OLD FILES
cleanUp () {
# Delete files defined in targets array
  for target in ${CLEANUPTARGETS[@]}
  do
    if [ -f $target ]
    then
      rm $target
    fi
  done
}

# CHECK REQUIRED FILES AND DIRECTORIES
checkFileAndDirectories () {
# Directories to check/create
  DIRS=("$WDIR" "$SUBMENUDIR" "$FBDIR") #locally defined
# Check!
  for dir in ${DIRS[@]}
  do
    if [[ ! -d $dir ]]
    then
      mkdir -p $dir
    fi
  done
# Files to check/create
  if [[ ! -f $USRCONFIG ]]
  then
    cp $APPDIR/mint-fm2.conf $USRCONFIG
  fi

# Load user configs
  loadUserConfig

# Files to touch
  FILES=("$FBMENU" "$FBCAT" "$FBCUSTOM") # locally defined
  for f in ${FILES[@]}
  do
    touch $f
  done
}

# CREATE ICON DATABASES IF REQUIRED
createIconDB () {
  if [ "$ICONS" = "TRUE" ]
  then
# Define folders to check 
    FOLDERS=("$ICONTHEME" "/usr/lib/linuxmint" "$HOME/.icons" "/usr/share/icons" "/usr/share/pixmaps/") #locally defined (not in systemConfig)
# Define corresponding output databases
    DATABASES=("$ICONPREFDB" "$ICONMINTDB" "$ICONUSERDB" "$ICONSYSDB" "$ICONPIXDB") #locally defined (not in systemConfig)
  
# Create them!
    folder=0
    while [[ $folder -lt ${#FOLDERS[@]} ]]
    do
      if [[ -n "${FOLDERS[$folder]}" && -d "${FOLDERS[$folder]}" ]]
      then
	debug debugCreateIconDB
	updatedb -U ${FOLDERS[$folder]} -o ${DATABASES[$folder]} --require-visibility no
	ACTIVEICONDB[$folder]=${DATABASES[$folder]}
      fi
      let folder=$folder+1
    done
    debug debugActiveIconDB
  else
    ACTIVEICONDB=()
    debug debugActiveIconDB
  fi
}

# BUILD THE LOCATE COMMAND
createIconLocate () {
  if [[ "$ICONS" = "TRUE" ]]
  then
    ICONLOCATE="locate -q -e -P -l 1 $(for db in ${ACTIVEICONDB[@]}; do echo '--database' $db '\'; done) -i "
  debug debugIconLocate
  else
    ICONLOCATE="true "
  fi
}

# GET ICON BY NAME
getIconByName () {
    $ICONLOCATE $@
}

# GET TRANSLATIONS FROM GETTEXT
getTranslation () {	# $1 = .mo file to get translation from; $2 = string to translate; $3 = locale directory (optional)
  if [[ -n $3 ]] ; then export TEXTDOMAINDIR=$3 ; fi ; export TEXTDOMAIN=$1 ;  echo "$(gettext "$2")"
}

# GET ENTRIES FOR TOP SECTION & QUIT
homeEntry () {
  echo "[exec]  ($USER)  {thunar $HOME} <$(getIconByName *gtk-home*.png)>"
}

browserEntry () {
  echo "[exec]  ($(getTranslation "libexo-0.3" "Web Browser" "/usr/share/locale"))  {x-www-browser} <$(getIconByName *applications-internet*.png)>"
}

mintInstallEntry () {
  echo "[exec]  ($(getTranslation "mintinstall" "Software Manager" "/usr/share/linuxmint/locale"))  {mintInstall} </usr/share/pixmaps/mintinstall.png>"
}

terminalEntry () {
  echo "[exec]  ($(getTranslation "libexo-0.3" "Terminal Emulator" "/usr/share/locale"))  {x-terminal-emulator} <$(getIconByName *terminal*.png)>"
}

runEntry () {
  echo "[exec]  ($(getTranslation "virt-manager" "Run" "/usr/share/locale-langpack"))  {/usr/bin/fbrun} <$(getIconByName *run*.png)>"
}

customEntry () {
  echo "[include] ($FBCUSTOM)"
}

quitEntry () {
  echo "[exec]  ($(getTranslation "vlc" "Quit" "/usr/share/locale"))  {ciao} <$(getIconByName *gtk-quit*.png)>"
}

installerEntry () {
  if [[ -f /usr/bin/ubiquity ]]
  then
    echo "[exec]  ($(getTranslation "system-config-printer" "Install" "/usr/share/locale-langpack"))  {ubiquity} <$(getIconByName *start-here*.png)>"
  fi
}

separator () {
  echo "[separator] (--------)"
}

generatedEntry () {
  echo "[include] ($FBCAT)"
}

# GENERATE THE FLUXBOX MENU FILE
createMenuRoot () {
  #declare -a ENTRYORDER
  echo "[begin] ($MENUTITLE) <$(getIconByName *start-here*.png)>"
# Installer entry  
  installerEntry
  if [[ ${#ENTRYORDER[@]} -eq 0 ]]
  then
#    ENTRYORDER=($(sed -e '/ENTRYORDER=/!d; s/.*=(//g; s/)//g; s/"//g' $MINTFM2ROOT/systemConfig ))
    eval $(grep "ENTRYORDER=" $MINTFM2ROOT/systemConfig )
  fi
  for entry in ${ENTRYORDER[@]}
  do
    declare -F $entry &> /dev/null && $entry
  done
  echo "[end]"
}

# GENERATE THE MENU CACHE
createMenuCache() {
  debug tempPathReset
  debug debugMenuCacheGen
  $MENUCACHEGEN
  tmp=$(cat $LISTNEW)
  echo "$tmp" | sed '1,/^+Applications/{/^+Applications/b; /^/d;}' > $LISTNEW
  debug tempCustomPathSet
}

# LOAD THE NEW MENU CACHE INTO ARRAYS
createCacheArrays () { #$1 = Cache file
if [[ -f $1 ]]
then
# Clean out Arrays
  RAWDATA=()
  CATINDEX=()
  APPINDEX=()

# Array of all data
  i=1
  while read line
  do 
    RAWDATA[$((i++))]="$line"
  done < $1

# Array of category Indexes
  CATINDEX=( $(sed -n '/^+/=' $1) )
# Add last element to allow less than condition for last category.
  CATINDEX[${#CATINDEX[@]}]=${#RAWDATA[@]}

# Array of application indexes
  APPINDEX=( $(sed -n '/^-.*.desktop/=' $1) )
fi
}

# GET THE NAME OF THE ENTRY
getName () {
  echo ${RAWDATA[$RAWINDEX+1]}
}

# GET THE ICON USING INDEX
getIconByIndex () {
  icon=""
  term=${RAWDATA[$RAWINDEX+3]}
  if [[ -f "$term" && "$ICONLOCATE" != "true " && -n $(echo "$term" | grep -e ".png$" -e ".xpm$" | cat) ]]
  then
    icon=$term
  elif [[ -n "$term" ]]
  then
    icon=$($ICONLOCATE "*$term*.png" "*$term*.xpm")
  fi
# Try to find other icon
  if [[ -z "$icon" ]]
  then
    icon=$($ICONLOCATE *$term* | grep -v ".svg")
  fi
  if [[ -z "$icon" ]]
  then
    icon=$($ICONLOCATE *stock_script*.png)
  fi
  echo $icon
}

# GET THE APPLICATION COMMAND
getCommand () {
  echo ${RAWDATA[$RAWINDEX+7]} #| fixCommand
}

# GET THE ENTRY'S TAG
getTag () {
  echo ${RAWDATA[$RAWINDEX]}
}

# GET THE FOLDER OF THE .desktop FILE
getFolder () {
  echo ${RAWDATA[$RAWINDEX+5]}
}

# GET THE APPLICATION'S CATEGORY
getAppCategory () {
  for (( count=1; count < ${#CATINDEX[@]}; count++ ))
  do
    if [[ $RAWINDEX -ge ${CATINDEX[$count-1]} && $RAWINDEX -lt ${CATINDEX[$count]} || -z ${CATINDEX[$count]} ]]
    then
      echo ${RAWDATA[${CATINDEX[$count-1]}]} | sed -e 's/+//g'
      break
    fi
  done
}

# GET THE APPLICATIONS IN A CATEGORY
getCategoryApps () { # $1 = index of category in CATINDEX
  for i in ${APPINDEX[@]}
  do
    if [[ $i -gt ${CATINDEX[$1]} && $i -lt ${CATINDEX[$1+1]} ]]
    then
      RAWINDEX=$i
      createAppEntry
    fi
  done
}

# CREATE THE CATEGORY MENU
createCategoryMenu () {
  for (( i=1; i < $[ ${#CATINDEX[@]}-1 ]; i++ ))
  do
    RAWINDEX=${CATINDEX[$i]}
    if [[ "${RAWDATA[$RAWINDEX]}" = "+System" || "${RAWDATA[$RAWINDEX]}" = "+DesktopSettings" || ! -f "$SUBMENUDIR/${RAWDATA[$RAWINDEX]//+/}" ]]
    then
      continue
    fi
    echo "[submenu]  ($(getName))  <$(getIconByIndex)>"
    echo "[include] ($SUBMENUDIR/${RAWDATA[$RAWINDEX]//+/})"
    echo "[end]"
  done
  for (( i=1; i < $[ ${#CATINDEX[@]}-1 ]; i++ ))
  do
    RAWINDEX=${CATINDEX[$i]}
    if [[ "${RAWDATA[$RAWINDEX]}" = "+System" ]]
    then
      entry="[submenu] ($(getName)) <$(getIconByIndex)>"
      echo $entry
      echo $entry
      echo "[include] ($SUBMENUDIR/${RAWDATA[$RAWINDEX]//+/})"
      echo "[end]"
    fi
  done
  for (( i=1; i < $[ ${#CATINDEX[@]}-1 ]; i++ ))
  do
    RAWINDEX=${CATINDEX[$i]}
    if [[ "${RAWDATA[$RAWINDEX]}" = "+DesktopSettings" ]]
    then
      echo "[submenu] ($(getName)) <$(getIconByIndex)>"
      echo "[include] ($SUBMENUDIR/${RAWDATA[$RAWINDEX]//+/})"
      echo "[end]"
      echo "[submenu] (Fluxbox) <$(getIconByIndex)>"
      echo "[config] (Fluxbox Menu)"
      echo "[reconfig] (Reload Config)"
      echo "[restart] (Restart Fluxbox)"
      echo "[workspaces] (Workspaces)"
      echo "[submenu] (Styles) {}"
      echo "[stylesdir] (/usr/share/fluxbox/styles)"
      echo "[stylesdir] (~/.fluxbox/styles)"
      echo "[end]"
      echo "[end]"
      echo "[end]"
    fi
  done
  
}

# CREATE ENTRY FOR AN APPLICATION USING INDEX
createAppEntry () {
  echo "[exec] ($(getName)) {$(getCommand)} <$(getIconByIndex)> ###$(getTag)"  | fixEntry #$(getFolder)###$(getAppCategory)"
}

# OUTPUTS MENU ENTRIES FOR ALL APPS (GOOD FOR DEBUGGING ENTRIES)
getAllApps () {
  for app in ${APPINDEX[@]}
  do
    RAWINDEX=$app
    createAppEntry
  done
}

# OUTPUT THE WHOLE MENU
outputMenu () {
  createMenuRoot > $FBMENU
  createCategoryMenu > $FBCAT
  for (( k=1; k<$[${#CATINDEX[@]}-1]; k++ ))
  do
    RAWINDEX=${CATINDEX[$k]}
    FILENAME=$(getAppCategory)
    if [[ -n "$FILENAME" ]]
    then
      getCategoryApps $k > $SUBMENUDIR/$FILENAME
    fi
  done
}

# UPDATE CURRENT MENU CACHE
updateCurCache () {
  debug debugUpdateCache
  if [[ -f $LISTCURRENT && -f $LISTNEW ]]
  then
    rm $LISTCURRENT
    mv $LISTNEW $LISTCURRENT
  elif [[ -f $LISTNEW ]]
  then
    mv $LISTNEW $LISTCURRENT
  fi
}

# GET CHANGES TO BE APPLIED
getChanges () {
  debug debugChanges
  ADDQUEUE=( $(diff $LISTCURRENT $LISTNEW | sed -e '/^> -/!d; s/^> -//' | while read line ; do grep -n "$line" $LISTNEW | sed 's/:.*//'; done) )
  RMQUEUE=( $(diff $LISTCURRENT $LISTNEW | sed -e '/^< -/!d; s/^< -//' | while read line ; do grep -n "$line" $LISTCURRENT | sed 's/:.*//'; done) )
}

# PROCESSING ENTRIES TO BE ADDED
processAddQ () {
  if [[ ${#ADDQUEUE[@]} -gt 0 ]]
  then
    debug debugAddQ
    for (( q=0; q < ${#ADDQUEUE[@]}; q++ ))
    do
      RAWINDEX=${ADDQUEUE[$q]}
      if [[ -n "${RAWDATA[$RAWINDEX]##+*}" ]]
      then
        submenu=$SUBMENUDIR/$(getAppCategory)
        if [[ -f $submenu ]]
        then
          loadCurrSubmenu $submenu
        else
          CURRSUBMENU=()
        fi
        CURRSUBMENU[${#CURRSUBMENU[@]}+1]=$(createAppEntry)
        sortArray CURRSUBMENU
        echo "$SORTED" > $submenu
      fi
    done
  fi
  debug debugEcho
}

# PROCESSING ENTRIES TO BE REMOVED
processRemoveQ () {
  if [[ ${#RMQUEUE[@]} -gt 0 ]]
  then
    debug debugRmQ
    createCacheArrays $LISTCURRENT
    for (( q=0; q < ${#RMQUEUE[@]}; q++ ))
    do
      RAWINDEX=${RMQUEUE[$q]}
      submenu=$SUBMENUDIR/$(getAppCategory)
      loadCurrSubmenu $submenu
      REMOVED=$(for (( elem=1; elem <= ${#CURRSUBMENU[@]}; elem++ )); do echo "${CURRSUBMENU[$elem]//*###${RAWDATA[$RAWINDEX]}*}" | grep -v "^$" | cat ; done)
      if [[ -z $REMOVED ]]
      then
	rm $submenu
      else
	echo "$REMOVED" > $submenu
      fi
    done
  fi
  debug debugEcho
}

# LOADS THE SPECIFIED SUBMENU INTO AN ARRAY
loadCurrSubmenu () {
  debug debugSubmenu $1
# Clean out CURRSUBMENU
  CURRSUBMENU=()

# Array of current submenu file
  i=1
  if [[ -f $1 ]]
  then
    while read line
    do 
      CURRSUBMENU[$((i++))]="$line"
    done < $1
  fi
}

# SORTS AN ARRAY ALPHABETICALLY
sortArray () { # $1 = name of array to be sorted
  num=$(eval echo \${#$1[@]})
  string=$(for (( e=1; e <= $num; e++ )); do echo "$(eval echo \${$1[$e]})"; done)
  SORTED=$(echo "$string" | sort -u -f )
}

# CREATE PRESERVED SUBMENUDIR
createPreservedSubmenu () {
  for (( k=1; k<$[${#CATINDEX[@]}-1]; k++ ))
  do
    RAWINDEX=${CATINDEX[$k]}
    FILENAME=$(getAppCategory)
    OLDSUBMENU=() #locally defined
    CUSTOMSEDCMD=() #locally defined
    if [[ -n "$FILENAME" ]]
    then
      if [[ -f "$SUBMENUDIR/$FILENAME" ]]
      then
	debug debugCurrSubmenu
	i=1
	while read line
	do 
	  OLDSUBMENU[$((i++))]="$line"
	done < $SUBMENUDIR/$FILENAME
	customString=$( for (( i=1; i <= ${#OLDSUBMENU[@]}; i++ )); do echo ${OLDSUBMENU[$i]//*###*/}; done )
	if [[ -n "$customString" ]]
	then
	  customDesktops="$(echo "$customString" | grep "#+#" | while read line; do echo $line | sed -e 's/.*#+#//'; done )"
	  CUSTOMSEDCMD="grep -v $(echo $customDesktops | sed -e 's/-/ -e /g')"
	  debug debugCustomEntries
	else
	  CUSTOMSEDCMD="cat"
	  debug debugNoCustomEntries
	fi
	generatedString=$(getCategoryApps $k | $CUSTOMSEDCMD)
	combined=$( echo "$(echo "$customString" ; echo "$generatedString")" | sort -u -f | grep -v "^$" )
	if [[ -n "$combined" ]]
	then
	  echo "$combined" > $SUBMENUDIR/$FILENAME
	fi
      else
	debug debugNewSubmenu
	submenu=$(getCategoryApps $k)
	if [[ -n $submenu ]]
	then
	  echo "$submenu" > $SUBMENUDIR/$FILENAME
	fi
      fi
    fi
    ZENPROGRESS=$((20+($k*75)/${#CATINDEX[@]}))
    zenityProgress
    debug debugEcho
  done
}

# OUTPUTS THE PROGRESS OF MENU GENERATION TO ZENTITY
zenityProgress () {
  if [[ $(tty | grep "pts") && "$ZEN" = "ON" ]]
  then 
    echo "$ZENPROGRESS"
  else
    true
  fi  
}

# FIXES KNOWN ISSUES IN GENERATED ENTRIES
fixEntry () {
  sed -e '
  s/).*)/)/g
  s/%.*}/}/g
  s/{catfish}/{catfish --path=$HOME}/g
  s/{f-spot}/{dbus-launch f-spot}/g
  s/{xdg-open .wine\/dosdevices\/c:}/{thunar ~\/.wine\/dosdevices\/c\\:}/g
  s/{gmplayer -msglevel all}/{gmplayer -msglevel all=0}/g
  s/(Xfce 4 /(/g
  s/{\/usr\/sbin\/gdmsetup}/{gksu \/usr\/sbin\/gdmsetup}/g
  s/{envyng -t}/{xterm -e "envyng -t"}/g
  s/{lxrandr}/{lxrandr ; fbsetbg -l}/g
  s/{mintupload-file-uploader} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{mintupload-file-uploader} <\/usr\/share\/pixmaps\/mintupload.png>/g
  s/{mintbackup} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{mintbackup} <\/usr\/share\/pixmaps\/mintbackup.png>/g
  s/{mintnanny} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{mintnanny} <\/usr\/share\/pixmaps\/mintnanny.png>/g
  s/{mintinstall} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{mintinstall} <\/usr\/share\/pixmaps\/mintinstall.png>/g
  s/{mintupdate} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{mintupdate} <\/usr\/share\/pixmaps\/mintupdate.png>/g
  s/{mintupload-manager} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{mintupload-manager} <\/usr\/share\/pixmaps\/mintupload.png>/g
  s/{xfce4-power-manager-settings} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{xfce4-power-manager-settings} <\/usr\/share\/pixmaps\/xfce4-power-manager.png>/g
  s/{xscreensaver-demo} <\/usr\/share\/icons\/gnome\/16x16\/mimetypes\/stock_script.png>/{xscreensaver-demo} <\/usr\/share\/pixmaps\/xscreensaver.xpm>/g
  s/{ubiquity --desktop }/{ubiquity}/g
  /{xfce4-panel -c}/d
  /(Screensaver Settings)/d
  /(XScreenSaver Setup)/d
  /{vino-preferences}/d
  /(Sun Java 6 Web Start)/d
  /lxde-applications.menu/d
  /{synaptic}/d
  /{openbox-session}/d
  /{kdesu \/usr\/sbin\/ndisgtk -- --kde}/d
  '
}

# TIMER FUNCTIONS FOR PERFORMANCE TESTS
timerStart () {
  start=`date +%s.%N`
}
timerEnd () {
  end=`date +%s.%N`
  echo "Script run in `echo "scale=4; $end-$start" | bc` seconds"
}
#		/FUNCTIONS
############################################# 

# Variable to check if all functions are loaded
LIBFUNCTIONS="LOADED"
