pragma Singleton

// Keeps everything that draws a window agreeing about light or dark.
//
// The shell repaints itself from Theme.qml the moment the preference changes,
// because every token there is a binding. Two other things draw on this desktop
// and do not read Panama's store:
//
//   GTK applications          read gsettings colour-scheme and gtk-theme
//   the compositor            draws window borders and shadows
//
// A toolbar or a window border still wearing the other scheme is more jarring
// than either scheme on its own, which is why this exists rather than the
// setting simply being a Theme property.

import Quickshell
import Quickshell.Io
import QtQuick
import qs.config

Singleton {
    id: root

    readonly property string appThemePath: Quickshell.shellDir + "/scripts/panama-theme-apps"

    readonly property bool dark: DesktopPreferences.get("colorScheme") !== "light"
    property string lastError: ""

    // Applied one command at a time: Process runs a single command, and several
    // of these are separate programs.
    property var pending: []

    Process {
        id: runner
        onExited: (exitCode, exitStatus) => {
            if (exitCode !== 0)
                root.lastError = "The colour scheme could not be applied everywhere.";
            root.drain();
        }
    }

    function drain(): void {
        if (runner.running || root.pending.length === 0)
            return;
        const next = root.pending[0];
        root.pending = root.pending.slice(1);
        runner.exec(next);
    }

    function enqueue(commands: var): void {
        root.pending = root.pending.concat(commands);
        root.drain();
    }

    // Pushed once at startup as well as on change: gsettings and the compositor
    // do not read Panama's store, so a scheme chosen in a previous session is
    // only in effect for the shell until this runs.
    Component.onCompleted: settle.restart()

    Timer {
        id: settle
        interval: 1200
        onTriggered: root.apply()
    }

    Connections {
        target: DesktopPreferences
        function onRevisionChanged(): void { coalesce.restart(); }
    }

    Timer {
        id: coalesce
        interval: 250
        onTriggered: root.apply()
    }

    function apply(): void {
        root.lastError = "";

        const scheme = root.dark ? "prefer-dark" : "prefer-light";
        // Adwaita's light and dark are the same theme; only the preference and
        // the -dark suffix differ, so applications that honour either agree.
        const gtkTheme = root.dark ? "Adwaita-dark" : "Adwaita";

        const commands = [
            ["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", scheme],
            ["gsettings", "set", "org.gnome.desktop.interface", "gtk-theme", gtkTheme]
        ];

        // Unfocused window borders. The focused border is the prism gradient and
        // is already scheme-independent; the inactive one is a flat neutral that
        // would be invisible against the opposite background.
        const inactive = root.dark ? "rgba(3b426199)" : "rgba(a8aecb99)";
        commands.push(["hyprctl", "eval",
            `hl.config({ general = { col = { inactive_border = "${inactive}" } } })`]);

        // Applications that predate org.freedesktop.appearance and carry their
        // own palettes -- terminals, chiefly. Everything that reads the portal
        // (GTK4, Qt6, Chromium, Electron) is already handled by the gsettings
        // write above and needs nothing here.
        commands.push([root.appThemePath, root.dark ? "dark" : "light"]);

        root.enqueue(commands);
    }
}
