import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Services.Pipewire

import "../../config" as Cfg
import "../../components"

ModuleChip {
    id: root

    property int outputMaxVolume: Cfg.Config.volumeOutputMax
    property int inputMaxVolume:  Cfg.Config.volumeInputMax

    readonly property string configPath:
        Quickshell.env("HOME") + "/.config/quickshell/config/config.qml"

    Process {
        id: volConfigWriter
        running: false
        onExited: (code) => {
            if (code !== 0)
                console.warn("[Volume] Failed to patch config.qml (exit " + code + ")")
        }
    }

    Timer {
        id: saveDebounce
        interval: 800
        repeat:   false
        onTriggered: {
            const outMax  = Math.round(root.outputMaxVolume).toString()
            const inMax   = Math.round(root.inputMaxVolume).toString()
            const cfgPath = root.configPath
            const sh =
                "sed -i" +
                " -e 's/\\(property int[ ]\\+volumeOutputMax:[ ]\\+\\)[0-9.]\\+/\\1" + outMax + "/'" +
                " -e 's/\\(property int[ ]\\+volumeInputMax:[ ]\\+\\)[0-9.]\\+/\\1" + inMax + "/'" +
                " " + JSON.stringify(cfgPath)
            volConfigWriter.command = ["/bin/sh", "-c", sh]
            volConfigWriter.running = true
        }
    }

    onOutputMaxVolumeChanged: { if (root.outputMaxVolume >= 10) saveDebounce.restart() }
    onInputMaxVolumeChanged:  { if (root.inputMaxVolume  >= 10) saveDebounce.restart() }

    readonly property var sink:   Pipewire.defaultAudioSink
    readonly property var source: Pipewire.defaultAudioSource

    PwObjectTracker {
        objects: root.sinkNodes.concat(root.sourceNodes)
    }

    readonly property var sinkNodes: {
        if (!Pipewire.nodes) return []
        return Pipewire.nodes.values.filter(n => !n.isStream && n.isSink)
    }
    readonly property var sourceNodes: {
        if (!Pipewire.nodes) return []
        return Pipewire.nodes.values.filter(n => !n.isStream && !n.isSink)
    }

    property int _outVol: 0
    property int _inVol:  0
    
    readonly property int outputVolume: {
        if (sink && sink.ready) { _outVol = Math.round(sink.audio.volume * 100); return _outVol }
        return _outVol
    }
    readonly property int inputVolume: {
        if (source && source.ready) { _inVol = Math.round(source.audio.volume * 100); return _inVol }
        return _inVol
    }
                                        
    readonly property bool outputMuted:  sink   && sink.ready ? sink.audio.muted   : false
    readonly property bool inputMuted:   source && source.ready ? source.audio.muted : false

    readonly property string outputDeviceName: sink   ? (sink.description   || sink.nickName   || sink.name)   : "No device"
    readonly property string inputDeviceName:  source ? (source.description || source.nickName || source.name) : "No device"

    function setOutputVol(pct)  { if (sink   && sink.ready) sink.audio.volume   = pct / 100 }
    function setInputVol(pct)   { if (source && source.ready) source.audio.volume = pct / 100 }
    function toggleOutputMute() { if (sink   && sink.ready) sink.audio.muted     = !sink.audio.muted   }
    function toggleInputMute()  { if (source && source.ready) source.audio.muted  = !source.audio.muted }

    Process {
        id: defaultSetter
        running: false
        property string nodeId: "0"
        command: ["wpctl", "set-default", nodeId]
    }
    function setDefaultDevice(node) {
        defaultSetter.nodeId = node.id.toString()
        defaultSetter.running = true
    }

    readonly property string iconSpeakerHigh:  "󰕾"
    readonly property string iconSpeakerMed:   "󰖀"
    readonly property string iconSpeakerLow:   "󰕿"
    readonly property string iconSpeakerMuted: "󰖁"
    readonly property string iconMic:          "󰍬"
    readonly property string iconMicMuted:     "󰍭"

    function speakerGlyph(muted, vol) {
        if (muted || vol === 0) return iconSpeakerMuted
        if (vol > 60)           return iconSpeakerHigh
        if (vol > 25)           return iconSpeakerMed
        return iconSpeakerLow
    }

    width:  chipLayout.implicitWidth + 15
    height: Cfg.Config.moduleHeight
    radius: panelRadius

    color:        hoverHandler.hovered ? t.volBgHover : t.volBg
    border.color: (hoverHandler.hovered || volumePopup.popupOpen) ? t.volBorderHover : t.volBorder
    border.width: borderWidth

    Behavior on color        { ColorAnimation { duration: 150 } }
    Behavior on border.color { ColorAnimation { duration: 150 } }

    HoverHandler { 
        id: hoverHandler
        cursorShape: Qt.PointingHandCursor 
        onHoveredChanged: {
            if (hovered && Cfg.Config.volumeShakeEnabled) shakeAnim.restart()
        }
    }
    TapHandler   { onTapped: volumePopup.toggle() }

    SequentialAnimation {
        id: shakeAnim
        NumberAnimation { target: chipLayout; property: "rotation"; to: -15; duration: 40; easing.type: Easing.OutQuad }
        NumberAnimation { target: chipLayout; property: "rotation"; to: 15; duration: 80; easing.type: Easing.InOutQuad }
        NumberAnimation { target: chipLayout; property: "rotation"; to: -10; duration: 80; easing.type: Easing.InOutQuad }
        NumberAnimation { target: chipLayout; property: "rotation"; to: 10; duration: 80; easing.type: Easing.InOutQuad }
        NumberAnimation { target: chipLayout; property: "rotation"; to: 0; duration: 40; easing.type: Easing.InQuad }
    }

    RowLayout {
        id: chipLayout
        anchors.centerIn: parent
        spacing: 5
        transformOrigin: Item.Center

        Text {
            text:           root.speakerGlyph(root.outputMuted, root.outputVolume)
            color:          root.outputMuted ? root.t.volIconMuted : root.t.volIcon
            font.pixelSize: Cfg.Config.volumeIconSize
            font.bold:      true
            Layout.alignment: Qt.AlignVCenter
            renderType:     Text.NativeRendering
        }
    }

    StandardPopup {
        id: volumePopup
        ns: "volume"
        chipRoot: root
        cardWidth: Cfg.Config.volumePopupWidth
        cardColor: root.t.volPopupBg
        cardBorderColor: root.t.volPopupBorder
        cardHeight: popupCol.implicitHeight + 30

        ColumnLayout {
            id: popupCol
            anchors { left: parent.left; right: parent.right; top: parent.top }
            anchors.margins:   16
            anchors.topMargin: 16
            spacing: 16

                    VolumeSection {
                        id: outputSection
                        Layout.fillWidth: true
                        label:       "Output"
                        icon:        root.speakerGlyph(root.outputMuted, root.outputVolume)
                        iconColor:   root.t.accent
                        volume:      root.outputVolume
                        maxVolume:   root.outputMaxVolume
                        muted:       root.outputMuted
                        accentColor: root.t.volHandle
                        dimColor:    root.t.textDim
                        textColor:   root.t.textMain
                        trackColor:  root.t.volTrack
                        deviceName:  root.outputDeviceName
                        nodes:       root.sinkNodes
                        currentNode: root.sink
                        onSliderMoved:    vol  => root.setOutputVol(vol)
                        onMuteToggled:    root.toggleOutputMute()
                        onDeviceSelected: node => root.setDefaultDevice(node)
                    }

                    Rectangle { Layout.fillWidth: true; height: 1; color: root.t.separator }

                    VolumeSection {
                        id: inputSection
                        Layout.fillWidth: true
                        label:       "Input"
                        icon:        root.inputMuted ? root.iconMicMuted : root.iconMic
                        iconColor:   root.t.statusInfo
                        volume:      root.inputVolume
                        maxVolume:   root.inputMaxVolume
                        muted:       root.inputMuted
                        accentColor: root.t.statusInfo
                        dimColor:    root.t.textDim
                        textColor:   root.t.textMain
                        trackColor:  root.t.volTrack
                        deviceName:  root.inputDeviceName
                        nodes:       root.sourceNodes
                        currentNode: root.source
                        onSliderMoved:    vol  => root.setInputVol(vol)
                        onMuteToggled:    root.toggleInputMute()
                        onDeviceSelected: node => root.setDefaultDevice(node)
                    }

                    Rectangle { Layout.fillWidth: true; height: 1; color: root.t.separator }

                    ColumnLayout {
                        Layout.fillWidth: true
                        spacing: 10
                        Text {
                            text: "Volume Limits"
                            color: root.t.textDim
                            font.pixelSize: 11
                            font.bold: true
                            font.capitalization: Font.AllUppercase
                            Layout.fillWidth: true
                            renderType: Text.NativeRendering
                        }
                        LimitRow {
                            Layout.fillWidth: true
                            icon:        root.iconSpeakerHigh
                            iconColor:   root.t.accent
                            labelText:   "Output max"
                            value:       root.outputMaxVolume
                            minValue:    10
                            maxValue:    300
                            textColor:   root.t.textMain
                            dimColor:    root.t.textDim
                            borderColor: root.t.volPopupBorder
                            btnIdle:     root.t.volLimitBtn
                            btnHover:    root.t.volOutputLimitBtnHover
                            btnPress:    root.t.volOutputLimitBtnPress
                            onDecrement: root.outputMaxVolume = Math.max(10,  root.outputMaxVolume - 10)
                            onIncrement: root.outputMaxVolume = Math.min(300, root.outputMaxVolume + 10)
                        }
                        LimitRow {
                            Layout.fillWidth: true
                            icon:        root.iconMic
                            iconColor:   root.t.statusInfo
                            labelText:   "Input max"
                            value:       root.inputMaxVolume
                            minValue:    10
                            maxValue:    300
                            textColor:   root.t.textMain
                            dimColor:    root.t.textDim
                            borderColor: root.t.volPopupBorder
                            btnIdle:     root.t.volLimitBtn
                            btnHover:    root.t.volInputLimitBtnHover
                            btnPress:    root.t.volInputLimitBtnPress
                            onDecrement: root.inputMaxVolume = Math.max(10,  root.inputMaxVolume - 10)
                            onIncrement: root.inputMaxVolume = Math.min(300, root.inputMaxVolume + 10)
                        }
                    }
                    Item { height: 0 }
                }
    }

    component VolumeSection: ColumnLayout {
        id: vs
        spacing: 11
        required property string label
        required property string icon
        required property color  iconColor
        required property int    volume
        required property int    maxVolume
        required property bool   muted
        required property color  accentColor
        required property color  dimColor
        required property color  textColor
        required property color  trackColor
        required property string deviceName
        required property var    nodes
        required property var    currentNode

        signal sliderMoved(int vol)
        signal muteToggled()
        signal deviceSelected(var node)

        property bool pickerOpen: false

        RowLayout {
            Layout.fillWidth: true
            spacing: 6
            Text {
                text:                  vs.icon
                color:                 vs.muted ? vs.dimColor : vs.iconColor
                font.pixelSize:        Cfg.Config.volumeIconSize
                Layout.alignment:      Qt.AlignVCenter
                Layout.preferredWidth: Cfg.Config.volumeIconSize + 4
                horizontalAlignment:   Text.AlignHCenter
                renderType:            Text.NativeRendering
                font.bold:             true
            }
            Text {
                text:             vs.label
                color:            vs.textColor
                font.pixelSize:   14
                font.bold:        true
                Layout.fillWidth: true
                renderType:       Text.NativeRendering
            }
            Text {
                text:                  vs.muted ? "muted" : vs.volume + "%"
                color:                 vs.muted ? vs.dimColor : vs.textColor
                font.pixelSize:        13
                font.bold:             !vs.muted
                horizontalAlignment:   Text.AlignHCenter
                Layout.preferredWidth: 44
                renderType:            Text.NativeRendering
            }

            Rectangle {
                width: 24; height: 24; radius: 8
                color: muteHover.hovered ? (vs.muted ? "#33ffffff" : "#22ffffff") : (vs.muted ? root.t.volMuteBg : "transparent")
                border.color: vs.muted ? vs.accentColor : vs.dimColor
                border.width: Cfg.Config.popupBorderWidth
                Text {
                    anchors.centerIn: parent
                    text:           vs.muted ? "󰖁" : "󰕾"
                    color:          vs.muted ? vs.accentColor : vs.dimColor
                    font.pixelSize: 11
                    font.bold:      true
                    renderType:     Text.NativeRendering
                }
                HoverHandler { id: muteHover; cursorShape: Qt.PointingHandCursor }
                TapHandler   { onTapped: vs.muteToggled() }
            }
        }

        Item {
            id: sliderItem
            Layout.fillWidth: true
            height: 28
            Rectangle {
                anchors.verticalCenter: parent.verticalCenter
                width: parent.width; height: 6; radius: 3
                color: vs.trackColor
                Rectangle {
                    width:  Math.min(parent.width, parent.width * (vs.volume / Math.max(1, vs.maxVolume)))
                    height: parent.height; radius: parent.radius
                    color:  vs.muted ? vs.dimColor : vs.accentColor
                    Behavior on width { NumberAnimation { duration: 60; easing.type: Easing.OutCubic } }
                }
            }

            Rectangle {
                readonly property real ratio: vs.volume / Math.max(1, vs.maxVolume)
                x:      Math.min(sliderItem.width - width, Math.max(0, sliderItem.width * ratio - width / 2))
                anchors.verticalCenter: parent.verticalCenter
                width: Cfg.Config.volumeHandleSize; height: Cfg.Config.volumeHandleSize; radius: width / 2
                color:        vs.muted ? vs.dimColor : vs.accentColor
                border.color: root.t.volHandleBorder
                border.width: Cfg.Config.popupBorderWidth
            }
            MouseArea {
                anchors.fill: parent; cursorShape: Qt.PointingHandCursor
                onPressed:         pos => sliderItem.seek(pos.x)
                onPositionChanged: pos => { if (pressed) sliderItem.seek(pos.x) }
            }
            WheelHandler {
                acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
                onWheel: event => {
                    const step = 2
                    const delta = event.angleDelta.y > 0 ? step : -step
                    vs.sliderMoved(Math.max(0, Math.min(vs.maxVolume, vs.volume + delta)))
                }
            }
            function seek(x) {
                let ratio = Math.max(0.0, Math.min(1.0, x / sliderItem.width))
                vs.sliderMoved(Math.round(ratio * vs.maxVolume))
            }
        }

        RowLayout {
            Layout.fillWidth: true
            spacing: 6
            Text { text: "󰒓"; color: vs.dimColor; font.pixelSize: 11; font.bold: true; renderType: Text.NativeRendering }
            Text {
                text:             vs.deviceName
                color:            vs.textColor
                font.pixelSize:   11
                font.bold:        true
                elide:            Text.ElideRight
                Layout.fillWidth: true
                renderType:       Text.NativeRendering
            }

            Rectangle {
                width: 24; height: 24; radius: 8
                color:        chevHover.hovered ? "#22ffffff" : "transparent"
                border.color: vs.pickerOpen ? vs.accentColor : root.t.volPopupBorder
                border.width: Cfg.Config.popupBorderWidth
                Text {
                    anchors.centerIn: parent
                    text:           vs.pickerOpen ? "󰅃" : "󰅀"
                    color:          vs.pickerOpen ? vs.accentColor : vs.dimColor
                    font.pixelSize: 10
                    font.bold:      true
                    renderType:     Text.NativeRendering
                }
                HoverHandler { id: chevHover; cursorShape: Qt.PointingHandCursor }
                TapHandler   { onTapped: vs.pickerOpen = !vs.pickerOpen }
            }
        }

        Item {
            Layout.fillWidth: true
            Layout.preferredHeight: vs.pickerOpen ? Math.min(deviceFlickable.contentHeight, 120) : 0
            opacity:          vs.pickerOpen ? 1.0 : 0.0
            clip:             true

            Behavior on Layout.preferredHeight { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }
            Behavior on opacity { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }

            Flickable {
                id: deviceFlickable
                anchors.fill: parent
                contentWidth:  width
                contentHeight: deviceList.implicitHeight
                clip:          true
                boundsBehavior: Flickable.StopAtBounds

                ColumnLayout {
                    id: deviceList
                    width: deviceFlickable.width
                    spacing: 2
                    Repeater {
                        model: vs.nodes
                        delegate: Rectangle {
                            required property var  modelData
                            readonly property bool isActive: vs.currentNode !== null && modelData.id === vs.currentNode.id
                            Layout.fillWidth: true; height: 28; radius: 7
                            color: isActive ? Qt.alpha(vs.accentColor, 0.18) : (rowHover.hovered ? root.t.volPickerHover : "transparent")
                            border.color: isActive ? Qt.alpha(vs.accentColor, 0.45) : "transparent"
                            border.width: Cfg.Config.popupBorderWidth
                            RowLayout {
                                anchors { fill: parent; leftMargin: 8; rightMargin: 8 }
                                spacing: 8
                                Rectangle {
                                    width: 6; height: 6; radius: 3
                                    color:        isActive ? vs.accentColor : "transparent"
                                    border.color: isActive ? "transparent" : vs.dimColor
                                    border.width: Cfg.Config.popupBorderWidth
                                }
                                Text {
                                    text:             modelData.description || modelData.nickName || modelData.name
                                    color:            isActive ? vs.accentColor : vs.textColor
                                    font.pixelSize:   11; font.bold: true
                                    elide:            Text.ElideRight; Layout.fillWidth: true
                                    renderType:       Text.NativeRendering
                                }
                            }
                            HoverHandler { id: rowHover; cursorShape: Qt.PointingHandCursor }
                            TapHandler { onTapped: { vs.deviceSelected(modelData); vs.pickerOpen = false } }
                        }
                    }
                }
            }
        }
    }

    component LimitRow: RowLayout {
        id: lr
        spacing: 10
        required property string icon
        required property color  iconColor
        required property string labelText
        required property int    value
        required property int    minValue
        required property int    maxValue
        required property color  textColor
        required property color  dimColor
        required property color  borderColor
        required property color  btnIdle
        required property color  btnHover
        required property color  btnPress

        signal decrement()
        signal increment()

        Text { text: lr.icon; color: lr.iconColor; font.pixelSize: 16; font.bold: true; renderType: Text.NativeRendering }
        Text {
            text:             lr.labelText
            color:            lr.textColor
            font.pixelSize:   11
            font.bold:        true
            Layout.fillWidth: true
            renderType:       Text.NativeRendering
        }

        Rectangle {
            id: decBtn
            width: 24; height: 24; radius: 8
            color: decMa.containsMouse
                ? (decMa.pressed ? lr.btnPress : lr.btnHover)
                : lr.btnIdle
            border.color: lr.borderColor
            border.width: Cfg.Config.popupBorderWidth
            scale: decMa.pressed ? 0.88 : 1.0

            Behavior on color { ColorAnimation { duration: 120 } }
            Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutCubic } }

            Text {
                anchors.centerIn: parent; text: "−"
                color: lr.value <= lr.minValue ? lr.dimColor : lr.textColor
                font.pixelSize: 14
                font.bold:      true
                renderType:     Text.NativeRendering
            }
            MouseArea {
                id: decMa
                anchors.fill: parent
                hoverEnabled: true
                cursorShape: Qt.PointingHandCursor
                onClicked: { if (lr.value > lr.minValue) lr.decrement() }
            }
        }
        Text {
            text:                  lr.value + "%"
            color:                 lr.textColor
            font.pixelSize:        12; font.bold: true
            horizontalAlignment:   Text.AlignHCenter
            Layout.preferredWidth: 44
            renderType:            Text.NativeRendering
        }

        Rectangle {
            id: incBtn
            width: 24; height: 24; radius: 8
            color: incMa.containsMouse
                ? (incMa.pressed ? lr.btnPress : lr.btnHover)
                : lr.btnIdle
            border.color: lr.borderColor
            border.width: Cfg.Config.popupBorderWidth
            scale: incMa.pressed ? 0.88 : 1.0

            Behavior on color { ColorAnimation { duration: 120 } }
            Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutCubic } }

            Text {
                anchors.centerIn: parent; text: "+"
                color: lr.value >= lr.maxValue ? lr.dimColor : lr.textColor
                font.pixelSize: 14
                font.bold:      true
                renderType:     Text.NativeRendering
            }
            MouseArea {
                id: incMa
                anchors.fill: parent
                hoverEnabled: true
                cursorShape: Qt.PointingHandCursor
                onClicked: { if (lr.value < lr.maxValue) lr.increment() }
            }
        }
    }
}


