// Bluetooth device picker. Paired devices first (that's what you actually
// reconnect to), then whatever discovery has turned up.

import QtQuick
import Quickshell
import Quickshell.Widgets
import Quickshell.Bluetooth
import qs.config

Item {
    id: root

    // True while the section is open. Discovery is expensive, so it only runs
    // while the list is on screen — same as the GNOME panel.
    property bool active: false
    property alias maxHeight: list.maxHeight

    readonly property var adapter: Bluetooth.defaultAdapter
    property bool discoveryOwned: false

    implicitHeight: list.implicitHeight

    readonly property var devices: {
        const list = Bluetooth.devices.values.slice();
        list.sort((a, b) => {
            if (a.connected !== b.connected)
                return a.connected ? -1 : 1;
            if (a.paired !== b.paired)
                return a.paired ? -1 : 1;
            return (a.name || "").localeCompare(b.name || "");
        });
        return list;
    }

    function syncDiscovery(): void {
        if (!root.adapter)
            return;
        const shouldDiscover = root.active && root.adapter.enabled;
        if (shouldDiscover && !root.adapter.discovering) {
            root.adapter.discovering = true;
            root.discoveryOwned = true;
        } else if (!shouldDiscover && root.discoveryOwned && root.adapter.discovering) {
            root.adapter.discovering = false;
            root.discoveryOwned = false;
        }
    }

    onActiveChanged: root.syncDiscovery()
    onAdapterChanged: {
        if (root.active)
            root.syncDiscovery();
    }

    Component.onDestruction: {
        if (root.adapter && root.discoveryOwned && root.adapter.discovering)
            root.adapter.discovering = false;
    }

    function stateText(device): string {
        if (device.pairing)
            return "Pairing…";
        switch (device.state) {
        case BluetoothDeviceState.Connecting:
            return "Connecting…";
        case BluetoothDeviceState.Disconnecting:
            return "Disconnecting…";
        case BluetoothDeviceState.Connected:
            return device.batteryAvailable ? "Connected · " + Math.round(device.battery * 100) + "%" : "Connected";
        default:
            return device.paired ? "Paired" : "";
        }
    }

    // Click cycles the obvious next step: pair, connect, disconnect.
    function activate(device): void {
        if (device.connected)
            device.disconnect();
        else if (device.paired)
            device.connect();
        else
            device.pair();
    }

    ScrollColumn {
        id: list
        anchors.fill: parent
        maxHeight: 300

        Text {
            width: parent.width
            visible: root.devices.length === 0
            topPadding: 12
            bottomPadding: 12
            horizontalAlignment: Text.AlignHCenter
            text: {
                if (!root.adapter)
                    return "No Bluetooth adapter";
                if (!root.adapter.enabled)
                    return "Bluetooth is off";
                return "Searching…";
            }
            color: Theme.fgMuted
            font.family: Theme.fontFamily
            font.pixelSize: Theme.fontSize
        }

        Repeater {
            model: root.devices

            RowButton {
                id: deviceRow

                required property var modelData

                width: parent.width
                // BlueZ reports a plain freedesktop name ("audio-headphones");
                // the symbolic variant is the one that can be recolored.
                icon: deviceRow.modelData.icon !== "" ? deviceRow.modelData.icon + "-symbolic" : "bluetooth-symbolic"
                iconFallback: "bluetooth-symbolic"
                label: deviceRow.modelData.name || deviceRow.modelData.address
                sublabel: root.stateText(deviceRow.modelData)
                selected: deviceRow.modelData.connected
                onClicked: root.activate(deviceRow.modelData)

                // Forgetting is destructive enough that it gets its own
                // control rather than sharing the row click.
                IconButton {
                    anchors.verticalCenter: parent.verticalCenter
                    visible: deviceRow.modelData.paired
                    size: 26
                    iconSize: 13
                    icon: "user-trash-symbolic"
                    iconFallback: "window-close-symbolic"
                    onClicked: deviceRow.modelData.forget()
                }
            }
        }
    }
}
