// Workspace indicator, in the shape GNOME's workspace switcher had: a row of
// dots, the current one drawn as a wide accent pill. Hyprland workspaces are
// dynamic here, so the row grows and shrinks as workspaces come and go.
//
// Scrolling anywhere over the row switches workspace — a habit carried over
// from the GNOME top panel and explicitly worth keeping.

import Quickshell
import Quickshell.Hyprland
import QtQuick
import qs.config

Item {
    id: root

    // The QsScreen this bar instance lives on. When set, only that monitor's
    // workspaces are shown — otherwise a second monitor would duplicate them.
    property var screen: null

    readonly property HyprlandMonitor monitor: root.screen ? Hyprland.monitorFor(root.screen) : null

    // Special workspaces (negative ids) are scratchpads, not places you switch
    // to by index — GNOME had no equivalent, so they stay out of the row.
    readonly property var workspaces: {
        const all = Hyprland.workspaces ? Hyprland.workspaces.values : [];
        return all.filter(ws => ws && ws.id > 0 && (!root.monitor || !ws.monitor || ws.monitor === root.monitor)).sort((a, b) => a.id - b.id);
    }

    // The delegate currently focused. The accent pill tracks its geometry, which
    // is what makes the pill appear to slide from one workspace to the next.
    property Item focusedItem: null

    readonly property int dotSize: 8
    readonly property int slotWidth: 16
    readonly property int focusedSlotWidth: 28

    implicitWidth: row.implicitWidth + 12
    // Full bar height makes (0, 0) part of the wheel target. The dots stay
    // optically centered by the row and active-pill geometry below.
    implicitHeight: Theme.barHeight

    // Scroll over the padding either side of the row, not just over a dot.
    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.NoButton
        onWheel: event => root.cycle(event.angleDelta.y)
    }

    Rectangle {
        id: activePill

        // Behind the dots: the focused delegate hides its own dot, so nothing
        // is ever drawn on top of the pill.
        z: -1

        visible: root.focusedItem !== null
        height: root.dotSize
        radius: Theme.pillRadius
        border.width: 0 // QTBUG-137166: rounded rects can lose their corners

        // The prism, as a fill rather than a hairline. This is the one element
        // in the bar that is always colored, so it is where the pair belongs.
        gradient: Gradient {
            orientation: Gradient.Horizontal
            GradientStop {
                position: 0.0
                color: Theme.accent
            }
            GradientStop {
                position: 1.0
                color: Theme.accentSecondary
            }
        }

        y: (root.height - height) / 2
        x: row.x + (root.focusedItem ? root.focusedItem.x : 0)
        width: root.focusedItem ? root.focusedItem.width : 0

        Behavior on x {
            NumberAnimation {
                duration: Theme.durNormal
                easing.type: Easing.OutCubic
            }
        }
        Behavior on width {
            NumberAnimation {
                duration: Theme.durNormal
                easing.type: Easing.OutCubic
            }
        }
    }

    Row {
        id: row
        anchors.centerIn: parent
        spacing: 4

        Repeater {
            model: root.workspaces

            delegate: Item {
                id: slot

                required property var modelData

                readonly property bool isFocused: slot.modelData.focused
                readonly property bool isOccupied: slot.modelData.toplevels ? slot.modelData.toplevels.values.length > 0 : false

                implicitWidth: slot.isFocused ? root.focusedSlotWidth : root.slotWidth
                implicitHeight: root.height

                Behavior on implicitWidth {
                    NumberAnimation {
                        duration: Theme.durNormal
                        easing.type: Easing.OutCubic
                    }
                }

                onIsFocusedChanged: if (slot.isFocused)
                    root.focusedItem = slot
                Component.onCompleted: if (slot.isFocused)
                    root.focusedItem = slot

                Rectangle {
                    anchors.centerIn: parent
                    width: root.dotSize
                    height: root.dotSize
                    radius: width / 2
                    border.width: 0
                    visible: !slot.isFocused

                    color: {
                        if (slot.modelData.urgent)
                            return Theme.urgent;
                        return slot.isOccupied ? Theme.barFg : Theme.alpha(Theme.barFg, 0.3);
                    }

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

                MouseArea {
                    anchors.fill: parent
                    cursorShape: Qt.PointingHandCursor
                    onClicked: slot.modelData.activate()
                    onWheel: event => root.cycle(event.angleDelta.y)
                }
            }
        }
    }

    // Scroll up goes to the previous workspace, down to the next — the same
    // direction GNOME used. Relative focus rather than by index so it keeps
    // working with dynamic workspaces.
    function cycle(angleDeltaY: int): void {
        Hyprland.dispatch(angleDeltaY > 0 ? 'hl.dsp.focus({ workspace = "-1" })' : 'hl.dsp.focus({ workspace = "+1" })');
    }
}
