#!/usr/bin/env bash

# Focus the window if it is already open; start it if it is not.
#
#   panama-launch --class '^helium$' -- helium-browser-bin
#   panama-launch --class '^kitty$' --title 'nvim' -- kitty nvim .
#
# This is what the application keys do on every other desktop. Pressing the
# browser key twice on macOS or Windows raises the browser; here it used to
# open a second one, which is the single most common "Linux feels wrong"
# moment and a twenty-line fix.
#
# Matching is a regular expression against the window class, optionally
# narrowed by title. Both halves matter: the terminal and the editor are both
# kitty on this desktop, and only the title tells them apart -- so a class-only
# match would make the editor key raise whatever terminal happened to be open.
#
# Anchor your patterns. `--class mail` would match `gmail-notifier`, and the
# key that should open Thunderbird would raise somebody's notifier instead.

set -uo pipefail

class_pattern=""
title_pattern=""

while (( $# > 0 )); do
    case "$1" in
        --class) class_pattern="${2:-}"; shift 2 ;;
        --title) title_pattern="${2:-}"; shift 2 ;;
        --)      shift; break ;;
        *)       break ;;
    esac
done

if [[ -z "$class_pattern" || $# -eq 0 ]]; then
    echo 'usage: panama-launch --class <regex> [--title <regex>] -- command [args...]' >&2
    exit 2
fi

launch() {
    # setsid so the application outlives this script and is not a child of the
    # compositor's exec, which would tie its lifetime to a shell that exits.
    setsid "$@" >/dev/null 2>&1 &
    exit 0
}

# No compositor, no window list: just start the thing.
command -v hyprctl >/dev/null 2>&1 || launch "$@"

address="$(hyprctl clients -j 2>/dev/null | jq -r --arg class "$class_pattern" --arg title "$title_pattern" '
    [ .[]
      | select(.mapped)
      | select(.class | test($class))
      | select($title == "" or (.title | test($title)))
    ]
    # Most recently focused first: with several matches, raise the one the
    # user was last in rather than whichever the compositor lists first.
    | sort_by(-.focusHistoryID)
    | .[0].address // empty
' 2>/dev/null)"

if [[ -n "$address" ]]; then
    # Hyprland 0.56 dispatches through Lua: `hyprctl dispatch focuswindow
    # address:0x...` is parsed as Lua source and fails. The selector string is
    # what hl.focus accepts; a table of the same fields is refused.
    exec hyprctl dispatch "hl.dsp.focus({ window = \"address:$address\" })"
fi

launch "$@"
