#!/usr/bin/env python3
# FWS — affiche les raccourcis clavier RÉELS de Hyprland.
#
# Source de vérité : « hyprctl binds -j » = les binds effectivement CHARGÉS
# par le compositeur (ceux de ~/.config/hypr/hyprland.conf, y compris ceux
# ajoutés par l'utilisateur après l'installation). Les descriptions viennent
# des « bindd » de la config ; un bind sans description affiche son action
# brute (dispatcher + argument). Affichage : wofi en mode dmenu (recherche
# incrémentale intégrée) — déclenché par le bouton ⌨ de la waybar ou SUPER+K.
#
# python3 et wofi sont déjà sur le système (image FWS + paquets Hyprland) :
# aucune dépendance supplémentaire.

import json
import subprocess
import sys

# Bits de modmask (masques X11) dans l'ordre d'affichage voulu.
_MODS = [
    (64, "SUPER"),
    (4, "CTRL"),
    (8, "ALT"),
    (1, "SHIFT"),
]

# Libellés humains pour les « touches » spéciales.
_KEY_LABELS = {
    "mouse:272": "clic gauche",
    "mouse:273": "clic droit",
    "mouse:274": "clic molette",
    "mouse_down": "molette bas",
    "mouse_up": "molette haut",
    "return": "Entrée",
    "space": "Espace",
    "escape": "Échap",
    "tab": "Tab",
    "left": "←",
    "right": "→",
    "up": "↑",
    "down": "↓",
}


def _mods(mask):
    return [name for bit, name in _MODS if mask & bit]


def _combo(bind):
    parts = _mods(bind.get("modmask", 0))
    key = bind.get("key") or ""
    if not key and bind.get("keycode"):
        key = f"code {bind['keycode']}"
    parts.append(_KEY_LABELS.get(key.lower(), key))
    return " + ".join(p for p in parts if p)


def _action(bind):
    desc = (bind.get("description") or "").strip()
    if desc:
        return desc
    arg = (bind.get("arg") or "").strip()
    return f"{bind.get('dispatcher', '?')} {arg}".strip()


def main():
    try:
        raw = subprocess.check_output(["hyprctl", "binds", "-j"], text=True)
        binds = json.loads(raw)
    except (OSError, subprocess.CalledProcessError, ValueError) as exc:
        subprocess.run(["notify-send", "Raccourcis FWS",
                        f"hyprctl binds a échoué : {exc}"], check=False)
        return 1

    lines = []
    seen = set()
    for bind in binds:
        combo = _combo(bind)
        if not combo or combo in seen:  # binde répétés (resize) : une ligne
            continue
        seen.add(combo)
        lines.append(f"{combo:<24}  {_action(bind)}")

    if not lines:
        lines = ["(aucun raccourci déclaré)"]

    # dmenu = liste + filtre incrémental ; la sélection n'a pas d'effet.
    subprocess.run(
        ["wofi", "--dmenu", "--prompt", "Raccourcis clavier — taper pour filtrer",
         "--width", "780", "--height", "560", "--insensitive", "--cache-file", "/dev/null"],
        input="\n".join(lines), text=True, check=False)
    return 0


if __name__ == "__main__":
    sys.exit(main())
