#!/usr/bin/env python3

"""Put this machine's displays the way device.conf asks.

The decision is in classroom_display; this applies it through Plasma's
own tool, which is the same way classroom-scale applies UI_SCALE.

DISPLAY_MODE:

    all             every connected display. Every existing classroom.
    external-only   the external display, with the built-in panel off.

Run at login from an XDG autostart entry, before classroom-scale, so that
the scale is applied to the displays this leaves enabled rather than to a
panel that is about to go off.

It never leaves the machine with no screen: a laptop asked for
external-only whose television is off keeps its own panel and says so.
That is a deliberate refusal to obey — a machine nobody can see is one
nobody can fix standing in front of it — and it corrects itself the next
time this runs with the television on.
"""

import json
import os
import pwd
import subprocess
import sys

sys.path.insert(0, "/usr/local/lib/ekklesia-classroom")
sys.path.insert(
    0,
    os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lib"),
)

import classroom_display as policy  # noqa: E402
from classroom_config import display_mode, read_device_config  # noqa: E402


def say(message):
    print(f"classroom-display-mode: {message}", flush=True)


def kscreen(*args):
    try:
        return subprocess.run(
            ["kscreen-doctor"] + list(args),
            capture_output=True, text=True, timeout=30, check=False,
        )
    except (OSError, subprocess.SubprocessError) as error:
        say(str(error))
        return None


def outputs():
    result = kscreen("-j")

    if result is None or result.returncode != 0:
        say("could not read the displays")
        return []

    try:
        return json.loads(result.stdout or "{}").get("outputs") or []
    except ValueError as error:
        say(f"unreadable display list: {error}")
        return []


def main():
    if pwd.getpwuid(os.getuid()).pw_name != "classroom":
        return 0

    mode = display_mode(read_device_config())
    found = outputs()

    if not found:
        say("no displays")
        return 1

    described = ", ".join(
        f"{item.get('name')}"
        f"{' (built-in)' if policy.is_internal(item) else ''}"
        f"{'' if item.get('enabled', True) else ' [off]'}"
        for item in policy.connected(found)
    )
    say(f"DISPLAY_MODE={mode}; connected: {described or 'none'}")

    enable, disable = policy.plan(found, mode)

    if mode == policy.EXTERNAL_ONLY and not disable and not any(
            policy.is_internal(item) for item in policy.connected(found)):
        say("external-only, and there is no built-in panel to switch off")
    elif mode == policy.EXTERNAL_ONLY and all(
            policy.is_internal(item) for item in policy.connected(found)):
        say("external-only asked for, but the only display is the built-in "
            "panel — leaving it on rather than blinding the machine")

    enable, disable = policy.changes(found, mode)

    if not enable and not disable:
        say("the displays already match")
        return 0

    failed = False

    # Enable first. Switching the panel off before the television is on
    # would leave a moment with no output at all.
    for name in enable:
        result = kscreen(f"output.{name}.enable")

        if result is None or result.returncode != 0:
            failed = True
            say(f"{name}: could not enable")
        else:
            say(f"{name} on")

    for name in disable:
        result = kscreen(f"output.{name}.disable")

        if result is None or result.returncode != 0:
            failed = True
            say(f"{name}: could not disable")
        else:
            say(f"{name} off")

    return 1 if failed else 0


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