// GNOME's system menu button: a small run of status glyphs — network, volume,
// bluetooth, caffeine — that opens the quick settings panel when clicked.
//
// This widget only *reports* state. The quick settings panel itself lives
// elsewhere; clicking here just raises `requestQuickSettings`.

import Quickshell
import Quickshell.Bluetooth
import Quickshell.Networking
import Quickshell.Services.Pipewire
import QtQuick
import qs.config
import qs.services
import qs.widgets

Pill {
    id: root

    signal requestQuickSettings

    // Set by the shell when an idle inhibitor is held. Lives outside this
    // widget because the inhibitor itself is owned by the quick settings side.
    property bool idleInhibited: false

    onActivated: root.requestQuickSettings()

    // Right-click opens the settings that govern this widget. Network and Bluetooth, which is most of what these glyphs report.
    onSecondaryActivated: ShellState.openSettings("connectivity")

    // ── Audio ───────────────────────────────────────────────────────────────
    // Without a tracker, volume and muted silently read as zero/false.
    PwObjectTracker {
        objects: [Pipewire.defaultAudioSink]
    }

    // defaultAudioSink is transiently null while pipewire settles.
    readonly property real volume: Pipewire.defaultAudioSink?.audio?.volume ?? 0
    readonly property bool muted: Pipewire.defaultAudioSink?.audio?.muted ?? false

    // ── Network ─────────────────────────────────────────────────────────────
    readonly property var devices: Networking.devices ? Networking.devices.values : []

    readonly property var wiredDevice: root.devices.find(d => d.type === DeviceType.Wired && d.connected) ?? null
    readonly property var wifiDevice: root.devices.find(d => d.type === DeviceType.Wifi) ?? null

    readonly property var wifiNetwork: {
        const networks = root.wifiDevice?.networks ? root.wifiDevice.networks.values : [];
        return networks.find(n => n.connected) ?? null;
    }

    // ── Bluetooth ───────────────────────────────────────────────────────────
    readonly property bool bluetoothOn: Bluetooth.defaultAdapter?.enabled ?? false
    readonly property bool bluetoothConnected: {
        const devices = Bluetooth.devices ? Bluetooth.devices.values : [];
        return devices.some(d => d.connected);
    }

    StatusGlyph {
        glyph: {
            if (root.wiredDevice)
                return "\u{F0200}";              // md-ethernet
            if (!root.wifiNetwork)
                return "\u{F092D}";              // md-wifi_strength_off

            // NetworkManager reports strength 0-100; some backends normalize to
            // 0-1, so accept either rather than showing one bar forever.
            const raw = root.wifiNetwork.signalStrength ?? 0;
            const strength = raw <= 1 ? raw * 100 : raw;
            if (strength >= 75)
                return "\u{F0928}";              // md-wifi_strength_4
            if (strength >= 50)
                return "\u{F0925}";              // md-wifi_strength_3
            if (strength >= 25)
                return "\u{F0922}";              // md-wifi_strength_2
            return "\u{F091F}";                  // md-wifi_strength_1
        }

        color: {
            if (root.wiredDevice)
                return Theme.barFg;
            if (!root.wifiNetwork)
                return Theme.barFgMuted;
            return Networking.connectivity === NetworkConnectivity.Full ? Theme.barFg : Theme.warn;
        }
    }

    // The active layout, only where there is a choice to indicate: a machine
    // with one layout knows what its keys say. GNOME shows the same short
    // code in the same corner, which is the muscle memory this preserves.
    Text {
        visible: KeyboardLayout.multiple
        anchors.verticalCenter: parent.verticalCenter
        text: KeyboardLayout.shortLabel
        color: Theme.barFg
        font.family: Theme.fontFamily
        font.pixelSize: Theme.fontSizeSmall
        font.weight: Font.Medium
        font.letterSpacing: 0.5
    }

    // A tunnel that is up changes what every connection means, so it earns a
    // permanent glyph while active -- and its absence is the resting state,
    // same shape as Bluetooth below.
    StatusGlyph {
        visible: Vpn.anyActive
        glyph: "\u{F0582}"                       // md-vpn
        color: Theme.accent
    }

    StatusGlyph {
        glyph: {
            if (root.muted || root.volume <= 0)
                return "\u{F075F}";              // md-volume_mute
            if (root.volume < 0.34)
                return "\u{F057F}";              // md-volume_low
            if (root.volume < 0.67)
                return "\u{F0580}";              // md-volume_medium
            return "\u{F057E}";                  // md-volume_high
        }
        color: root.muted ? Theme.barFgMuted : Theme.barFg
    }

    StatusGlyph {
        // Bluetooth off is the resting state on this machine, so the glyph is
        // simply absent rather than shown crossed out.
        visible: root.bluetoothOn
        glyph: root.bluetoothConnected ? "\u{F00B1}" : "\u{F00AF}"   // md-bluetooth_connect / md-bluetooth
        color: root.bluetoothConnected ? Theme.accent : Theme.barFg
    }

    StatusGlyph {
        visible: root.idleInhibited
        glyph: "\u{F0176}"                       // md-coffee
        color: Theme.warn
    }

    StatusGlyph {
        visible: KdeConnect.phoneReachable
        glyph: "\u{F03F2}"                       // md-cellphone-link
        color: Theme.cyan
    }

    // Battery. Absent entirely on a desktop: `available` is false until a
    // battery has actually been read, so this is not a zero that looks like a
    // flat cell. Same shape as the graphics field in VitalsWidget, which gates
    // on both the preference and the hardware.
    //
    // One color for the icon and the number beside it: two different colors
    // would read as two indicators.
    readonly property color batteryColor: {
        if (Battery.critical) return Theme.danger;
        if (Battery.low) return Theme.warn;
        if (Battery.charging) return Theme.ok;
        return Theme.barFg;
    }

    StatusGlyph {
        visible: Settings.showBattery && Battery.available
        glyph: {
            if (Battery.charging)
                return "\u{F0084}";              // md-battery_charging
            const level = Math.round(Battery.percent / 10) * 10;
            if (level >= 100) return "\u{F0079}";  // md-battery
            if (level <= 0)   return "\u{F008E}";  // md-battery_outline
            // md-battery_10 .. md-battery_90 are consecutive from F007A.
            return String.fromCodePoint(0xF007A + (level / 10) - 1);
        }
        color: root.batteryColor
    }

    // The exact number, for the people who ask the icon to be more specific --
    // GNOME's "Show Battery Percentage", living under the same gates as the
    // icon it annotates.
    Text {
        visible: Settings.showBattery && Settings.showBatteryPercent && Battery.available
        anchors.verticalCenter: parent.verticalCenter
        text: Math.round(Battery.percent) + "%"
        color: root.batteryColor
        font.family: Theme.fontFamily
        font.pixelSize: Theme.fontSizeSmall

        Behavior on color {
            ColorAnimation {
                duration: Theme.durFast
            }
        }
    }
}
