#!/usr/bin/env python3
# Copyright (c) 2026 Norbert Jander – MIT-Lizenz (siehe LICENSE)
# Sitzungsmenü für das Plank-Dock: Herunterfahren, Neustarten, Abmelden,
# Benutzer wechseln, Bereitschaft, Abbrechen.
# Aufruf ohne Argument öffnet den Dialog, mit Argument wird die Aktion direkt ausgeführt.
import subprocess
import sys

ACTIONS = [
    ("shutdown", "Herunterfahren", "system-shutdown",
     ["cinnamon-session-quit", "--power-off", "--no-prompt"]),
    ("reboot", "Neustarten", "system-reboot",
     ["cinnamon-session-quit", "--reboot", "--no-prompt"]),
    ("logout", "Abmelden", "system-log-out",
     ["cinnamon-session-quit", "--logout", "--no-prompt"]),
    ("switch-user", "Benutzer wechseln", "system-users",
     ["dm-tool", "switch-to-greeter"]),
    ("suspend", "Bereitschaft", "system-suspend",
     ["systemctl", "suspend"]),
]

CSS = b"""
window.session-menu { background-color: rgba(40, 40, 40, 0.96); }
.session-menu label.title { color: #e0e0e0; font-size: 13pt; font-weight: bold; }
.session-menu button {
    background: rgba(70, 70, 70, 0.9); color: #e8e8e8;
    border: 1px solid rgba(150, 150, 150, 0.5); border-radius: 10px;
    padding: 10px 6px; min-width: 120px;
}
.session-menu button:hover, .session-menu button:focus {
    background: rgba(110, 110, 110, 0.95); border-color: rgba(200, 200, 200, 0.8);
}
.session-menu button.cancel { min-width: 0; padding: 6px 24px; }
"""


def run(action_id):
    for aid, label, _icon, cmd in ACTIONS:
        if aid == action_id:
            try:
                subprocess.Popen(cmd, start_new_session=True)
                return True
            except OSError as e:
                error(f"{label} fehlgeschlagen:\n{e}")
                return False
    return False


def error(msg):
    try:
        subprocess.run(["zenity", "--error", "--title=Sitzung", f"--text={msg}"])
    except OSError:
        print(msg, file=sys.stderr)


DOCK_GAP_CM = 2.0
EDGE_GAP = 12


def cm_to_px(monitor, cm):
    """Zentimeter in Pixel; unplausible Monitorangaben (z. B. xrdp) -> 96 dpi."""
    dpi = 96.0
    mm = monitor.get_width_mm()
    if mm > 0:
        measured = monitor.get_geometry().width / (mm / 25.4)
        if 70 <= measured <= 400:
            dpi = measured
    return round(cm / 2.54 * dpi)


def place_above_dock(win, Gdk):
    """Fenster waagerecht mittig, etwa 2 cm über dem Dock."""
    display = Gdk.Display.get_default()
    _screen, px, py = display.get_default_seat().get_pointer().get_position()
    monitor = display.get_monitor_at_point(px, py)
    geo = monitor.get_geometry()
    work = monitor.get_workarea()
    w, h = win.get_size()

    # Plank reserviert seinen Platz (Strut) nur, wenn es nicht ausgeblendet wird.
    bottom = work.y + work.height
    if bottom >= geo.y + geo.height:
        bottom = min(bottom, py - 40)
    y = max(work.y + EDGE_GAP, bottom - h - cm_to_px(monitor, DOCK_GAP_CM))
    x = work.x + (work.width - w) // 2
    win.move(x, y)


def show_dialog():
    import gi
    gi.require_version("Gtk", "3.0")
    gi.require_version("Gdk", "3.0")
    from gi.repository import Gdk, Gtk

    provider = Gtk.CssProvider()
    provider.load_from_data(CSS)
    Gtk.StyleContext.add_provider_for_screen(
        Gdk.Screen.get_default(), provider,
        Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    win = Gtk.Window(title="Sitzung")
    win.get_style_context().add_class("session-menu")
    win.set_position(Gtk.WindowPosition.NONE)
    win.set_resizable(False)
    win.set_keep_above(True)
    win.set_skip_taskbar_hint(True)
    win.set_type_hint(Gdk.WindowTypeHint.DIALOG)
    win.connect("destroy", Gtk.main_quit)
    win.connect("key-press-event",
                lambda w, e: w.destroy() if e.keyval == Gdk.KEY_Escape else None)

    box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16)
    box.set_border_width(20)
    win.add(box)

    title = Gtk.Label(label="Was möchten Sie tun?")
    title.get_style_context().add_class("title")
    box.pack_start(title, False, False, 0)

    row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
    box.pack_start(row, False, False, 0)

    def on_click(_btn, aid):
        win.hide()
        run(aid)
        Gtk.main_quit()

    for aid, label, icon, _cmd in ACTIONS:
        btn = Gtk.Button()
        inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        inner.pack_start(Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.DIALOG),
                         False, False, 0)
        inner.pack_start(Gtk.Label(label=label), False, False, 0)
        btn.add(inner)
        btn.connect("clicked", on_click, aid)
        row.pack_start(btn, True, True, 0)

    cancel = Gtk.Button(label="Abbrechen")
    cancel.get_style_context().add_class("cancel")
    cancel.connect("clicked", lambda *_: win.destroy())
    bottom = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
    bottom.pack_end(cancel, False, False, 0)
    box.pack_start(bottom, False, False, 0)

    win.show_all()
    place_above_dock(win, Gdk)
    cancel.grab_focus()
    Gtk.main()


if __name__ == "__main__":
    if len(sys.argv) > 1:
        sys.exit(0 if run(sys.argv[1]) else 1)
    show_dialog()
