#!/bin/bash

if [ -z "$(type -p xdotool)" ]; then
	echo 'Install xdotool!' >&2
	exit 1
fi

if [ -z "$(type -p numlockx)" ]; then
	echo 'Install numlockx!' >&2
	exit 1
fi

if [ -t 1 ]; then
	echo 'Redirect the output to a file!' >&2
	exit 1
fi

function print_header {
	local i
	printf '%15s'
	for i; do
		if [ "$i" = "|" ]; then
			printf ' |'
		else
			printf ' %-9.9s' "$i"
		fi
	done
	echo
}

function do_reset {
	echo -ne '\e[?1050l' >&2
	echo -ne '\e[?1051l' >&2
	echo -ne '\e[?1052l' >&2
	echo -ne '\e[?1053l' >&2
	echo -ne '\e[?1060l' >&2
	echo -ne '\e[?1061l' >&2
	echo -ne '\e[?1l' >&2  # normal cursor mode
	echo -ne '\e>' >&2     # normal keypad mode
}

function do_keys {
	local shift="$1"
	local ctrl="$2"
	local alt="$3"
	local numlock="$4"
	shift 4

	local i key keys line esc
	local mod=""
	local label=""

	if [ "$shift" = "1" ]; then
		mod="${mod}Shift+"
		label="${label}S+"
	fi
	if [ "$ctrl" = "1" ]; then
		mod="${mod}Ctrl+"
		label="${label}C+"
	fi
	if [ "$alt" = "1" ]; then
		mod="${mod}Alt+"
		label="${label}A+"
	fi
	if [ "$label" = "" ]; then
		label="<plain>+"
	fi
	if [ "$numlock" = "1" ]; then
		numlockx on
		label="${label}Num+"
	else
		numlockx off
	fi
	label="${label%+}"

	printf '    %-11s' "$label"
	echo "###     $label" >&2
	for i; do
		if [ $i = '|' ]; then
			printf ' |'
			continue
		fi

		skip=0
		if [[ $i =~ F && $alt = 1 ]]; then
			# Alt+Fx are usually window manager actions
			skip=1
		elif [[ $i = F10 && $shift = 1 ]]; then
			# Shift+F10 brings up the menu in g-t
			skip=1
		elif [[ $i =~ Page && $ctrl = 1 ]]; then
			# Ctrl+{PgUp,PgDn} switches tab in g-t
			skip=1
		elif [[ $i =~ Insert && ( $shift = 1 || $ctrl = 1 ) ]]; then
			# {Shift,Ctrl}+Insert is about mouse selection
			skip=1
		elif [[ ( $i =~ Home || $i =~ End || $i =~ Page ) && $shift = 1 ]]; then
			# Shift+{PgUp,PgDn,Home,End} effect the scrollbar
			skip=1
		elif [[ ( $i =~ Up || $i =~ Down ) && $shift = 1 && $ctrl = 1 ]]; then
			# Ctrl+Shift+{Up,Dn} also effect the scrollbar
			skip=1
		elif [[ ( $i =~ Add || $i =~ Sub ) && ( $shift = 1 || $ctrl = 1 ) ]]; then
			# Ctrl+{+,-} changes window size in vteapp, Shift+{+,-} does in xterm
			skip=1
		fi

		if [ $skip = 0 ]; then
			xdotool key $mod$i Return
			IFS='' read -r esc
			esc="${esc/$'\033'/^[}"  # replace actual escape character with two-character ascii representation
		else
			esc='(skipped)'
		fi
		printf ' %-9s' "$esc"
	done
	echo

	# a bit of healthy paranoia
	xdotool key at Return
	IFS='' read -r esc
	if [ "$esc" != '@' ]; then
		echo 'Sync error!' >&2
		exit 1
	fi
}

function do_one_cursor_and_keypad_mode {
	local keys='| F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 | Up KP_Up Down KP_Down Right KP_Right Left KP_Left | Insert KP_Insert Delete KP_Delete | Home KP_Home End KP_End | Page_Up KP_Page_Up Page_Down KP_Page_Down | KP_Begin | KP_Divide KP_Multiply KP_Subtract KP_Add |'
	print_header $keys
	do_keys 0 0 0 0 $keys
	do_keys 0 0 0 1 $keys
	do_keys 1 0 0 0 $keys
	do_keys 1 0 0 1 $keys
	do_keys 0 1 0 0 $keys
	do_keys 0 1 0 1 $keys
	do_keys 0 0 1 0 $keys
	do_keys 0 0 1 1 $keys
	echo
}

function do_fkeys_mode {
	local name="$1"
	local dec="$2"

	echo "$name"
	echo "### $name" >&2

	do_reset
	if [ $dec != 0 ]; then
		echo -ne "\e[?${dec}h" >&2
	fi

	echo '  normal'
	echo '###   normal' >&2
	do_one_cursor_and_keypad_mode

	echo -ne '\e[?1h' >&2  # application cursor mode
	echo '  cursor'
	echo '###   cursor' >&2
	do_one_cursor_and_keypad_mode

	echo -ne '\e[?1l' >&2  # normal cursor mode
	echo -ne '\e=' >&2     # application keypad
	echo '  keypad'
	echo '###   keypad' >&2
	do_one_cursor_and_keypad_mode

	echo -ne '\e[?1h' >&2  # application cursor mode
	echo '  cursor+keypad'
	echo '###   cursor+keypad' >&2
	do_one_cursor_and_keypad_mode

	echo
}

cat <<END >&2

Don't do anything!
Don't touch your keyboard!  Not even modifiers or numlock!
Don't change window focus!
Just leave it running!

Make sure to have English keyboard layout.

Press Ctrl+C if you really need to abort.

END
for i in {10..1}; do
	echo -n "$i... " >&2
	sleep 1
done
echo >&2

# Sometimes the first run of xdotool produces some garbage, no clue why.
xdotool key T e s t space 1 Return
read x
xdotool key T e s t space 2 Return
read x
if [ "$x" != 'Test 2' ]; then
	echo "Oops, xdotool doesn't seem to work!" >&2
	exit 1
fi

# Let's go!

trap 'do_reset; echo >&2; echo >&2; echo "If your keyboard is stuck, press and release the modifiers one by one" >&2' EXIT

do_fkeys_mode DEFAULT 0
#do_fkeys_mode TERMCAP 1050  # not supported by vte
do_fkeys_mode SUN 1051
do_fkeys_mode HP 1052
#do_fkeys_mode SCO 1053  # not supported by vte
do_fkeys_mode LEGACY 1060
do_fkeys_mode VT220 1061

do_reset

echo >&2
echo "Ready." >&2

trap EXIT
