#!/bin/bash

# omarchy:summary=Toggle a scratch workspace (scratchpad, claude, fastmail, beeper), starting its resident apps when any is missing.

set -uo pipefail

# Usage: omarchy-toggle-scratch [scratchpad|claude|fastmail|beeper]
#
# Each profile is a special workspace, the monitor it opens on, and its
# resident apps (class pattern -> launch command; the pattern is an anchored
# ERE, so a plain class name matches itself). hypr/hyprland.lua pins every
# class to its workspace, so only the launch and the reveal are left here. A
# resident is launched only when no window on the workspace matches it, which
# is what keeps a launcher that always opens a fresh window (the Chromium
# web apps) from piling up duplicates. On first start the master layout gives
# the master slot to whichever window appears first (a terminal beats an
# Electron app); swap them afterwards if the arrangement matters.
#
# HOME_MONITOR is a connector name or an EDID description (the latter, as in
# the workspace rules, survives a different DP connector after re-docking).
# When it is empty or that monitor is absent, the workspace opens on the
# focused one.
HOME_MONITOR=""
case ${1:-scratchpad} in
scratchpad)
  NAME="scratchpad"
  HOME_MONITOR="Apple Computer Inc StudioDisplay 0x984262A7"
  MEMBERS=("so.stencil.tern|tern")
  ;;
claude)
  NAME="claude"
  HOME_MONITOR="Apple Computer Inc StudioDisplay 0x984262A7"
  MEMBERS=("com.anthropic.Claude|claude-desktop")
  ;;
fastmail)
  NAME="fastmail"
  MEMBERS=("com.fastmail.Fastmail|gtk-launch fastmail")
  ;;
beeper)
  NAME="beeper"
  MEMBERS=("Beeper|gtk-launch beeper")
  ;;
*)
  echo "usage: ${0##*/} [scratchpad|claude|fastmail|beeper]" >&2
  exit 2
  ;;
esac
WORKSPACE="special:$NAME"

# Classes of the windows living on the scratchpad, shown or hidden.
scratch_classes() {
  hyprctl clients -j | jq -r --arg ws "$WORKSPACE" '.[] | select(.workspace.name == $ws) | .class'
}

scratch_windows() {
  scratch_classes | wc -l
}

# Name of the monitor currently displaying the scratchpad, if any.
scratch_monitor() {
  hyprctl monitors -j | jq -r --arg ws "$WORKSPACE" '.[] | select(.specialWorkspace.name == $ws) | .name'
}

scratch_visible() {
  [[ -n $(scratch_monitor) ]]
}

# Hyprland's lua config takes lua expressions on `hyprctl dispatch`; the
# classic dispatcher is the pre-lua fallback (same shape as omarchy-cycle).
focus_monitor() {
  hyprctl dispatch "hl.dsp.focus({ monitor = \"$1\" })" >/dev/null 2>&1 ||
    hyprctl dispatch focusmonitor "$1" >/dev/null
}

# togglespecialworkspace acts on the focused monitor, so focus decides where
# the scratchpad appears (or which copy hides).
toggle_on() {
  focus_monitor "$1"
  hyprctl dispatch "hl.dsp.workspace.toggle_special(\"$NAME\")" >/dev/null 2>&1 ||
    hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null
}

# The profile's home monitor when connected, else whatever is focused.
target_monitor() {
  hyprctl monitors -j | jq -r --arg m "$HOME_MONITOR" \
    'first(.[] | select(.name == $m or .description == $m)) // first(.[] | select(.focused)) | .name'
}

# Showing: just hide, even if a member is gone. Missing members come back on
# the next reveal.
shown=$(scratch_monitor)
if [[ -n $shown ]]; then
  toggle_on "$shown"
  exit 0
fi

present=$(scratch_classes)
before=$(grep -c . <<<"$present")
launched=0
for member in "${MEMBERS[@]}"; do
  pattern=${member%%|*}
  grep -qE "^(${pattern})$" <<<"$present" && continue
  setsid uwsm-app -- ${member#*|} >/dev/null 2>&1 &
  launched=1
done

if ((launched)); then
  # Reveal once the first newcomer lands (Claude takes a few seconds); any
  # later ones drop straight onto the now-visible scratchpad. Deciding the
  # reveal after the window lands keeps this correct whether or not the rule
  # already showed the workspace.
  for _ in {1..80}; do
    sleep 0.1
    (($(scratch_windows) > before)) && break
  done
fi

scratch_visible || toggle_on "$(target_monitor)"
