// The dock — a replacement for Dash-to-Dock configured as: bottom edge, 48px
// icons, intellihide against all windows, dot running-indicators, show-apps
// button at the leading edge.
//
// Intellihide means the dock reserves no space at all (exclusiveZone 0) and
// floats over windows, appearing when the pointer reaches the bottom edge or
// when nothing is in the way.

import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick
import qs.config
import qs.services

PanelWindow {
    id: root

    // Set by Variants when instantiated per-screen. Plain rather than
    // `required` so the dock can also be created standalone while testing;
    // Variants injects into the existing property either way.
    property var modelData: null
    screen: root.modelData

    anchors {
        bottom: true
        left: true
        right: true
    }

    color: "transparent"

    // A dock that reserved space would not be intellihiding.
    exclusiveZone: 0
    exclusionMode: ExclusionMode.Ignore

    // Matched by the `qs-dock` layer rule in hypr/rules.lua — do not rename.
    WlrLayershell.namespace: "qs-dock"
    WlrLayershell.layer: WlrLayer.Top
    WlrLayershell.keyboardFocus: WlrKeyboardFocus.None

    // ── Geometry ────────────────────────────────────────────────────────────
    // Height = room for the tooltip above the bar + the bar + the gap under it.
    readonly property int revealStripHeight: 3
    readonly property int bottomMargin: Theme.barGap
    readonly property int tooltipSpace: 34

    implicitHeight: tooltipSpace + body.implicitHeight + bottomMargin

    // ── Intellihide ─────────────────────────────────────────────────────────
    // Hyprland does not expose live toplevel geometry, so exact overlap cannot
    // be computed. "Is anything on this workspace at all" is the robust proxy,
    // and it is what Dash-to-Dock's all-windows intellihide felt like in
    // practice: an empty workspace keeps the dock out.
    readonly property bool workspaceOccupied: {
        const ws = Hyprland.focusedWorkspace;
        return !!ws && ws.toplevels.values.length > 0;
    }

    readonly property bool wantRevealed: !Settings.dockAutohide || !workspaceOccupied || pointer.hovered

    property bool revealed: true

    onWantRevealedChanged: {
        if (wantRevealed) {
            hideTimer.stop();
            revealTimer.restart();
        } else {
            revealTimer.stop();
            hideTimer.restart();
        }
    }

    Timer {
        id: revealTimer
        interval: Settings.dockRevealDelayMs
        onTriggered: root.revealed = true
    }

    Timer {
        id: hideTimer
        interval: Settings.dockHideDelayMs
        onTriggered: root.revealed = false
    }

    // Other modules (the bar, the capture overlay) read this.
    onRevealedChanged: ShellState.dockRevealed = revealed

    // wantRevealed's first evaluation emits no change signal when it lands on
    // false (the default), so the initial state has to be taken explicitly —
    // otherwise a shell started on a busy workspace would leave the dock up.
    Component.onCompleted: {
        revealed = wantRevealed;
        ShellState.dockRevealed = revealed;
    }

    // ── Input region ────────────────────────────────────────────────────────
    // Revealed: the bar plus everything below it, so crossing the gap under the
    // dock does not count as leaving. Hidden: a sliver along the screen edge,
    // which is the only thing that can still trigger the dock — every other
    // click passes straight through to the window underneath.
    mask: Region {
        item: maskItem
    }

    Item {
        id: surface
        anchors.fill: parent

        // Reports the pointer anywhere inside the input region. A HoverHandler
        // rather than a MouseArea because it keeps reporting while the icons'
        // own MouseAreas are hovered.
        HoverHandler {
            id: pointer
        }

        Item {
            id: maskItem
            x: root.revealed ? body.x : 0
            y: root.revealed ? body.y : surface.height - root.revealStripHeight
            width: root.revealed ? body.width : surface.width
            height: root.revealed ? surface.height - body.y : root.revealStripHeight
        }

        DockBody {
            id: body
            anchors.horizontalCenter: parent.horizontalCenter

            // Slides off the bottom edge when hidden.
            y: root.revealed ? root.tooltipSpace : surface.height
            opacity: root.revealed ? 1 : 0

            // Asymmetric on purpose. Revealing is a response to something the
            // user just did, so it has to feel immediate — any delay there
            // reads as lag. Hiding is not a response to anything, so it can
            // take its time and stay calm in peripheral vision.
            Behavior on y {
                NumberAnimation {
                    duration: root.revealed ? Theme.durDockReveal : Theme.durNormal
                    easing.type: root.revealed ? Easing.OutQuint : Easing.InCubic
                }
            }

            Behavior on opacity {
                NumberAnimation {
                    duration: root.revealed ? Theme.durDockReveal : Theme.durNormal
                    easing.type: Easing.OutCubic
                }
            }
        }
    }
}
