// One dock icon: the app, its running-window dots, hover scale and click
// behaviour. Reproduces Dash-to-Dock's item: 48px icon, up to four dots
// underneath, grow-on-hover, click to focus / cycle / launch.

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

Item {
    id: root

    // { entry: DesktopEntry|null, windows: [HyprlandToplevel], appId: string }
    // Built by DockBody so this file stays presentational.
    required property var app

    readonly property DesktopEntry entry: app && app.entry ? app.entry : null
    readonly property var windows: app && app.windows ? app.windows : []
    readonly property string label: entry ? entry.name : (app && app.appId ? app.appId : "")
    readonly property bool running: windows.length > 0
    readonly property bool hovered: mouse.containsMouse

    // Emitted so DockBody can drive the single shared tooltip.
    signal entered
    signal exited

    // The icon may grow past the cell on hover; the cell itself stays a fixed
    // size so the row doesn't reflow.
    implicitWidth: Theme.dockIconSize
    implicitHeight: Theme.dockIconSize + dots.height + 4

    // Desktop entries usually carry a freedesktop icon *name*, but some ship an
    // absolute path. iconPath() only understands names, so branch on it. The
    // `true` argument makes a missing icon return "" instead of a placeholder
    // that renders as a black square in some themes.
    readonly property string iconSource: {
        const name = entry && entry.icon ? entry.icon : (app && app.appId ? app.appId : "");
        if (!name)
            return "";
        if (name.startsWith("/"))
            return "file://" + name;
        return Quickshell.iconPath(name, true);
    }

    Item {
        id: iconSlot
        width: Theme.dockIconSize
        height: Theme.dockIconSize
        anchors.horizontalCenter: parent.horizontalCenter
        y: 0

        // Grow upward out of the dock, the way GNOME's dash does, so the icon
        // never overlaps its own running dots.
        transformOrigin: Item.Bottom
        scale: root.hovered ? 1.15 : 1.0

        Behavior on scale {
            NumberAnimation {
                duration: Theme.durFast
                easing.type: Easing.OutBack
                easing.overshoot: 1.6
            }
        }

        IconImage {
            anchors.fill: parent
            visible: root.iconSource !== ""
            source: root.iconSource
            asynchronous: true
            mipmap: true
        }

        // Last resort for an app with no resolvable icon: an initial, which is
        // still recognisable, unlike the theme's broken-icon placeholder.
        Rectangle {
            anchors.fill: parent
            visible: root.iconSource === ""
            radius: Theme.cardRadius
            border.width: 0
            color: Theme.alpha(Theme.fg, Theme.activeAlpha)

            Text {
                anchors.centerIn: parent
                text: root.label ? root.label.charAt(0).toUpperCase() : "?"
                color: Theme.fg
                font.family: Theme.fontFamily
                font.pixelSize: Math.round(Theme.dockIconSize * 0.5)
            }
        }
    }

    // Running indicators. Dash-to-Dock showed one dot per window up to four.
    Row {
        id: dots
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.bottom: parent.bottom
        spacing: 3
        height: 4

        Repeater {
            model: Math.min(root.windows.length, 4)

            Rectangle {
                width: 4
                height: 4
                radius: 2
                border.width: 0 // QTBUG-137166: transparent rounded rects punch corners
                color: Theme.accent
            }
        }
    }

    MouseArea {
        id: mouse
        anchors.fill: parent
        hoverEnabled: true
        acceptedButtons: Qt.LeftButton | Qt.MiddleButton

        onEntered: root.entered()
        onExited: root.exited()

        onClicked: mev => {
            // Middle click always starts a new instance, as in GNOME.
            if (mev.button === Qt.MiddleButton) {
                root.launch();
                return;
            }
            if (root.running)
                root.focusNext();
            else
                root.launch();
        }
    }

    function launch(): void {
        if (root.entry)
            root.entry.execute();
    }

    // Clicking a running app cycles through its windows, matching GNOME's dash.
    function focusNext(): void {
        const wins = root.windows;
        if (wins.length === 0)
            return;

        let next = wins[0];
        for (let i = 0; i < wins.length; i++) {
            if (wins[i].activated) {
                next = wins[(i + 1) % wins.length];
                break;
            }
        }
        if (!next)
            return;

        // Quickshell reports the address without the 0x prefix Hyprland's
        // window selector expects; normalise so either form works.
        if (next.address) {
            const addr = next.address.startsWith("0x") ? next.address : "0x" + next.address;
            Hyprland.dispatch(`hl.dsp.focus({ window = "address:${addr}" })`);
        } else if (next.wayland) {
            // No address yet (the toplevel is still being reported) — the
            // wlr handle can still raise it.
            next.wayland.activate();
        }
    }
}
