#!/bin/bash
do {
	#////////////////////////////////////
	# DietPi Process Tool
	#
	#////////////////////////////////////
	# Created by Daniel Knight / daniel.knight@dietpi.com / dietpi.com
	#
	#////////////////////////////////////
	#
	# Info:
	# - Menu system that allows users to configure and set system wide process/program attributes: Nice, Affinity, Scheduler Policy/Priority.
	# - Called from /DietPi/dietpi/dietpi-services start (eg: after all installed dietpi-software has been started)
	#
	# Usage:
	# dietpi-process_tool 0/no input	= Menu
	# dietpi-process_tool 1				= Apply
	#////////////////////////////////////

	#Grab Input
	setvar INPUT = '0'
	if [[ $1 =~ ^-?[0-9]+$ ]] {

		setvar INPUT = "$1"

	}

	#Import DietPi-Globals ---------------------------------------------------------------
	source /DietPi/dietpi/func/dietpi-globals
	G_CHECK_ROOT_USER
	export G_PROGRAM_NAME='DietPi-Process_tool'
	#Import DietPi-Globals ---------------------------------------------------------------

	#/////////////////////////////////////////////////////////////////////////////////////
	# Globals
	#/////////////////////////////////////////////////////////////////////////////////////
	setvar EXIT_CODE = '0'

	setvar MAX_PROGRAMS = '0'

	setvar aNICE = ''()
	setvar aAFFINITY = ''()
	setvar aSCHEDULE_POLICY = ''()
	setvar aSCHEDULE_PRIORITY = ''()
	setvar aPID = ''()
	setvar aNAME = ''()
	setvar aPROCESS_NAME = ''()
	setvar aAVAILABLE = ''()
	setvar aSCHEDULE_AVAILABLE_POLICY = ''()
	setvar aSCHEDULE_AVAILABLE_PRIORITY_MIN = ''()
	setvar aSCHEDULE_AVAILABLE_PRIORITY_MAX = ''()
	setvar MAX_SCHEDULE_POLICIES = '0'

	proc Apply_Process_Tool {

		G_DIETPI-NOTIFY 3 DietPi-Process_Tool Apply

		local status_text=""
		local status_text_ok="$(G_DIETPI-NOTIFY 0)"
		local status_text_failed="$(G_DIETPI-NOTIFY 1)"

		for ((i=0; i<$MAX_PROGRAMS; i++))
		do

			if (( ${aAVAILABLE[$i]} == 1 )); then

				# - Find all processes with target name
				cat "$FP_PS_LIST" | grep "${aPROCESS_NAME[$i]}" | awk '{print $1}' > "$FP_TEMP"
				while read -r line
				do

					#Obtain PID
					aPID[$i]=$line

					#info
					echo -e "${aNAME[$i]} (${aPID[$i]})"

					# - Check if PID exists (this can occur with programs like netdata that run as root initally, create a new process under a different user, then terminate the original root process.
					if (( ! $( ps ax | awk '{print $1}' | grep -ci -m1 "${aPID[$i]}") )); then

						G_DIETPI-NOTIFY 2 PID "${aPID[$i]}" no longer exists

					else

						# - nice
						renice -n ${aNICE[$i]} ${aPID[$i]} &> /dev/null
						if (( $? == 0 )); then

							status_text=$status_text_ok

						else

							status_text=$status_text_failed
							EXIT_CODE=1

						fi

						status_text+="\e[90mNice      ${aNICE[$i]}\e[0m"
						echo -e "$status_text"

						# - Affinity
						taskset -pc ${aAFFINITY[$i]} ${aPID[$i]} &> /dev/null
						if (( $? == 0 )); then

							status_text=$status_text_ok

						else

							status_text=$status_text_failed
							EXIT_CODE=2

						fi

						status_text+="\e[90mAffinity  ${aAFFINITY[$i]}\e[0m"
						echo -e "$status_text"

						# - schedule
						# -- Match scheduler policy to array list of available
						for ((j=0; j<$MAX_SCHEDULE_POLICIES; j++))
						do

							if [ "${aSCHEDULE_POLICY[$i]}" = "${aSCHEDULE_AVAILABLE_POLICY[$j]}" ]; then

								# -- cap to min/max against array of available
								if (( ${aSCHEDULE_PRIORITY[$i]} < ${aSCHEDULE_AVAILABLE_PRIORITY_MIN[$j]} )) ||
									(( ${aSCHEDULE_PRIORITY[$i]} > ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$j]} )); then

									aSCHEDULE_PRIORITY[$i]=${aSCHEDULE_AVAILABLE_PRIORITY_MIN[$j]}

								fi

								break

							fi

						done

						local chrt_mode=''
						if [ "${aSCHEDULE_POLICY[$i]}" = "SCHED_OTHER" ]; then

							chrt_mode='-o'

						elif [ "${aSCHEDULE_POLICY[$i]}" = "SCHED_FIFO" ]; then

							chrt_mode='-f'

						elif [ "${aSCHEDULE_POLICY[$i]}" = "SCHED_RR" ]; then

							chrt_mode='-r'

						elif [ "${aSCHEDULE_POLICY[$i]}" = "SCHED_BATCH" ]; then

							chrt_mode='-b'

						elif [ "${aSCHEDULE_POLICY[$i]}" = "SCHED_IDLE" ]; then

							chrt_mode='-i'

						fi

						# - Apply
						chrt $chrt_mode -p ${aSCHEDULE_PRIORITY[$i]} ${aPID[$i]} &> /dev/null

						if (( $? == 0 )); then

							status_text=$status_text_ok

						else

							status_text=$status_text_failed
							EXIT_CODE=3

						fi

						status_text+="\e[90mScheduler ${aSCHEDULE_POLICY[$i]} ${aSCHEDULE_PRIORITY[$i]}\e[0m"
						echo -e "$status_text"

					fi


				done < "$FP_TEMP"
				rm "$FP_TEMP"

			fi

		done

		echo -e ""

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Init
	#/////////////////////////////////////////////////////////////////////////////////////
	proc Init {

		#--------------------------------------------------------------------------------
		#Init array
		local index=0
		setvar aNAME[$index]='Aria2';setvar aPROCESS_NAME[$index]='aria2c';((index++))
		setvar aNAME[$index]='Avahi Daemon';setvar aPROCESS_NAME[$index]='avahi-daemon';((index++))
		setvar aNAME[$index]='Bluetooth (Service)';setvar aPROCESS_NAME[$index]='bluetooth';((index++))
		setvar aNAME[$index]='Bluetooth (Hciuart)';setvar aPROCESS_NAME[$index]='hciattach';((index++))
		setvar aNAME[$index]='Blynk Server';setvar aPROCESS_NAME[$index]='blynkserver';((index++))
		setvar aNAME[$index]='BruteFIR';setvar aPROCESS_NAME[$index]='brutefir';((index++))
		setvar aNAME[$index]='CAVA';setvar aPROCESS_NAME[$index]='cava';((index++))
		setvar aNAME[$index]='Cuberite';setvar aPROCESS_NAME[$index]='Cuberite';((index++))
		setvar aNAME[$index]='CloudPrint';setvar aPROCESS_NAME[$index]='cloudprintd';((index++))
		setvar aNAME[$index]='CouchPotato';setvar aPROCESS_NAME[$index]='CouchPotato.py';((index++))
		setvar aNAME[$index]='Cron';setvar aPROCESS_NAME[$index]='cron';((index++))
		setvar aNAME[$index]='CUPS';setvar aPROCESS_NAME[$index]='cupsd';((index++))
		setvar aNAME[$index]='DarkIce';setvar aPROCESS_NAME[$index]='darkice';((index++))
		setvar aNAME[$index]='Deluge (Daemon)';setvar aPROCESS_NAME[$index]='deluged';((index++))
		setvar aNAME[$index]='Deluge (Web interface)';setvar aPROCESS_NAME[$index]='deluge-web';((index++))
		setvar aNAME[$index]='DHCP Client';setvar aPROCESS_NAME[$index]='dhclient';((index++))
		setvar aNAME[$index]='DHCP Server';setvar aPROCESS_NAME[$index]='dhcpd';((index++))
		setvar aNAME[$index]='Docker';setvar aPROCESS_NAME[$index]='docker';((index++))
		setvar aNAME[$index]='Dropbear';setvar aPROCESS_NAME[$index]='dropbear';((index++))
		setvar aNAME[$index]='Emby Server';setvar aPROCESS_NAME[$index]='emby-server';((index++))
		setvar aNAME[$index]='EmonHUB';setvar aPROCESS_NAME[$index]='emonhub';((index++))
		setvar aNAME[$index]='Fail2Ban';setvar aPROCESS_NAME[$index]='fail2ban-server';((index++))
		setvar aNAME[$index]='FuguHub';setvar aPROCESS_NAME[$index]='bdd';((index++))
		setvar aNAME[$index]='Gitea';setvar aPROCESS_NAME[$index]='gitea';((index++))
		setvar aNAME[$index]='Gogs';setvar aPROCESS_NAME[$index]='gogs';((index++))
		setvar aNAME[$index]='Google AIY';setvar aPROCESS_NAME[$index]='main.py';((index++))
		setvar aNAME[$index]='Gmediarender';setvar aPROCESS_NAME[$index]='gmediarender';((index++))
		setvar aNAME[$index]='Home Assistant';setvar aPROCESS_NAME[$index]='hass';((index++))
		setvar aNAME[$index]='HTPC Manager';setvar aPROCESS_NAME[$index]='Htpc.py';((index++))
		setvar aNAME[$index]='IceCast';setvar aPROCESS_NAME[$index]='icecast2';((index++))
		setvar aNAME[$index]='MPD';setvar aPROCESS_NAME[$index]='bin/mpd';((index++)) #Tomcat8 triggers 'mpd'
		setvar aNAME[$index]='YMPD (HiFi)';setvar aPROCESS_NAME[$index]='ympd';((index++))
		setvar aNAME[$index]='Jackett';setvar aPROCESS_NAME[$index]='JackettConsole.exe';((index++))
		setvar aNAME[$index]='Jack Server';setvar aPROCESS_NAME[$index]='jackd';((index++))
		setvar aNAME[$index]='Jconvolver';setvar aPROCESS_NAME[$index]='jconvolver';((index++))
		setvar aNAME[$index]='Minio';setvar aPROCESS_NAME[$index]='minio';((index++))
		setvar aNAME[$index]='MiniDLNA';setvar aPROCESS_NAME[$index]='minidlna';((index++))
		#aNAME[$index]='moOde (worker.php)';aPROCESS_NAME[$index]='worker.php';((index++))
		setvar aNAME[$index]='Mopidy';setvar aPROCESS_NAME[$index]='mopidy';((index++))
		setvar aNAME[$index]='Mosquitto';setvar aPROCESS_NAME[$index]='mosquitto';((index++))
		setvar aNAME[$index]='MotionEye';setvar aPROCESS_NAME[$index]='motioneye';((index++))
		setvar aNAME[$index]='Mumble Server';setvar aPROCESS_NAME[$index]='murmurd';((index++))
		setvar aNAME[$index]='NAA Daemon';setvar aPROCESS_NAME[$index]='networkaudiod';((index++))
		setvar aNAME[$index]='NetData';setvar aPROCESS_NAME[$index]='netdata';((index++))
		setvar aNAME[$index]='Nukkit';setvar aPROCESS_NAME[$index]='nukkit.jar';((index++))
		setvar aNAME[$index]='Node-Red';setvar aPROCESS_NAME[$index]='node-red';((index++))
		setvar aNAME[$index]='NoIp';setvar aPROCESS_NAME[$index]='noip2';((index++))
		setvar aNAME[$index]='NZBget';setvar aPROCESS_NAME[$index]='nzbget';((index++))
		setvar aNAME[$index]='OctoPrint';setvar aPROCESS_NAME[$index]='octoprint';((index++))
		setvar aNAME[$index]='OpenBazaar';setvar aPROCESS_NAME[$index]='openbazaard.py';((index++))
		setvar aNAME[$index]='Open Media Vault';setvar aPROCESS_NAME[$index]='omv-engined';((index++))
		setvar aNAME[$index]='OpenSSH Server';setvar aPROCESS_NAME[$index]='sshd';((index++))
		setvar aNAME[$index]='OpenVPN Server';setvar aPROCESS_NAME[$index]='openvpn';((index++))
		setvar aNAME[$index]='Pi-SPC';setvar aPROCESS_NAME[$index]='sds.sh';((index++))
		setvar aNAME[$index]='Pi-hole';setvar aPROCESS_NAME[$index]='dnsmasq';((index++))
		setvar aNAME[$index]='Pi-hole FTL';setvar aPROCESS_NAME[$index]='pihole-FTL';((index++))
		setvar aNAME[$index]='ProFTP';setvar aPROCESS_NAME[$index]='proftpd';((index++))
		setvar aNAME[$index]='qBitTorrent';setvar aPROCESS_NAME[$index]='qbittorrent-nox';((index++))
		setvar aNAME[$index]='Radarr';setvar aPROCESS_NAME[$index]='Radarr.exe';((index++))
		setvar aNAME[$index]='Raspotify';setvar aPROCESS_NAME[$index]='librespot';((index++))
		setvar aNAME[$index]='RoonBridge';setvar aPROCESS_NAME[$index]='RoonBridge';((index++))
		setvar aNAME[$index]='RoonBridge (Helper)';setvar aPROCESS_NAME[$index]='RoonBridgeHelper';((index++))
		setvar aNAME[$index]='RoonBridge/Server (RAATServer)';setvar aPROCESS_NAME[$index]='RAATServer';((index++))
		setvar aNAME[$index]='RoonBridge/Server (Appliance)';setvar aPROCESS_NAME[$index]='RoonAppliance';((index++))
		setvar aNAME[$index]='RoonServer';setvar aPROCESS_NAME[$index]='RoonServer';((index++))
		setvar aNAME[$index]='rTorrent';setvar aPROCESS_NAME[$index]='rtorrent';((index++))
		setvar aNAME[$index]='SABnzbd';setvar aPROCESS_NAME[$index]='SABnzbd.py';((index++))
		setvar aNAME[$index]='Samba Server (Daemon)';setvar aPROCESS_NAME[$index]='smbd';((index++))
		setvar aNAME[$index]='Samba Server (NetBios)';setvar aPROCESS_NAME[$index]='nmbd';((index++))
		setvar aNAME[$index]='Shairport Sync';setvar aPROCESS_NAME[$index]='shairport-sync';((index++))
		setvar aNAME[$index]='SickRage';setvar aPROCESS_NAME[$index]='SickBeard.py';((index++))
		setvar aNAME[$index]='Sonarr';setvar aPROCESS_NAME[$index]='NzbDrone.exe';((index++))
		setvar aNAME[$index]='Spotify Connect Web';setvar aPROCESS_NAME[$index]='spotify-connect-web';((index++))
		setvar aNAME[$index]='Supervisor';setvar aPROCESS_NAME[$index]='supervisor';((index++))
		setvar aNAME[$index]='Squeezebox Server (LMS)';setvar aPROCESS_NAME[$index]='squeezeboxserver';((index++))
		setvar aNAME[$index]='Squeezelite';setvar aPROCESS_NAME[$index]='squeezelite';((index++))
		setvar aNAME[$index]='Syncthing';setvar aPROCESS_NAME[$index]='syncthing';((index++))
		setvar aNAME[$index]='Syncthing-inotify';setvar aPROCESS_NAME[$index]='syncthing-inotify';((index++))
		setvar aNAME[$index]='Tomcat8';setvar aPROCESS_NAME[$index]='tomcat8';((index++))
		setvar aNAME[$index]='Tonido';setvar aPROCESS_NAME[$index]='tonidoconsole';((index++))
		setvar aNAME[$index]='Transmission';setvar aPROCESS_NAME[$index]='transmission-daemon';((index++))
		setvar aNAME[$index]='AmiBerry';setvar aPROCESS_NAME[$index]='amiberry';((index++))
		setvar aNAME[$index]='UrBackup Server';setvar aPROCESS_NAME[$index]='urbackupsrv';((index++))
		setvar aNAME[$index]='Webserver Apache2';setvar aPROCESS_NAME[$index]='apache2';((index++))
		setvar aNAME[$index]='Webserver Nginx';setvar aPROCESS_NAME[$index]='nginx';((index++))
		setvar aNAME[$index]='Webserver Lighttpd';setvar aPROCESS_NAME[$index]='lighttpd';((index++))
		setvar aNAME[$index]='Webserver MariaDB';setvar aPROCESS_NAME[$index]='mariadb';((index++))
		setvar aNAME[$index]='Webserver MySql';setvar aPROCESS_NAME[$index]='mysqld';((index++))
		setvar aNAME[$index]='Webserver Php-fpm';setvar aPROCESS_NAME[$index]='php-fpm';((index++))
		setvar aNAME[$index]='Webserver Redis';setvar aPROCESS_NAME[$index]='redis-server';((index++))
		setvar aNAME[$index]='WiFi Hotspot';setvar aPROCESS_NAME[$index]='hostapd';((index++))
		setvar aNAME[$index]='WiFi Monitor';setvar aPROCESS_NAME[$index]='dietpi-wifi-monitor.sh';((index++))
		setvar aNAME[$index]='VirtualHere';setvar aPROCESS_NAME[$index]='vhusbd';((index++))
		setvar aNAME[$index]='YaCy search engine';setvar aPROCESS_NAME[$index]='yacy';((index++))
		setvar MAX_PROGRAMS = ${#aNAME[@]}

		# - Find out which programs are running (impies installed) | exclude kernel threads ([])
		ps ax | grep -v '\[' > "$FP_PS_LIST"

		# - Enable affinity for all cores and all programs by default
		local init_affinity_value=0
		if (( $(nproc --all) > 1 )) {

			setvar init_affinity_value = ""0-"$(( $(nproc --all) - 1))"

		}

		# - Init other arrays
		for ((i=0; i<$MAX_PROGRAMS; i++))
		do

			aNICE[$i]=0
			aAFFINITY[$i]=$init_affinity_value
			aSCHEDULE_POLICY[$i]="SCHED_OTHER"
			aSCHEDULE_PRIORITY[$i]=0
			aPID[$i]=0
			aAVAILABLE[$i]=0

			# - Obtain list of available programs
			if (( $(cat "$FP_PS_LIST" | awk '{string=""; for (i=5; i<=NF; i++) string=string $i " "; print string}' | grep -ci -m1 "${aPROCESS_NAME[$i]}") == 1 )); then

				aAVAILABLE[$i]=1

			fi

			# - Apply default nice to specific programs
			if [ "${aPROCESS_NAME[$i]}" = "netdata" ]; then

				aNICE[$i]=19

			fi

		done

		#Obtain list of available system schedulers/priorites
		chrt -m > "$FP_TEMP"

		setvar index = '0'
		while read line
		{

			# - available schedulers
			setvar aSCHEDULE_AVAILABLE_POLICY[$index]=$(echo -e "$line" | awk '{print $1}')

			# - available priorities
			# -- min
			setvar aSCHEDULE_AVAILABLE_PRIORITY_MIN[$index]=$(echo -e "$line" | sed 's/.*: //' | sed 's/\// /' | awk '{print $1}')
			# -- max
			setvar aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index]=$(echo -e "$line" | sed 's/.*: //' | sed 's/\// /' | awk '{print $2}')

			((index++))

		} < "$FP_TEMP"
		setvar MAX_SCHEDULE_POLICIES = ${#aSCHEDULE_AVAILABLE_POLICY[@]}

		#--------------------------------------------------------------------------------
		#Load Settings file. Generate if required.
		if test ! -f $FP_SETTINGS {

			Write_Settings_File

		} else {

			Read_Settings_File

		}

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Settings File
	#/////////////////////////////////////////////////////////////////////////////////////
	#Define Location
	setvar FP_SETTINGS = ""/DietPi/dietpi/.dietpi-process_tool""
	setvar FP_TEMP = ""/tmp/.dietpi-process_tool_temp""
	# - ps-aux > ram
	setvar FP_PS_LIST = ""/tmp/.dietpi-process_tool_ps_list""

	proc Read_Settings_File {

		local string=""
		for ((i=0; i<$MAX_PROGRAMS; i++))
		do

			#Match settings against process name
			local string=$( cat "$FP_SETTINGS" | grep -m1 "^${aPROCESS_NAME[$i]}")

			if [ -n "$string" ]; then

				#- nice
				aNICE[$i]=$( echo -e "$string" | awk '{print $2}')

				#- affinity
				aAFFINITY[$i]=$(echo -e "$string" | awk '{print $3}')

				#- schedule
				aSCHEDULE_POLICY[$i]=$(echo -e "$string" | awk '{print $4}')
				aSCHEDULE_PRIORITY[$i]=$(echo -e "$string" | awk '{print $5}')

			fi

		done

	}

	proc Write_Settings_File {

		rm $FP_SETTINGS &> /dev/null

		for ((i=0; i<$MAX_PROGRAMS; i++))
		do

			if (( ${aAVAILABLE[$i]} == 1 )); then

				echo -e "${aPROCESS_NAME[$i]} ${aNICE[$i]} ${aAFFINITY[$i]} ${aSCHEDULE_POLICY[$i]} ${aSCHEDULE_PRIORITY[$i]}" >> $FP_SETTINGS

			fi

		done

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Menu System
	#/////////////////////////////////////////////////////////////////////////////////////
	setvar PROGRAM_NAME = ""DietPi-Process Tool""
	setvar WHIP_QUESTION = '0'
	setvar CHOICE = '0'
	setvar TARGETMENUID = '0'
	setvar LAST_SELECTED_NAME = '0'

	proc Menu_Exit {

		whiptail --title "Exit $PROGRAM_NAME?" --yesno "Exit $PROGRAM_NAME?" --backtitle $PROGRAM_NAME --yes-button "Ok" --no-button "Back" --defaultno 9 55
		setvar CHOICE = ""$?
		if (( $CHOICE == 0 )) {

			#exit
			setvar TARGETMENUID = '-1'

		} else {

			#Return to Main Menu
			setvar TARGETMENUID = '0'

		}
	}

	#TARGETMENUID=0
	proc Menu_Main {

		setvar TARGETMENUID = '0'

		local whiptail_menu_array=()

		for ((i=0; i<$MAX_PROGRAMS; i++))
		do

			if (( ${aAVAILABLE[$i]} == 1 )); then

				whiptail_menu_array+=("${aNAME[$i]}" ": Nice ${aNICE[$i]} | Affinity ${aAFFINITY[$i]}")

			fi

		done

		setvar WHIP_QUESTION = ''Select a program:''
		setvar OPTION = $(whiptail --title "$PROGRAM_NAME" --backtitle "$PROGRAM_NAME" --menu "$WHIP_QUESTION" --cancel-button "Exit" --default-item "$LAST_SELECTED_NAME" 16 70 8 "${whiptail_menu_array[@]}" 3>&1 1>&2 2>&3)
		setvar CHOICE = ""$?

		unset whiptail_menu_array

		if (( $CHOICE == 0 )) {

			setvar LAST_SELECTED_NAME = "$OPTION"

			#Find selected program index
			local index_current=0
			for ((i=0; i<$MAX_PROGRAMS; i++))
			do

				if [ "${aNAME[$i]}" = "$OPTION" ]; then

					index_current=$i
					break

				fi

			done

			#Installed?
			if (( ${aAVAILABLE[$index_current]} == 0 )) {

				whiptail --title "Not available." --msgbox "${aNAME[$index_current]} is not running and/or installed." 8 70

			#Prompt for nice/affinity/schedule choice
			} else {

				#return back to this section.
				local run_loop=1

				while (( $run_loop == 1 ))
				{

					#menu, choose nice/affinity etc
					setvar WHIP_QUESTION = ""Select an option for ${aNAME[$index_current]}:""
					local whiptail_menu_array=()

					setvar whiptail_menu_array = ''("Nice" ": ${aNICE[$index_current]}")
					setvar whiptail_menu_array = ''("Affinity" ": ${aAFFINITY[$index_current]}")
					setvar whiptail_menu_array = ''("Schedule Policy" ": ${aSCHEDULE_POLICY[$index_current]}")
					setvar whiptail_menu_array = ''("Schedule Priority" ": ${aSCHEDULE_PRIORITY[$index_current]}")

					setvar OPTION = $(whiptail --title "${aNAME[$index_current]}" --backtitle "$PROGRAM_NAME" --menu "$WHIP_QUESTION" --cancel-button "Back" 11 60 4 "${whiptail_menu_array[@]}" 3>&1 1>&2 2>&3)
					setvar CHOICE = ""$?

					unset whiptail_menu_array

					if (( $CHOICE == 0 )) {

						case (OPTION) {
							Nice {

								#Get existing nice level
								local nice_current=0
								# - note: Whiptail will not work with negative numbers. The string cannot start with "-" as it throws subscript error.
								setvar nice_current = ""Nice : ${aNICE[$index_current]}""

								local whiptail_menu_array=()

								for ((i=-20; i<20; i++))
								do
									local desc=""
									if (( $i == -20 )); then

										desc="(Highest priority)"

									elif (( $i == -10 )); then

										desc="(Higher priority)"

									elif (( $i == -5 )); then

										desc="(High priority)"

									elif (( $i == 0 )); then

										desc="(Default priority)"

									elif (( $i == 5 )); then

										desc="(Low priority)"

									elif (( $i == 10 )); then

										desc="(Lower priority)"

									elif (( $i == 19 )); then

										desc="(Lowest priority)"

									fi

									whiptail_menu_array+=("Nice : $i" " $desc")

								done

								setvar WHIP_QUESTION = ""Please select your nice level for:\n${aNAME[$index_current]}\n\nInfo:\n - Negative values have a higher priority (eg: -10).\n - Positive values have a lower priority (eg: 15).\n - The default value is 0.""
								setvar OPTION = $(whiptail --title "Nice : ${aNAME[$index_current]}" --backtitle "$PROGRAM_NAME" --menu "$WHIP_QUESTION" --cancel-button "Back" --default-item "$nice_current" 19 60 5 "${whiptail_menu_array[@]}" 3>&1 1>&2 2>&3)
								setvar CHOICE = ""$?

								unset whiptail_menu_array

								if (( $CHOICE == 0 )) {

									#Convert back to int
									setvar aNICE[$index_current]=$(echo "$OPTION" | sed 's/[A-Za-z :]*//g')

									#Apply
									Apply_Process_Tool

								}
							}
							Affinity {

								# - Get existing affinity
								#NB: currently enables all
								local whiptail_menu_array=()

								for ((i=0; i<$(nproc --all); i++))
								do

									whiptail_menu_array+=("$i" "CPU                           " "on")

								done

								setvar WHIP_QUESTION = ""Please select your CPU affinity for:\n${aNAME[$index_current]}\n\n- Use the spacebar to enable/disable access to specific cores, for this program.\n- The default value is to enable all items.""
								whiptail --title "Affinity : ${aNAME[$index_current]}" --backtitle $PROGRAM_NAME --checklist --separate-output $WHIP_QUESTION --cancel-button "Back" 18 65 6 ${whiptail_menu_array[@]} 2> "$FP_TEMP"
								setvar CHOICE = ""$?

								unset whiptail_menu_array

								if (( $CHOICE == 0 )) {

									local new_affinity=""
									local loop_count=0

									while read choice
									{

										if test $choice != "" {

											#taskset requires , (comma) seperated cpu index indexs after 1st entry.
											if (( $loop_count == 0 )) {

												setvar new_affinity = "$choice"

											#Add comma for future entries
											} else {

												setvar new_affinity = "",$choice""

											}

											((loop_count++))

										}

									} < "$FP_TEMP"

									#Update affinity array with new value, if at least 1 item was selected.
									if test $new_affinity != ""  {

										setvar aAFFINITY[$index_current]="$new_affinity"

									}

									#Apply
									Apply_Process_Tool

								}

								rm $FP_TEMP &> /dev/null

							}
							"Schedule Policy" {

								local whiptail_menu_array=()
								local description=''

								for ((i=0; i<$MAX_SCHEDULE_POLICIES; i++))
								do
									# - add descriptions
									if [ "${aSCHEDULE_AVAILABLE_POLICY[$i]}" = "SCHED_OTHER" ]; then

										description='Normal (Default)'

									elif [ "${aSCHEDULE_AVAILABLE_POLICY[$i]}" = "SCHED_FIFO" ]; then

										description='First In, First Out (Real-time, time-critical)'

									elif [ "${aSCHEDULE_AVAILABLE_POLICY[$i]}" = "SCHED_RR" ]; then

										description='Round Robin (Real-time, time-critical)'

									elif [ "${aSCHEDULE_AVAILABLE_POLICY[$i]}" = "SCHED_BATCH" ]; then

										description='Batch style execution'

									elif [ "${aSCHEDULE_AVAILABLE_POLICY[$i]}" = "SCHED_IDLE" ]; then

										description='Background Jobs (Very low priority)'

									fi

									whiptail_menu_array+=("${aSCHEDULE_AVAILABLE_POLICY[$i]}" ": $description")

								done

								setvar WHIP_QUESTION = ""Please select a Schedule Policy for :\n${aNAME[$index_current]}""
								setvar OPTION = $(whiptail --title "Schedule Policy : ${aNAME[$index_current]}" --backtitle "$PROGRAM_NAME" --menu "$WHIP_QUESTION" --cancel-button "Back" --default-item "${aSCHEDULE_POLICY[$index_current]}" 14 70 5 "${whiptail_menu_array[@]}" 3>&1 1>&2 2>&3)
								setvar CHOICE = ""$?

								unset whiptail_menu_array

								if (( $CHOICE == 0 )) {

									setvar aSCHEDULE_POLICY[$index_current]=$OPTION

									#Apply
									Apply_Process_Tool

								}
							}
							"Schedule Priority" {

								#Match scheduler policy to array list
								local index_schedule=0
								for ((i=0; i<$MAX_SCHEDULE_POLICIES; i++))
								do

									if [ "${aSCHEDULE_POLICY[$index_current]}" = "${aSCHEDULE_AVAILABLE_POLICY[$i]}" ]; then

										index_schedule=$i
										break

									fi

								done

								#No priority options available
								if (( ( ${aSCHEDULE_AVAILABLE_PRIORITY_MIN[$index_schedule]} + ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]} ) == 0 )) {

									whiptail --title ${aSCHEDULE_POLICY[$index_current]} --msgbox "No priority options are available for ${aSCHEDULE_POLICY[$index_current]}." 8 60

								#List available
								} else {

									local whiptail_menu_array=()

									# - 7 step description scale
									local scale_value_lowest=${aSCHEDULE_AVAILABLE_PRIORITY_MIN[$index_schedule]}
									local scale_value_lower=$(( ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]} / 6 ))
									local scale_value_low=$(( ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]} / 6 * 2 ))
									local scale_value_medium=$(( ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]} / 6 * 3 ))
									local scale_value_high=$(( ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]} / 6 * 4 ))
									local scale_value_higher=$(( ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]} / 6 * 5 ))
									local scale_value_highest=${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]}
									for ((i=${aSCHEDULE_AVAILABLE_PRIORITY_MIN[$index_schedule]}; i<$(( ${aSCHEDULE_AVAILABLE_PRIORITY_MAX[$index_schedule]} + 1 )); i++))
									do
										local description=""
										if (( $i == $scale_value_lowest )); then

											description="(Lowest priority)"

										elif (( $i == $scale_value_lower )); then

											description="(Lower priority)"

										elif (( $i == $scale_value_low )); then

											description="(Low priority)"

										elif (( $i == $scale_value_medium )); then

											description="(Medium priority)"

										elif (( $i == $scale_value_high )); then

											description="(High priority)"

										elif (( $i == $scale_value_higher )); then

											description="(Higher priority)"

										elif (( $i == $scale_value_highest )); then

											description="(Highest priority)"

										fi

										whiptail_menu_array+=("$i" ": $description")

									done

									setvar WHIP_QUESTION = ""Please select a Schedule Priority level for :\n${aNAME[$index_current]}\n\n - Lower values are low priority\n - Higher values are high priority""
									setvar OPTION = $(whiptail --title "Schedule Policy : ${aNAME[$index_current]}" --backtitle "$PROGRAM_NAME" --menu "$WHIP_QUESTION" --cancel-button "Back" --default-item "${aSCHEDULE_PRIORITY[$index_current]}" 18 60 5 "${whiptail_menu_array[@]}" 3>&1 1>&2 2>&3)
									setvar CHOICE = ""$?

									unset whiptail_menu_array

									if (( $CHOICE == 0 )) {

										setvar aSCHEDULE_PRIORITY[$index_current]=$OPTION

										#Apply
										Apply_Process_Tool

									}

								}
							}

						}

					#Return to main menu
					} else {

						setvar run_loop = '0'

					}

				}

			}

		} else {

			Menu_Exit

		}

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# MAIN
	#/////////////////////////////////////////////////////////////////////////////////////
	#-----------------------------------------------------------------------------------
	#Init
	Init
	#-----------------------------------------------------------------------------------
	#Run config menu
	if (( $INPUT == 0 )) { {

			clear

			if (( $TARGETMENUID == 0 )) {

				Menu_Main

			}

		}

		#Save settings
		Write_Settings_File

	#Apply Process Tool
	} elif (( $INPUT == 1 )) {

		Apply_Process_Tool
		G_DIETPI-NOTIFY -1 ${EXIT_CODE:=0} "$PROGRAM_NAME |"

	}
	#-----------------------------------------------------------------------------------
	#Del tmp files
	rm $FP_TEMP &> /dev/null
	rm $FP_PS_LIST &> /dev/null
	#-----------------------------------------------------------------------------------
	#Delete[] Global arrays
	unset aNICE
	unset aAFFINITY
	unset aSCHEDULE_POLICY
	unset aSCHEDULE_PRIORITY
	unset aPID
	unset aNAME
	unset aPROCESS_NAME
	unset aAVAILABLE
	unset aSCHEDULE_AVAILABLE_POLICY
	unset aSCHEDULE_AVAILABLE_PRIORITY_MIN
	unset aSCHEDULE_AVAILABLE_PRIORITY_MAX
	#-----------------------------------------------------------------------------------
	exit
	#-----------------------------------------------------------------------------------
}