#!/usr/bin/env bash
# TUI for creating/updating device-specific Hyprland input overrides.
# Usage: mk-device-exception.sh <target-lua-file>
set -euo pipefail

die() { printf 'error: %s\n' "$*" >&2; exit 1; }

[[ $# -eq 1 ]] || die "Usage: ${0##*/} <target-lua-file>"
target="$1"

command -v hyprctl &>/dev/null || die "hyprctl not found"
command -v dialog  &>/dev/null || die "dialog not found"
command -v jq      &>/dev/null || die "jq not found"

# ---------- device selection ----------

devices=$(hyprctl devices -j)
entries=$(jq -r '
    (.keyboards[]?.name | "keyboard\t" + .),
    (.mice[]?.name     | "mouse\t"    + .)
' <<<"$devices")

[[ -z "$entries" ]] && die "No keyboard or mouse devices found"

if command -v fzf &>/dev/null; then
    selected=$(printf '%s\n' "$entries" \
        | fzf --prompt="Select device: " --height=~10 --with-nth=2.. --delimiter=$'\t')
else
    echo "Select a device:" >&2
    IFS=$'\n'
    select selected in $entries; do [[ -n "$selected" ]] && break; done
    unset IFS
fi

[[ -z "$selected" ]] && exit 1

type=${selected%%$'\t'*}
name=${selected#*$'\t'}

# ---------- parse existing field value from target file ----------
# Returns the Lua literal for the field (e.g. `"de"`, `15`, `false`).

get_field() {
    local field="$1" default="$2"
    [[ -f "$target" ]] || { printf '%s' "$default"; return; }
    awk -v dev="$name" -v fld="$field" -v def="$default" '
        /^hl\.device\(\{/   { blk=""; in_blk=1; matched=0 }
        in_blk              { blk = blk $0 "\n" }
        in_blk && /name = / { if ($0 ~ "\"" dev "\"") matched=1 }
        in_blk && /^\}\)/   {
            if (matched) {
                n = split(blk, lines, "\n")
                for (i = 1; i <= n; i++) {
                    line = lines[i]
                    sub(/^[[:space:]]+/, "", line)            # strip indentation
                    if (line ~ "^" fld "[[:space:]]*=") {
                        sub(/^[^=]+=/, "", line)              # drop "fieldname = "
                        sub(/^[[:space:]]*/, "", line)        # strip leading space
                        sub(/[[:space:]]*,.*$/, "", line)     # drop trailing comma and any inline comment
                        print line; exit
                    }
                }
                print def; exit
            }
            in_blk = 0
        }
    ' "$target"
}

# Strip surrounding Lua string quotes for display in form ("de" -> de).
unquote() { local v="$1"; v="${v#\"}"; v="${v%\"}"; printf '%s' "$v"; }

# ---------- show form and collect values ----------

case "$type" in
keyboard)
    v_layout=$(unquote "$(get_field kb_layout '"de"')")
    v_variant=$(unquote "$(get_field kb_variant '""')")
    v_model=$(unquote "$(get_field kb_model '""')")
    v_options=$(unquote "$(get_field kb_options '""')")
    v_rules=$(unquote "$(get_field kb_rules '""')")
    v_rate=$(get_field repeat_rate 0)
    v_delay=$(get_field repeat_delay 0)
    v_sens=$(get_field sensitivity 0)

    # dialog writes its TUI to stdout (/dev/tty) and the result to stderr.
    values=$(dialog \
        --title "Keyboard exception: $name" \
        --form "" 16 74 8 \
        "kb_layout"    1 1 "$v_layout"   1 17 42 0 \
        "kb_variant"   2 1 "$v_variant"  2 17 42 0 \
        "kb_model"     3 1 "$v_model"    3 17 42 0 \
        "kb_options"   4 1 "$v_options"  4 17 42 0 \
        "kb_rules"     5 1 "$v_rules"    5 17 42 0 \
        "repeat_rate"  6 1 "$v_rate"     6 17 10 0 \
        "repeat_delay" 7 1 "$v_delay"    7 17 10 0 \
        "sensitivity"  8 1 "$v_sens"     8 17 10 0 \
        2>&1 >/dev/tty) || exit 0

    mapfile -t vals <<< "$values"
    new_block="hl.device({
    name = \"$name\",
    kb_layout  = \"${vals[0]:-}\",
    kb_variant = \"${vals[1]:-}\",
    kb_model   = \"${vals[2]:-}\",
    kb_options = \"${vals[3]:-}\",
    kb_rules   = \"${vals[4]:-}\",
    repeat_rate  = ${vals[5]:-0},
    repeat_delay = ${vals[6]:-0},
    sensitivity  = ${vals[7]:-0},
})"
    ;;

mouse)
    v_sens=$(get_field sensitivity 0)
    v_accel=$(unquote "$(get_field accel_profile '""')")
    v_left=$(get_field left_handed false)
    v_natural=$(get_field natural_scroll false)
    v_scroll=$(unquote "$(get_field scroll_points '""')")

    values=$(dialog \
        --title "Mouse exception: $name" \
        --form "" 13 74 5 \
        "sensitivity"    1 1 "$v_sens"    1 20 20 0 \
        "accel_profile"  2 1 "$v_accel"   2 20 20 0 \
        "left_handed"    3 1 "$v_left"    3 20 10 0 \
        "natural_scroll" 4 1 "$v_natural" 4 20 10 0 \
        "scroll_points"  5 1 "$v_scroll"  5 20 20 0 \
        2>&1 >/dev/tty) || exit 0

    mapfile -t vals <<< "$values"
    new_block="hl.device({
    name = \"$name\",
    sensitivity    = ${vals[0]:-0},
    accel_profile  = \"${vals[1]:-}\",
    left_handed    = ${vals[2]:-false},
    natural_scroll = ${vals[3]:-false},
    scroll_points  = \"${vals[4]:-}\",
})"
    ;;
esac

# ---------- replace or append in target file ----------

[[ -f "$target" ]] || touch "$target"

# Remove the existing block for this device (no-op if not present).
tmp=$(mktemp)
awk -v dev="$name" '
    /^hl\.device\(\{/   { blk = $0 "\n"; in_blk=1; matched=0; next }
    in_blk              {
        blk = blk $0 "\n"
        if (/name = / && ($0 ~ "\"" dev "\"")) matched=1
        if (/^\}\)/) {
            if (!matched) printf "%s", blk
            in_blk=0; blk=""
        }
        next
    }
    { print }
' "$target" > "$tmp"
mv "$tmp" "$target"

# Append the new block, separated by a blank line if the file is non-empty.
if [[ -s "$target" ]]; then
    [[ "$(tail -c 1 "$target")" != $'\n' ]] && printf '\n' >> "$target"
    printf '\n' >> "$target"
fi
printf '%s\n' "$new_block" >> "$target"

printf 'Written to %s\n' "$target"
