#!/usr/bin/env bash

# SPDX-License-Identifier: MIT

set -euo pipefail

root=${TOUCHPAD_HOLD_TAP_TEST_ROOT:-}
config_file="$root/etc/touchpad-hold-tap.conf"
override_file="$root/etc/libinput/plugins/90-touchpad-hold-tap.lua"
template_file="$root/usr/lib64/libinput/plugins/90-touchpad-hold-tap.lua"
if [[ ! -e $template_file ]]; then
    template_file="$root/usr/lib/libinput/plugins/90-touchpad-hold-tap.lua"
fi

minimum_anchor_age_ms=100
minimum_pause_ms=100
minimum_tap_ms=10
maximum_tap_ms=150
output_event=BTN_MIDDLE

die()
{
    printf 'touchpad-hold-tap-config: %s\n' "$*" >&2
    exit 1
}

require_root()
{
    if [[ -z $root && $EUID -ne 0 ]]; then
        die "this operation must be run as root"
    fi
}

validate()
{
    [[ $minimum_anchor_age_ms =~ ^[0-9]+$ ]] || die "minimum anchor age must be an integer"
    [[ $minimum_pause_ms =~ ^[0-9]+$ ]] || die "minimum pause must be an integer"
    [[ $minimum_tap_ms =~ ^[0-9]+$ ]] || die "minimum tap duration must be an integer"
    [[ $maximum_tap_ms =~ ^[0-9]+$ ]] || die "maximum tap duration must be an integer"
    (( minimum_anchor_age_ms <= 2000 )) || die "minimum anchor age must not exceed 2000 ms"
    (( minimum_pause_ms <= 2000 )) || die "minimum pause must not exceed 2000 ms"
    (( maximum_tap_ms >= 1 && minimum_tap_ms <= maximum_tap_ms && maximum_tap_ms <= 2000 )) || \
        die "tap durations must be between 0 and 2000 ms, with a positive maximum and minimum not exceeding maximum"
    [[ $output_event =~ ^(BTN|KEY)_[A-Z0-9_]+$ ]] || \
        die "output event must be an evdev BTN_* or KEY_* name"
}

read_config()
{
    if [[ ! -r $config_file ]]; then
        return 0
    fi

    while IFS='=' read -r key value; do
        case $key in
            MINIMUM_ANCHOR_AGE_MS) minimum_anchor_age_ms=$value ;;
            MINIMUM_PAUSE_MS) minimum_pause_ms=$value ;;
            MINIMUM_TAP_MS) minimum_tap_ms=$value ;;
            MAXIMUM_TAP_MS) maximum_tap_ms=$value ;;
            OUTPUT_EVENT) output_event=$value ;;
            ''|'#'*) ;;
            *) die "unknown setting: $key" ;;
        esac
    done < "$config_file"
    validate
}

write_config()
{
    mkdir -p "$(dirname -- "$config_file")"
    local temporary
    temporary=$(mktemp "${config_file}.XXXXXX")
    trap 'rm -f -- "$temporary"' RETURN
    printf 'MINIMUM_ANCHOR_AGE_MS=%s\nMINIMUM_PAUSE_MS=%s\nMINIMUM_TAP_MS=%s\nMAXIMUM_TAP_MS=%s\nOUTPUT_EVENT=%s\n' \
        "$minimum_anchor_age_ms" "$minimum_pause_ms" "$minimum_tap_ms" "$maximum_tap_ms" \
        "$output_event" > "$temporary"
    chmod 0644 "$temporary"
    mv -f -- "$temporary" "$config_file"
    trap - RETURN
}

write_override()
{
    [[ -r $template_file ]] || die "installed plugin was not found"
    mkdir -p "$(dirname -- "$override_file")"
    local temporary
    temporary=$(mktemp "${override_file}.XXXXXX")
    trap 'rm -f -- "$temporary"' RETURN
    awk -v age="$minimum_anchor_age_ms" -v pause="$minimum_pause_ms" \
        -v mintap="$minimum_tap_ms" \
        -v maxtap="$maximum_tap_ms" \
        -v output="$output_event" '
        /^local settings = / {
            print "local settings = {"
            print "    minimum_anchor_age_ms = " age ","
            print "    minimum_pause_ms = " pause ","
            print "    minimum_tap_ms = " mintap ","
            print "    maximum_tap_ms = " maxtap ","
            print "    output_event = \"" output "\","
            print "}"
            replaced = 1
            next
        }
        { print }
        END { if (!replaced) exit 1 }
    ' "$template_file" > "$temporary" || die "installed plugin has an unsupported format"
    chmod 0644 "$temporary"
    mv -f -- "$temporary" "$override_file"
    trap - RETURN
}

usage()
{
    cat <<'EOF'
Usage:
  touchpad-hold-tap-config show
  touchpad-hold-tap-config set [--minimum-anchor-age-ms N] [--minimum-pause-ms N] [--minimum-tap-ms N] [--maximum-tap-ms N] [--output-event NAME]
  touchpad-hold-tap-config reset
  touchpad-hold-tap-config refresh
EOF
}

command=${1:-}
case $command in
    show)
        [[ $# -eq 1 ]] || die "show takes no arguments"
        read_config
        printf 'MINIMUM_ANCHOR_AGE_MS=%s\nMINIMUM_PAUSE_MS=%s\nMINIMUM_TAP_MS=%s\nMAXIMUM_TAP_MS=%s\nOUTPUT_EVENT=%s\n' \
            "$minimum_anchor_age_ms" "$minimum_pause_ms" "$minimum_tap_ms" "$maximum_tap_ms" \
            "$output_event"
        ;;
    set)
        shift
        read_config
        while [[ $# -gt 0 ]]; do
            case $1 in
                --minimum-anchor-age-ms)
                    [[ $# -ge 2 ]] || die "$1 requires a value"
                    minimum_anchor_age_ms=$2
                    shift 2
                    ;;
                --minimum-pause-ms)
                    [[ $# -ge 2 ]] || die "$1 requires a value"
                    minimum_pause_ms=$2
                    shift 2
                    ;;
                --minimum-tap-ms)
                    [[ $# -ge 2 ]] || die "$1 requires a value"
                    minimum_tap_ms=$2
                    shift 2
                    ;;
                --maximum-tap-ms)
                    [[ $# -ge 2 ]] || die "$1 requires a value"
                    maximum_tap_ms=$2
                    shift 2
                    ;;
                --output-event)
                    [[ $# -ge 2 ]] || die "$1 requires a value"
                    output_event=$2
                    shift 2
                    ;;
                *) die "unknown option: $1" ;;
            esac
        done
        require_root
        validate
        write_config
        write_override
        ;;
    reset)
        [[ $# -eq 1 ]] || die "reset takes no arguments"
        require_root
        rm -f -- "$config_file" "$override_file"
        ;;
    refresh)
        [[ $# -eq 1 ]] || die "refresh takes no arguments"
        require_root
        if [[ -e $config_file ]]; then
            read_config
            write_override
        else
            rm -f -- "$override_file"
        fi
        ;;
    -h|--help)
        usage
        ;;
    *)
        usage >&2
        exit 2
        ;;
esac
