pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Layouts
import Caelestia.Config
import qs.components
import qs.services

RowLayout {
    id: root

    property real value
    property real max: Infinity
    property real min: -Infinity
    property real step: 1
    property alias repeatRate: timer.interval

    property bool isEditing: false
    property string displayText: root.value.toString()

    signal valueModified(value: real)

    spacing: Tokens.spacing.small

    onValueChanged: {
        if (!root.isEditing) {
            root.displayText = root.value.toString();
        }
    }

    StyledTextField {
        id: textField

        inputMethodHints: Qt.ImhFormattedNumbersOnly
        text: root.isEditing ? text : root.displayText
        validator: DoubleValidator {
            bottom: root.min
            top: root.max
            decimals: root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0
        }
        onActiveFocusChanged: {
            if (activeFocus) {
                root.isEditing = true;
            } else {
                root.isEditing = false;
                root.displayText = root.value.toString();
            }
        }
        onAccepted: {
            const numValue = parseFloat(text);
            if (!isNaN(numValue)) {
                const clampedValue = Math.max(root.min, Math.min(root.max, numValue));
                root.value = clampedValue;
                root.displayText = clampedValue.toString();
                root.valueModified(clampedValue);
            } else {
                text = root.displayText;
            }
            root.isEditing = false;
        }
        onEditingFinished: {
            if (text !== root.displayText) {
                const numValue = parseFloat(text);
                if (!isNaN(numValue)) {
                    const clampedValue = Math.max(root.min, Math.min(root.max, numValue));
                    root.value = clampedValue;
                    root.displayText = clampedValue.toString();
                    root.valueModified(clampedValue);
                } else {
                    text = root.displayText;
                }
            }
            root.isEditing = false;
        }

        padding: Tokens.padding.extraSmall
        leftPadding: Tokens.padding.medium
        rightPadding: Tokens.padding.medium

        background: StyledRect {
            implicitWidth: 100
            radius: Tokens.rounding.medium
            color: Colours.tPalette.m3surfaceContainerHigh
        }
    }

    StyledRect {
        radius: Tokens.rounding.medium
        color: Colours.palette.m3primary

        implicitWidth: implicitHeight
        implicitHeight: upIcon.implicitHeight + Tokens.padding.small

        StateLayer {
            id: upState

            color: Colours.palette.m3onPrimary

            onPressAndHold: timer.start()
            onReleased: timer.stop()

            onClicked: {
                let newValue = Math.min(root.max, root.value + root.step);
                // Round to avoid floating point precision errors
                const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
                newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
                root.value = newValue;
                root.displayText = newValue.toString();
                root.valueModified(newValue);
            }
        }

        MaterialIcon {
            id: upIcon

            anchors.centerIn: parent
            text: "keyboard_arrow_up"
            color: Colours.palette.m3onPrimary
        }
    }

    StyledRect {
        radius: Tokens.rounding.medium
        color: Colours.palette.m3primary

        implicitWidth: implicitHeight
        implicitHeight: downIcon.implicitHeight + Tokens.padding.small

        StateLayer {
            id: downState

            onClicked: {
                let newValue = Math.max(root.min, root.value - root.step);
                // Round to avoid floating point precision errors
                const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
                newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
                root.value = newValue;
                root.displayText = newValue.toString();
                root.valueModified(newValue);
            }

            color: Colours.palette.m3onPrimary

            onPressAndHold: timer.start()
            onReleased: timer.stop()
        }

        MaterialIcon {
            id: downIcon

            anchors.centerIn: parent
            text: "keyboard_arrow_down"
            color: Colours.palette.m3onPrimary
        }
    }

    Timer {
        id: timer

        interval: 100
        repeat: true
        triggeredOnStart: true
        onTriggered: {
            if (upState.pressed)
                upState.clicked();
            else if (downState.pressed)
                downState.clicked();
        }
    }
}
