// Displays.
//
// Resolution, refresh rate, scale, and rotation, plus panel brightness and the
// gaming display policy that was already here.
//
// Every geometry change goes through an apply-then-confirm countdown. This is
// the one page where a wrong value can leave the screen unreadable or blank,
// and no other control in the app can undo it once that happens. Confirming is
// what writes the choice to the settings store; letting the countdown run
// leaves nothing behind.

import QtQuick
import qs.config
import qs.services
import qs.widgets

SettingsPage {
    id: root

    title: "Displays"
    lede: SystemSettings.monitorDescription || "Reading the active display…"

    property string selectedOutput: ""
    readonly property var monitor: Displays.monitorNamed(root.selectedOutput)
        ?? (Displays.monitors.length > 0 ? Displays.monitors[0] : null)
    readonly property string currentMode: root.monitor
        ? root.monitor.mode
        : ""

    function syncSelectedOutput(): void {
        if (!Displays.monitorNamed(root.selectedOutput))
            root.selectedOutput = Displays.monitors.length > 0 ? Displays.monitors[0].name : "";
    }

    // Probing I2C for DDC-capable monitors takes on the order of a second, so
    // it runs when this page is opened rather than at shell startup. Monitors
    // do not appear while you are looking at a settings page, so once is enough.
    Component.onCompleted: {
        root.syncSelectedOutput();
        if (!Brightness.scanned)
            Brightness.refresh();
    }
    Connections {
        target: Displays
        function onMonitorsChanged(): void { root.syncSelectedOutput(); }
    }

    // The confirmation sits above everything, because while it is counting down
    // it is the only thing that matters on this page.
    header: Component {
        Rectangle {
            visible: Displays.awaitingConfirmation
            implicitHeight: visible ? confirmRow.implicitHeight + 28 : 0
            radius: Theme.cardRadius
            color: Theme.mix(Theme.bgPanel, Theme.warn, 0.12)
            border.width: 1
            border.color: Theme.alpha(Theme.warn, 0.4)

            Row {
                id: confirmRow
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                anchors.margins: 16
                spacing: 14

                Column {
                    width: parent.width - keepButton.width - revertButton.width - 28
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 3

                    Text {
                        width: parent.width
                        text: "Keep this display setting?"
                        color: Theme.fg
                        font.family: Theme.fontFamily
                        font.pixelSize: Theme.fontSize
                        font.weight: Font.DemiBold
                    }
                    Text {
                        width: parent.width
                        text: "Reverting in " + Displays.secondsLeft + (Displays.secondsLeft === 1 ? " second" : " seconds")
                            + " if you do nothing. If you cannot read this, just wait."
                        color: Theme.fgDim
                        font.family: Theme.fontFamily
                        font.features: Theme.tabularFigures
                        font.pixelSize: Theme.fontSizeSmall
                        wrapMode: Text.WordWrap
                    }
                }

                SettingsButton {
                    id: revertButton
                    anchors.verticalCenter: parent.verticalCenter
                    text: "Revert now"
                    onClicked: Displays.revert()
                }
                SettingsButton {
                    id: keepButton
                    anchors.verticalCenter: parent.verticalCenter
                    text: "Keep"
                    enabled: Displays.canConfirm
                    onClicked: Displays.confirm()
                }
            }
        }
    }

    SettingsCard {
        visible: Displays.monitors.length > 1
        title: "Arrange displays"
        subtitle: "Drag the screens into place. The primary display anchors the desktop at 0,0."

        DisplayArrangement {
            width: parent.width
            displayService: Displays
            selectedOutput: root.selectedOutput
            interactionEnabled: !Displays.awaitingConfirmation && !Displays.busy
            onSelectionRequested: output => root.selectedOutput = output
        }
    }

    SettingsCard {
        visible: Displays.monitors.length > 1
        title: "Connected display"
        subtitle: "Choose the output whose resolution, scale, and rotation you want to adjust."

        ChoiceGrid {
            width: parent.width
            label: "Display"
            options: Displays.monitors.map(monitor => ({
                value: monitor.name,
                label: monitor.description || monitor.name
            }))
            current: root.monitor ? root.monitor.name : ""
            enabled: !Displays.awaitingConfirmation && !Displays.busy
            divider: false
            onPicked: value => root.selectedOutput = value
        }
    }

    SettingsCard {
        title: root.monitor ? root.monitor.name : (SystemSettings.monitorName || "Active display")
        subtitle: root.monitor
            ? `${root.monitor.description} · ${root.monitor.width} × ${root.monitor.height} at ${Math.round(root.monitor.refreshRate)} Hz · ${root.monitor.scale.toFixed(2)}× scale`
            : "Reading the active display…"

        TextRow {
            label: "Color mode"
            detail: "Wide-gamut SDR at 10-bit. Full-time HDR is left to the Hyprland config: it currently breaks screenshots, OBS, and the lock screen's blurred background."
            value: root.monitor
                ? `${root.monitor.colorPreset || "standard"} · ${root.monitor.currentFormat || "detecting format"}`
                : "Detecting"
        }
        TextRow {
            label: "Variable refresh"
            detail: root.monitor && root.monitor.vrr
                ? "Active on this output for current fullscreen content"
                : "This output is ready when game or video content requests it"
            value: root.monitor && root.monitor.vrr ? "Active" : "Standby"
            divider: Displays.isOverridden(root.monitor ? root.monitor.name : "")
        }
        ActionRow {
            visible: Displays.isOverridden(root.monitor ? root.monitor.name : "")
            label: "Using a custom display setting"
            detail: "Forget it to go back to the shipped resolution and scale"
            action: "Forget"
            divider: false
            onTriggered: Displays.forget(root.monitor.name)
        }
    }

    SettingsCard {
        visible: root.monitor !== null
        title: "Resolution"
        subtitle: "Applied straight away, then reverted automatically unless you confirm."

        DisplayModePicker {
            width: parent.width
            monitor: root.monitor
            enabled: !Displays.awaitingConfirmation && !Displays.busy
        }
    }

    SettingsCard {
        visible: root.monitor !== null
        title: "Scale and rotation"

        ChoiceGrid {
            width: parent.width
            label: "Scale"
            detail: "Fractional scales that do not divide the resolution into whole pixels are rejected by the compositor, so only clean ones are offered."
            options: Displays.scalesForMode(root.currentMode)
                .map(scale => ({ value: scale, label: scale.toFixed(2) + "×" }))
            current: root.monitor ? root.monitor.scale : 1
            enabled: !Displays.awaitingConfirmation && !Displays.busy
            onPicked: value => root.applyWith({ scale: value })
        }

        ChoiceGrid {
            width: parent.width
            label: "Rotation"
            options: Displays.transforms
            current: root.monitor ? root.monitor.transform : 0
            enabled: !Displays.awaitingConfirmation && !Displays.busy
            divider: false
            onPicked: value => root.applyWith({ transform: value })
        }
    }

    SettingsCard {
        title: "Night Light"
        subtitle: NightLight.active
            ? "On now, warming the display to reduce blue light."
            : "Warms the display in the evening to reduce blue light."

        ToggleRow { setting: "nightLightEnabled" }
        ToggleRow { setting: "nightLightAutomatic" }
        TimeOfDayRow { setting: "nightLightFrom" }
        TimeOfDayRow { setting: "nightLightTo" }
        SliderRow { setting: "nightLightTemperature"; divider: false }
    }

    // Panel brightness, over DDC/CI.
    //
    // This is hardware state rather than a stored preference: the monitor
    // remembers it, the bezel buttons change it behind Panama's back, and
    // writing it into settings.json would mean restoring a value the panel had
    // already moved on from. So there is no schema key here and no SliderRow --
    // the rows read and write the display directly.
    SettingsCard {
        visible: Brightness.available || Brightness.lastError !== ""
        title: "Brightness"
        subtitle: Brightness.available
            ? "Sent to the monitor over DDC/CI, the same channel its buttons use."
            : Brightness.lastError

        Repeater {
            model: Brightness.displays

            SettingRow {
                id: brightnessRow
                required property var modelData
                required property int index

                label: Displays.monitorNamed(modelData.connector)?.description || modelData.connector
                detail: modelData.connector ? modelData.connector + " · " + modelData.value + "%"
                                            : modelData.value + "%"
                divider: brightnessRow.index < Brightness.displays.length - 1
                controlWidth: 190

                ValueSlider {
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.right: parent.right
                    width: parent.width
                    value: brightnessRow.modelData.value / 100
                    onMoved: v => Brightness.set(brightnessRow.modelData.bus, Math.round(v * 100))
                }
            }
        }
    }

    SettingsCard {
        title: "Gaming display policy"
        subtitle: "Applied immediately and restored when the session starts."

        ToggleRow { setting: "autoHdr" }
        ChoiceRow { setting: "vrrPolicy" }
        ChoiceRow { setting: "directScanoutPolicy"; divider: false }
    }

    SettingsCard {
        visible: Displays.lastError !== ""
        title: "Display problem"
        subtitle: Displays.lastError
    }

    // Applies a change to one field, keeping the others at what is in effect.
    function applyWith(change: var): void {
        if (!root.monitor)
            return;
        const mode = change.mode ?? root.currentMode;
        const requestedScale = change.scale ?? root.monitor.scale;
        Displays.apply(
            root.monitor.name,
            mode,
            Displays.isScaleClean(mode, requestedScale)
                ? requestedScale
                : Displays.nearestCleanScale(mode, requestedScale),
            change.transform ?? root.monitor.transform);
    }
}
