#!/bin/bash

# i3hwlock
#
# Allows locking i3 by the presence of a file. This file is intended to be
# created using udev rules so that you can trigger the lock when you remove
# a device such as a YubiKey. Example rule below.
#
# /etc/udev/rules.d/99-i3hwlock-yubikey.rules:
# ACTION=="add", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="1050", RUN+="/usr/bin/rm /tmp/i3hwlock.trigger"
# ACTION=="remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="1050", RUN+="/usr/bin/touch /tmp/i3hwlock.trigger"

LOCK_TIMEOUT=3 # How many seconds to wait before locking.
DMENU_FILE="/tmp/i3hwlock"
TRIGGER_FILE="$DMENU_FILE.trigger"
ABORT_FILE="$DMENU_FILE.abort"
CONTINUE_FILE="$DMENU_FILE.continue"
LOCKING_FILE="$DMENU_FILE.locking"
DMENU_ARGS="-i -nb #e74c3c -nf #303030 -sb #c0392b -sf #ecf0f1"

function check_prerequisites() {
	[ -e "$ABORT_FILE" ] && rm "$ABORT_FILE"

	if ! which inotifywait >/dev/null; then
		echo "ERROR: inotifywait not found." 1>&2
		return 1
	fi
}

function dmenu_selection() {
	# shellcheck disable=SC2086
	selection="$(xargs -n 1 <<<"Continue Abort" | dmenu $DMENU_ARGS -p "The screen is about to lock")"
	[ -z "$selection" ] && return
	file="$DMENU_FILE.${selection,,}"
	echo "INFO: Touching $file"
	touch "$file"
}

function abort_lock() {
	[ -e "$ABORT_FILE" ] && rm "$ABORT_FILE"

	if ! [ -f "$LOCKING_FILE" ]; then
		echo "WARN: Abort initiated without file '$LOCKING_FILE'." 1>&2
		return 0
	fi

	echo "INFO: Aborting lock."
	killall dmenu
	notify-send "Lock aborted." &
	[ -e $LOCKING_FILE ] && rm "$LOCKING_FILE"
}

function lock() {
	[ -e "$CONTINUE_FILE" ] && rm "$CONTINUE_FILE"

	if ! [ -f "$LOCKING_FILE" ]; then
		echo "WARN: Won't lock as file '$LOCKING_FILE' is missing." 1>&2
		return 0
	fi

	echo "INFO: Locking now."
	killall dmenu
	i3exit lock 2>/dev/null || i3lock --color=29414f --nofork
	[ -e $LOCKING_FILE ] && rm "$LOCKING_FILE"
}

function delayed_lock() {
	echo "INFO: Delayed lock initiated."
	touch "$DMENU_FILE.locking"

	for i in $(seq $LOCK_TIMEOUT); do
		notify-send "Locking in $((LOCK_TIMEOUT - i + 1))" -t 1000 &
		sleep 1

		if [ -e "$ABORT_FILE" ]; then
			abort_lock
			return 0
		elif [ -e "$CONTINUE_FILE" ]; then
			break
		fi
	done

	! [ -e $LOCKING_FILE ] && return 0
	lock
}

function listen() {
	while read -r directory action file; do
		if [[ "$file" == "${TRIGGER_FILE##*/}" ]]; then
			if [[ "$action" == "CLOSE_WRITE,CLOSE" ]] && ! [ -e "$LOCKING_FILE" ]; then
				$0 dmenu-selection &
				$0 delayed-lock &
			elif [[ "$action" == "DELETE" ]]; then
				abort_lock
			fi
		fi
	done < <(inotifywait -m "${TRIGGER_FILE%/*}")
}

check_prerequisites || exit 1

if [ -z "$1" ]; then
	listen || exit 2
elif [[ "$1" == delayed-lock ]]; then
	delayed_lock || exit 3
elif [[ "$1" == dmenu-selection ]]; then
	dmenu_selection || exit 4
else
	echo "ERROR: Unrecognized action: '$1'." 1>&2
	exit 127
fi
