#!/bin/sh
SCAN_PERIOD=5
AUTOTRUST=false #trust every newly paired device
AUTOSCAN=false #immediately start scanning when started to speed up pairing process
#If you want to update symbols in your statusbar add your details below:
bar="dwmblocks"
barsignal="4"

#locations of blacklist and hard coded list of paired devices (watch out, need to modify Makefile as well if you want to change these values and still use make install)
BLACKLIST=${XDG_CONFIG_HOMe:-$HOME/.config}/bt/blacklist
PAIRLIST=${XDG_CONFIG_HOME:-$HOME/.config}/bt/paired
ALIASLIST=${XDG_CONFIG_HOME:-$HOME/.config}/bt/alias

actions="$(printf 'pair\nunpair\n')"
#disconnect menu does not need to be displayed since just directly selecting the connected device will disconnect it
#set to anything non-empty to enable
include_disconnect_option=""

[ "$AUTOTRUST" = false ] && actions="$(printf '%s\ntrust\n' "$actions")"

power(){
	powerstatus="$( bluetoothctl show | grep Powered | awk '{print $2}' )"
	if [ "$powerstatus" = "no" ]; then
		[ "$1" = on ] && bluetoothctl power on
	elif [ "$powerstatus" = "yes" ]; then
		[ "$1" = off ] && bluetoothctl power off
	fi
}

scan(){
	scanstatus="$( bluetoothctl show | grep Discovering | awk '{print $2}' )"
	if [ "$1" = on ]; then
		#sets variable in case scanning was no already on before the start of bt
		[ -f /tmp/bt_start_scan$$ ] || start_scan="$( date +'%s' )"
		if [ "$scanstatus" = "no" ]; then
			bluetoothctl scan on  &
			start_scan="$( date +'%s' )"
		fi
		echo "$start_scan" > /tmp/bt_start_scan$$
	elif [ "$1" = off ]; then
		if [ "$scanstatus" = "yes" ]; then
			bluetoothctl scan off
		fi
	fi
}

#Check bluetoothctl paired-devices for new devices to be hardcoded into pair list
update_pair_list(){
	btctl_paired_devices="$(bluetoothctl paired-devices)"
	if [ -n "$btctl_paired_devices" ]
	then
		btctl_not_in_list="$( echo "$btctl_paired_devices" | grep -vf "$PAIRLIST")"
		[ -n "$btctl_not_in_list" ] && echo "$btctl_not_in_list" >> "$PAIRLIST"
	fi
}


#start scanning as early as possible to speed up pairing process
startup(){
	if $AUTOSCAN
	then
		power on
		scan on > /dev/null
	fi
}


if [ "$1" = "edit" ]; then
	case $2 in
		"pairlist")${EDITOR:-vim} "$PAIRLIST";;
		"blacklist")${EDITOR:-vim} "$BLACKLIST";;
		"aliaslist")${EDITOR:-vim} "$ALIASLIST";;
		*)true;;
		esac
	exit
fi
#Sadly needs to be run without '&' for now, since it otherwise breaks start_scan variable
startup &
update_pair_list &

connected_ids="$(bluetoothctl devices | awk '{print "bluetoothctl info "$2}' | . /dev/stdin | grep -E '(^Device |Connected:)' | sed -e  'N;s/\n/;/' | grep 'Connected: yes' | cut -d' ' -f2,5)"
connected_devices="$(echo "$connected_ids" | while read -r id; do
	if grep -q "$id" "$ALIASLIST"; then
		grep "$id" "$ALIASLIST" | cut -d' ' --complement -f1
	else
		grep "$id" "$PAIRLIST" | cut -d' ' --complement -f1,2
	fi
done)"

[ -n "$include_disconnect_option" ] &&
	[ -n "$connected_devices" ] &&
	actions="$(printf "disconnect\n%s" "$actions")"

#Compile list of all Bluetooth IDS of paired devices (from bluetoothctl and from hardcoded list)
bt_IDS="$( ( bluetoothctl paired-devices && cat "$PAIRLIST" ) | awk '{print $2}' | sort -u )"

#Compile list of all device Names of paired devices (from bluetoothctl and from hardcoded list)
paired_devices_with_id="$( ( bluetoothctl paired-devices && cat "$PAIRLIST" ) | sort -u | cut -d' ' --complement -f1)"
#de-alias
paired_devices="$(echo "$paired_devices_with_id" | while read -r device_line; do
	id="$(echo "$device_line" | cut -d' ' -f1)"
	if grep -q "$id" "$ALIASLIST"; then
		grep "$id" "$ALIASLIST" | cut -d' ' --complement -f1
	else
		echo "$device_line" | cut -d' ' --complement -f1
	fi
done)"

echo "paired_devices:$paired_devices"

disp_devices="$( echo "$paired_devices" | grep -vf "$BLACKLIST" )"

#show which devices are connected in menu
disp_devices_with_links="$(echo "$paired_devices" | while read -r device; do
	printf '%s' "$device"
	if echo "$device" | grep -q "$connected_devices"; then
		printf '🔗'
	fi
	printf '\n'
done
)"

#detects current power mode of controller and adjusts options accordingly
powerstatus="$( bluetoothctl show | grep Powered | awk '{print $2}' )"
poweroption="$( echo "$powerstatus" | sed 's/yes/power off/; s/no/power on/' )"
#Don't print empty device list, removes unnecessary empty choice in dmenu
if [ "$disp_devices" = "" ];then
	choice=$( printf "%s\n%s" "$actions" "$poweroption" |
		dmenu -i -p 'What BT action would you like to perform:')
else
	choice=$( ( echo "$disp_devices_with_links" && printf "%s\n%s" "$actions" "$poweroption" ) |
		dmenu -i -p 'What BT action would you like to perform:' | tr -d '🔗')
fi

cleanup(){
	scan off
	rm -f /tmp/bt_start_scan*
	pkill -RTMIN+$barsignal $bar
	exit 0
}

pair(){
	#since this function can get called indefinitely, make sure to always be scanning and controller has power in the case that it got deactived by some other process.
	power on
	scan on > /dev/null
	#check whether $SCAN_PERIOD seconds has already passed since starting scanning, if not, wait for the rest of that time.
	start_scan="$(cat /tmp/bt_start_scan$$)"
	if [ $(( $(date +'%s') - $start_scan )) -lt $SCAN_PERIOD ]; then
		sleep_period="$(( $SCAN_PERIOD - $( date +'%s') + $start_scan ))"
		[ "$sleep_period" -gt 1 ] && plural="s"
		notify-send "bt" "Searching for devices, please wait $sleep_period second$plural"
		sleep "$sleep_period"
	fi
	all_devices="$( bluetoothctl devices )"
	if [ "$paired_devices" = "" ]; then
		new_devices="$( echo "$all_devices" | awk '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"
		bt_IDS="$( echo "$all_devices" | awk '{print $2}' )"
	else
		#echo "$paired_devices" > /tmp/paired_devices$$
		filtered_devices="$( echo "$all_devices" | grep -v "$paired_devices")"
		bt_IDS="$( echo "$filtered_devices" | awk '{print $2}' )"
		new_devices="$( echo "$filtered_devices" | awk '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"
		#rm /tmp/paired_devices$$
	fi
	[ "$new_devices" = "" ] && options="rescan" || options=$(echo "$new_devices" && echo 'rescan')
	choice=$( echo "$options" | dmenu -l 10 -i -p 'pair with which device?' )
	if [ -n "$choice" ]; then
		if [ "$choice" = "rescan" ]; then
			start_scan="$( date +'%s')"
			echo "$start_scan" > /tmp/bt_start_scan$$
			pair
		else
			dev_no=$( echo "$new_devices" | nl | grep "$choice" | awk '{print $1}')
			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
			#only attempt to connect if pairing succeeds
			bluetoothctl pair "$dev_id" && ( pair_succesful=true && bluetoothctl connect "$dev_id" ) || pair_succesful=false
			if $AUTOTRUST; then
				bluetoothctl trust "$dev_id"
			fi
			#if device is not already hard coded as paired, add to paired devices list
			if $pair_succesful && [ "$( grep "$dev_id" "$PAIRLIST")" = "" ] || [ "$(wc -l < "$PAIRLIST")" -eq 0 ]
			then
				echo to be added to "$PAIRLIST":
				echo Device "$dev_id" "$choice"
				echo Device "$dev_id" "$choice" >> "$PAIRLIST"
			fi
		fi
	fi
}

case $choice in
	"power on") bluetoothctl power on;;
	"power off") bluetoothctl power off;;
	"scan on") bluetoothctl power on && echo power on && sleep 2
		([ -n "$TERMINAL" ] && $TERMINAL -e bluetoothctl scan on ) || st bluetoothctl scan on > /dev/null;;
	"pair") pair;;
	"unpair") choice=$( echo "$paired_devices" | dmenu -l 10 -i -p 'remove which paired device?')
		if [ -n "$choice" ]; then
			dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
			bluetoothctl remove "$dev_id"
			#remove device to unpair from hard coded paired devices list
			new_paired_list="$( grep -v "$dev_id" "$PAIRLIST" )"
			if [ "$new_paired_list" != "" ]
			then
				echo "$new_paired_list" > "$PAIRLIST"
			else
				rm -rf "$PAIRLIST"
				touch "$PAIRLIST"
			fi
		fi;;

	"trust")
		#search through all devices which are connected and only list those as options
		# shellcheck disable=SC1091
		untrusted_devices="$( awk '{print "bluetoothctl info "$2}' "$PAIRLIST" | . /dev/stdin | grep -E '(Alias:|Trusted:)' | sed -e  'N;s/\n/;/;s/^.?*Alias: //' | grep "Trusted: no" | awk -F ';' '{print $1}' )"
		if [ "$( echo "$untrusted_devices" | wc -l )" -gt 0 ]
		then
			choice=$( echo "$untrusted_devices" | dmenu -l 10 -i -p 'remove which paired device?')
		else
			notify-send "bt" "No paired devices that are not trusted"
		fi
		if [ -n "$choice" ]; then
			dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
			bluetoothctl trust "$dev_id"
		fi;;
	"disconnect")
		#search through all devices and check which are connected
		#only list those connceted devices as options
		# shellcheck disable=SC1091
		connected_ids="$(bluetoothctl devices | awk '{print "bluetoothctl info "$2}' | . /dev/stdin | grep -E '(^Device |Connected:)' | sed -e  'N;s/\n/;/' | grep 'Connected: yes' | cut -d' ' -f2,5)"
		connected_devices="$(echo "$connected_ids" | while read -r id; do
			if grep -q "$id" "$ALIASLIST"; then
				grep "$id" "$ALIASLIST" | cut -d' ' --complement -f1
			else
				grep "$id" "$PAIRLIST" | cut -d' ' --complement -f1,2
			fi
		done)"
		#only open dmenu prompt if there is more than one connected device
		if [ "$( echo "$connected_devices" | wc -l )" -gt 1 ]
		then
			choice=$( echo "$connected_devices" | dmenu -l 10 -i -p 'disconnect which paired device?')
	        else
			choice="$connected_devices"
		fi
		#only there was a choice (instead of canceling the dmenu)
		if [ -n "$choice" ]; then
			dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
			bluetoothctl disconnect "$dev_id"
		fi;;
	"blacklist")
		choice=$( echo "$paired_devices" | dmenu -l 10 -i -p 'blacklist which paired device from selection?')
		if [ -n "$choice" ]; then
			echo "$choice" >> "$BLACKLIST"
		fi;;
	*)
		echo "choice: $choice"
		dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
		[ "$dev_no" != "" ] && dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}')
		echo "dev_id: $dev_id"
		if [ -n "$dev_id" ]; then
			power on
			if bluetoothctl info "$dev_id" | grep -q "Connected: yes"
			then
				echo Was connceted... disconnecting
				bluetoothctl disconnect "$dev_id"
			else
				if ! bluetoothctl paired-devices | grep -q "$dev_id"; then
					echo "not in paired-devices list, re-adding (device needs to be in pairing mode for this)"
					notify-send "bt" "device not in paired-devices list, re-adding (device needs to be in pairing mode for this)"
					scan on
					echo "Scan on"
					sleep 5
					echo Attempting to pair...
					bluetoothctl pair "$dev_id"
					scan off
				fi
				echo connecting...
				bluetoothctl connect "$dev_id"
			fi
		fi;;
esac

cleanup