// Desktop & Dock.
//
// The dock timings used to be shown here as text -- "Instant", "250 ms" -- even
// though they were already stored, mutable integers. They are controls now.
// The window-layout card keeps text rows because those really are facts about
// how Panama tiles rather than settings: the adjustable parts of window
// appearance live on the Appearance page, next to the preview that explains
// them.

import QtQuick
import Quickshell
import qs.config
import qs.services

SettingsPage {
    id: root

    // Which project is one click from being forgotten. Same in-place confirm the
    // SSH Keys and Snapshots pages use.
    property string confirmingProject: ""

    // Turning the last screen off would leave no dock anywhere and no obvious
    // way back, so the final one cannot be removed -- it collapses to "every
    // screen" instead, which is the same thing on one display and recoverable
    // on several.
    function toggleDockScreen(name: string): void {
        const all = Quickshell.screens.map(screen => String(screen.name));
        const current = Settings.dockScreens.length === 0
            ? all.slice()
            : Settings.dockScreens.map(String);
        const at = current.indexOf(name);
        let next = current.slice();
        if (at >= 0)
            next.splice(at, 1);
        else
            next.push(name);
        if (next.length === 0 || next.length === all.length)
            next = [];
        DesktopPreferences.set("dockScreens", next);
    }

    title: "Desktop & Dock"
    lede: "Keep the shell instant, spatial, and out of your way."

    SettingsCard {
        title: "Dock"

        ChoiceRow { setting: "dockPosition" }

        // One row per connected screen. Nothing selected means every screen,
        // which is stated rather than left as an empty list somebody has to
        // interpret -- and it is what a single-monitor machine should do
        // without being configured at all.
        SettingRow {
            label: "Screens"
            detail: Settings.dockScreens.length === 0
                ? "On every display"
                : "On " + Settings.dockScreens.length + " of "
                  + Quickshell.screens.length + " displays"
            visible: Quickshell.screens.length > 1
            controlWidth: 260

            Row {
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                spacing: 7

                Repeater {
                    model: Quickshell.screens

                    delegate: Rectangle {
                        id: screenPill

                        required property var modelData

                        readonly property string screenName: String(screenPill.modelData.name ?? "")
                        // An empty list means all, so every pill reads as on.
                        readonly property bool on: Settings.dockScreens.length === 0
                            || Settings.dockScreens.indexOf(screenPill.screenName) >= 0

                        width: pillLabel.implicitWidth + 20
                        height: 28
                        radius: 8
                        color: screenPill.on ? Theme.alpha(Theme.accent, 0.22)
                                             : Theme.alpha(Theme.fg, 0.06)
                        border.width: screenPill.on ? 1 : 0
                        border.color: Theme.alpha(Theme.accent, 0.5)

                        Text {
                            id: pillLabel
                            anchors.centerIn: parent
                            text: screenPill.screenName
                            color: screenPill.on ? Theme.fg : Theme.fgDim
                            font.family: Theme.fontFamily
                            font.pixelSize: Theme.fontSizeSmall
                        }

                        HoverHandler { cursorShape: Qt.PointingHandCursor }
                        TapHandler { onTapped: root.toggleDockScreen(screenPill.screenName) }
                    }
                }
            }
        }

        ToggleRow { setting: "dockAutohide" }
        SliderRow { setting: "dockRevealDelayMs"; zeroLabel: "Instant" }
        SliderRow { setting: "dockHideDelayMs"; zeroLabel: "Instant" }
        SliderRow { setting: "dockIconSize"; divider: false }
    }

    SettingsCard {
        title: "Pinned applications"
        subtitle: "What sits in the Dock whether or not it is running. Order here is the order on screen."

        DockPinsEditor {
            id: pins
            width: parent.width
        }
    }

    SettingsCard {
        title: "Pin another application"

        DockAppPicker {
            width: parent.width
            pinned: pins.pinned
            onPicked: id => pins.add(id)
        }
    }

    SettingsCard {
        title: "Window layout"
        subtitle: "Follows the Forge mental model, with native Hyprland tiling."

        // These were two read-only rows reporting "Tiling" and "Dynamic", which
        // described settings rather than facts -- both are ordinary Hyprland
        // options that simply had no controls. TextRow's own documentation says
        // a setting the user could reasonably change does not belong in it.
        ChoiceRow { setting: "windowLayout" }
        ToggleRow { setting: "preserveSplit" }
        ChoiceRow { setting: "forceSplit" }
        ToggleRow { setting: "windowSnapping" }
        ActionRow {
            label: "Gaps, corners, and effects"
            detail: "Adjusted on the Appearance page, beside a live preview"
            action: "Open Appearance"
            divider: false
            onTriggered: ShellState.openSettingsSection("appearance", "windows")
        }
    }

    // GNOME's Multitasking panel, in Hyprland's terms.
    // Only meaningful when the layout above is Master and stack. Hidden
    // otherwise, because a card of settings that do nothing under the layout
    // you are actually running is worse than not offering the layout at all.
    SettingsCard {
        visible: DesktopPreferences.get("windowLayout") === "master"
        title: "Master and stack"
        subtitle: "How the master area behaves. These apply only while the tiling layout above is Master and stack."

        SliderRow { setting: "masterFactor" }
        ChoiceRow { setting: "masterOrientation" }
        ChoiceRow { setting: "masterNewStatus" }
        ToggleRow { setting: "masterNewOnTop"; divider: false }
    }

    SettingsCard {
        title: "Window edges"
        subtitle: "How the pointer grabs a window's border, and how floating windows behave near each other and the screen edge."

        ToggleRow { setting: "resizeOnBorder" }
        SliderRow { setting: "borderGrabArea"; zeroLabel: "Border only" }
        ToggleRow { setting: "hoverIconOnBorder" }
        SliderRow { setting: "snapWindowGap"; zeroLabel: "Touching" }
        SliderRow { setting: "snapMonitorGap"; zeroLabel: "Touching" }
        ToggleRow { setting: "snapRespectGaps"; divider: false }
    }

    // Hyprland's own interruptions. Panama turns all four off, which is a
    // defensible default and was not previously a decision anyone could
    // reverse without editing looks.lua.
    SettingsCard {
        title: "Hyprland notices"
        subtitle: "Panama hides all of these by default. They are the compositor's own, not Panama's."

        ToggleRow { setting: "hyprlandLogo" }
        ToggleRow { setting: "hyprlandSplash" }
        ToggleRow { setting: "hyprlandUpdateNews" }
        ToggleRow { setting: "hyprlandDonationNag"; divider: false }
    }

    // Saved here, not created here. A layout is worth recording at the moment
    // you have it right, so saving is a launcher command; this is where the
    // ones you kept are reviewed and the ones you did not are removed.
    SettingsCard {
        title: "Projects"
        subtitle: Projects.projects.length > 0
            ? "Saved window layouts. Opening one claims free workspaces, so it never lands on top of what you are doing."
            : "No saved layouts yet. Arrange your windows, then run \"Save Layout as Project\" from the launcher."

        Repeater {
            model: Projects.projects

            delegate: SettingRow {
                id: projectRow

                required property var modelData
                required property int index

                readonly property string projectName: String(projectRow.modelData.name ?? "")
                readonly property bool confirming: root.confirmingProject === projectRow.projectName

                label: projectRow.projectName
                detail: projectRow.confirming
                    ? "Forgetting this only removes the layout. Nothing that is open closes."
                    : projectRow.modelData.windows + " windows across "
                      + projectRow.modelData.workspaces + " workspaces — "
                      + (projectRow.modelData.applications ?? []).join(", ")
                divider: projectRow.index < Projects.projects.length - 1
                controlWidth: 210

                Row {
                    anchors.right: parent.right
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 8

                    SettingsButton {
                        anchors.verticalCenter: parent.verticalCenter
                        visible: !projectRow.confirming
                        text: "Open"
                        enabled: !Projects.busy
                        onClicked: Projects.open(projectRow.projectName)
                    }

                    SettingsButton {
                        anchors.verticalCenter: parent.verticalCenter
                        visible: projectRow.confirming
                        text: "Forget it"
                        tone: "danger"
                        enabled: !Projects.busy
                        onClicked: {
                            root.confirmingProject = "";
                            Projects.remove(projectRow.projectName);
                        }
                    }

                    SettingsButton {
                        anchors.verticalCenter: parent.verticalCenter
                        text: projectRow.confirming ? "Keep" : "Forget"
                        enabled: !Projects.busy
                        onClicked: root.confirmingProject =
                            projectRow.confirming ? "" : projectRow.projectName
                    }
                }
            }
        }

        TextRow {
            visible: Projects.lastError !== ""
            label: "Problem"
            detail: Projects.lastError
            divider: false
        }
    }

    SettingsCard {
        title: "Workspaces & focus"
        subtitle: "Hyprland's workspaces are created and destroyed as you use them, so there is no fixed count to set."

        ToggleRow { setting: "workspaceBackAndForth" }
        ToggleRow { setting: "allowWorkspaceCycles" }
        ToggleRow { setting: "focusOnActivate" }
        ToggleRow { setting: "mouseMoveFocusesMonitor" }
        ToggleRow { setting: "windowSwallow"; divider: false }
    }

    SettingsCard {
        title: "Focus"

        SliderRow { setting: "focusDurationMinutes"; divider: false }
    }

    // Distinct from the snapshots below, which put THIS machine back as it
    // was. This carries settings to a different one, and deliberately leaves
    // behind anything that describes hardware.
    SettingsCard {
        title: "Carry settings to another machine"
        subtitle: SettingsSync.lastError !== ""
            ? SettingsSync.lastError
            : "Everything except what describes this machine: the display arrangement stays here."

        ActionRow {
            label: "Export"
            detail: SettingsSync.lastAction === "export" && SettingsSync.carried > 0
                ? SettingsSync.carried + " settings written to " + SettingsSync.defaultPath
                : "Writes " + SettingsSync.defaultPath
            action: "Export"
            enabled: !SettingsSync.busy
            onTriggered: SettingsSync.exportTo(SettingsSync.defaultPath)
        }

        ActionRow {
            label: "See what an import would change"
            detail: SettingsSync.previewed
                ? SettingsSync.changes.length + " would change, "
                  + SettingsSync.skipped.length + " skipped"
                : "Reads " + SettingsSync.defaultPath + " without applying anything"
            action: "Preview"
            enabled: !SettingsSync.busy
            onTriggered: SettingsSync.preview(SettingsSync.defaultPath)
        }

        // Only offered once a preview has said what it would do. Importing
        // settings sight unseen is how somebody ends up wondering why their
        // desktop changed.
        ActionRow {
            visible: SettingsSync.previewed && SettingsSync.changes.length > 0
            label: "Apply those " + SettingsSync.changes.length + " changes"
            detail: "Settings the file does not mention are left alone"
            action: "Import"
            enabled: !SettingsSync.busy
            onTriggered: SettingsSync.importFrom(SettingsSync.defaultPath)
        }

        Repeater {
            model: SettingsSync.previewed ? SettingsSync.skipped : []

            delegate: TextRow {
                required property var modelData
                width: parent.width
                label: String(modelData.key ?? "")
                detail: "Skipped: " + String(modelData.reason ?? "")
                value: ""
            }
        }

        TextRow {
            visible: SettingsSync.lastAction === "import" && SettingsSync.lastError === ""
            label: SettingsSync.applied === 0
                ? "Nothing needed changing"
                : SettingsSync.applied + " settings applied"
            detail: "From " + (SettingsSync.exportedFrom || "the export")
            value: ""
            divider: false
        }
    }

    SettingsCard {
        title: "Snapshots"
        subtitle: SettingsBackup.lastError !== ""
            ? SettingsBackup.lastError
            : "Your whole desktop configuration is one file, so a snapshot is a copy of it. Restoring also snapshots what it replaces, so it is itself undoable."

        ActionRow {
            label: "Back up current settings"
            detail: SettingsBackup.snapshots.length === 0
                ? "No snapshots yet"
                : SettingsBackup.snapshots.length + (SettingsBackup.snapshots.length === 1 ? " snapshot kept" : " snapshots kept") + ", newest first"
            action: "Back up now"
            enabled: !SettingsBackup.busy
            divider: SettingsBackup.snapshots.length > 0
            onTriggered: SettingsBackup.save()
        }

        Repeater {
            id: snapshotRows
            model: SettingsBackup.snapshots

            ActionRow {
                required property var modelData
                required property int index

                label: modelData.when
                detail: modelData.keys + " settings"
                action: "Restore"
                enabled: !SettingsBackup.busy
                divider: index < snapshotRows.count - 1
                onTriggered: SettingsBackup.restore(modelData.name)
            }
        }
    }

    SettingsCard {
        title: "Reset"
        subtitle: "Restores the appearance, dock, clock, focus, and display policy, and clears your Home accessory arrangement. Pinned applications, files, and paired devices are not changed."

        ActionRow {
            label: "Restore defaults"
            detail: "Applies immediately, including to the compositor"
            action: "Restore defaults"
            divider: false
            onTriggered: SystemSettings.restoreDefaults()
        }
    }
}
