pragma Singleton
pragma ComponentBehavior: Bound

import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common

Singleton {
    id: root

    property bool cyclingActive: false
    property string cachedCyclingTime: SessionData.wallpaperCyclingTime
    property int cachedCyclingInterval: SessionData.wallpaperCyclingInterval
    property string lastTimeCheck: ""
    property var monitorTimers: ({})
    property var monitorLastTimeChecks: ({})
    property var monitorProcesses: ({})
    Component.onCompleted: {
        updateCyclingState()
    }

    Component {
        id: monitorTimerComponent
        Timer {
            property string targetScreen: ""
            running: false
            repeat: true
            onTriggered: {
                if (typeof WallpaperCyclingService !== "undefined" && targetScreen !== "") {
                    WallpaperCyclingService.cycleNextForMonitor(targetScreen)
                }
            }
        }
    }

    Component {
        id: monitorProcessComponent
        Process {
            property string targetScreenName: ""
            property string currentWallpaper: ""
            property bool goToPrevious: false
            running: false
            stdout: StdioCollector {
                onStreamFinished: {
                    if (text && text.trim()) {
                        const files = text.trim().split('\n').filter(file => file.length > 0)
                        if (files.length <= 1) return
                        const wallpaperList = files.sort()
                        const currentPath = currentWallpaper
                        let currentIndex = wallpaperList.findIndex(path => path === currentPath)
                        if (currentIndex === -1) currentIndex = 0
                        let targetIndex
                        if (goToPrevious) {
                            targetIndex = currentIndex === 0 ? wallpaperList.length - 1 : currentIndex - 1
                        } else {
                            targetIndex = (currentIndex + 1) % wallpaperList.length
                        }
                        const targetWallpaper = wallpaperList[targetIndex]
                        if (targetWallpaper && targetWallpaper !== currentPath) {
                            if (targetScreenName) {
                                SessionData.setMonitorWallpaper(targetScreenName, targetWallpaper)
                            } else {
                                SessionData.setWallpaper(targetWallpaper)
                            }
                        }
                    }
                }
            }
        }
    }

    Connections {
        target: SessionData

        function onWallpaperCyclingEnabledChanged() {
            updateCyclingState()
        }

        function onWallpaperCyclingModeChanged() {
            updateCyclingState()
        }

        function onWallpaperCyclingIntervalChanged() {
            cachedCyclingInterval = SessionData.wallpaperCyclingInterval
            if (SessionData.wallpaperCyclingMode === "interval") {
                updateCyclingState()
            }
        }

        function onWallpaperCyclingTimeChanged() {
            cachedCyclingTime = SessionData.wallpaperCyclingTime
            if (SessionData.wallpaperCyclingMode === "time") {
                updateCyclingState()
            }
        }

        function onPerMonitorWallpaperChanged() {
            updateCyclingState()
        }

        function onMonitorCyclingSettingsChanged() {
            updateCyclingState()
        }
    }

    function updateCyclingState() {
        if (SessionData.perMonitorWallpaper) {
            stopCycling()
            updatePerMonitorCycling()
        } else if (SessionData.wallpaperCyclingEnabled && SessionData.wallpaperPath) {
            startCycling()
            stopAllMonitorCycling()
        } else {
            stopCycling()
            stopAllMonitorCycling()
        }
    }

    function updatePerMonitorCycling() {
        if (typeof Quickshell === "undefined") return

        var screens = Quickshell.screens
        for (var i = 0; i < screens.length; i++) {
            var screenName = screens[i].name
            var settings = SessionData.getMonitorCyclingSettings(screenName)
            var wallpaper = SessionData.getMonitorWallpaper(screenName)

            if (settings.enabled && wallpaper && !wallpaper.startsWith("#") && !wallpaper.startsWith("we:")) {
                startMonitorCycling(screenName, settings)
            } else {
                stopMonitorCycling(screenName)
            }
        }
    }

    function stopAllMonitorCycling() {
        var screenNames = Object.keys(monitorTimers)
        for (var i = 0; i < screenNames.length; i++) {
            stopMonitorCycling(screenNames[i])
        }
    }

    function startCycling() {
        if (SessionData.wallpaperCyclingMode === "interval") {
            intervalTimer.interval = cachedCyclingInterval * 1000
            intervalTimer.start()
            cyclingActive = true
        } else if (SessionData.wallpaperCyclingMode === "time") {
            cyclingActive = true
            checkTimeBasedCycling()
        }
    }

    function stopCycling() {
        intervalTimer.stop()
        cyclingActive = false
    }

    function startMonitorCycling(screenName, settings) {
        if (settings.mode === "interval") {
            var timer = monitorTimers[screenName]
            if (!timer && monitorTimerComponent && monitorTimerComponent.status === Component.Ready) {
                var newTimers = Object.assign({}, monitorTimers)
                newTimers[screenName] = monitorTimerComponent.createObject(root)
                newTimers[screenName].targetScreen = screenName
                monitorTimers = newTimers
                timer = monitorTimers[screenName]
            }
            if (timer) {
                timer.interval = settings.interval * 1000
                timer.start()
            }
        } else if (settings.mode === "time") {
            var newChecks = Object.assign({}, monitorLastTimeChecks)
            newChecks[screenName] = ""
            monitorLastTimeChecks = newChecks
        }
    }

    function stopMonitorCycling(screenName) {
        var timer = monitorTimers[screenName]
        if (timer) {
            timer.stop()
            timer.destroy()
            var newTimers = Object.assign({}, monitorTimers)
            delete newTimers[screenName]
            monitorTimers = newTimers
        }

        var process = monitorProcesses[screenName]
        if (process) {
            process.destroy()
            var newProcesses = Object.assign({}, monitorProcesses)
            delete newProcesses[screenName]
            monitorProcesses = newProcesses
        }

        var newChecks = Object.assign({}, monitorLastTimeChecks)
        delete newChecks[screenName]
        monitorLastTimeChecks = newChecks
    }

    function cycleToNextWallpaper(screenName, wallpaperPath) {
        const currentWallpaper = wallpaperPath || SessionData.wallpaperPath
        if (!currentWallpaper) return

        const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))

        if (screenName && monitorProcessComponent && monitorProcessComponent.status === Component.Ready) {
            // Use per-monitor process
            var process = monitorProcesses[screenName]
            if (!process) {
                var newProcesses = Object.assign({}, monitorProcesses)
                newProcesses[screenName] = monitorProcessComponent.createObject(root)
                monitorProcesses = newProcesses
                process = monitorProcesses[screenName]
            }

            if (process) {
                process.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
                process.targetScreenName = screenName
                process.currentWallpaper = currentWallpaper
                process.goToPrevious = false
                process.running = true
            }
        } else {
            // Use global process for fallback
            cyclingProcess.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
            cyclingProcess.targetScreenName = screenName || ""
            cyclingProcess.currentWallpaper = currentWallpaper
            cyclingProcess.running = true
        }
    }

    function cycleToPrevWallpaper(screenName, wallpaperPath) {
        const currentWallpaper = wallpaperPath || SessionData.wallpaperPath
        if (!currentWallpaper) return

        const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/'))

        if (screenName && monitorProcessComponent && monitorProcessComponent.status === Component.Ready) {
            // Use per-monitor process (same as next, but with prev flag)
            var process = monitorProcesses[screenName]
            if (!process) {
                var newProcesses = Object.assign({}, monitorProcesses)
                newProcesses[screenName] = monitorProcessComponent.createObject(root)
                monitorProcesses = newProcesses
                process = monitorProcesses[screenName]
            }

            if (process) {
                process.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
                process.targetScreenName = screenName
                process.currentWallpaper = currentWallpaper
                process.goToPrevious = true
                process.running = true
            }
        } else {
            // Use global process for fallback
            prevCyclingProcess.command = ["sh", "-c", `ls -1 "${wallpaperDir}"/*.jpg "${wallpaperDir}"/*.jpeg "${wallpaperDir}"/*.png "${wallpaperDir}"/*.bmp "${wallpaperDir}"/*.gif "${wallpaperDir}"/*.webp 2>/dev/null | sort`]
            prevCyclingProcess.targetScreenName = screenName || ""
            prevCyclingProcess.currentWallpaper = currentWallpaper
            prevCyclingProcess.running = true
        }
    }

    function cycleNextManually() {
        if (SessionData.wallpaperPath) {
            cycleToNextWallpaper()
            // Restart timers if cycling is active
            if (cyclingActive && SessionData.wallpaperCyclingEnabled) {
                if (SessionData.wallpaperCyclingMode === "interval") {
                    intervalTimer.interval = cachedCyclingInterval * 1000
                    intervalTimer.restart()
                }
            }
        }
    }

    function cyclePrevManually() {
        if (SessionData.wallpaperPath) {
            cycleToPrevWallpaper()
            // Restart timers if cycling is active
            if (cyclingActive && SessionData.wallpaperCyclingEnabled) {
                if (SessionData.wallpaperCyclingMode === "interval") {
                    intervalTimer.interval = cachedCyclingInterval * 1000
                    intervalTimer.restart()
                }
            }
        }
    }

    function cycleNextForMonitor(screenName) {
        if (!screenName) return

        var currentWallpaper = SessionData.getMonitorWallpaper(screenName)
        if (currentWallpaper) {
            cycleToNextWallpaper(screenName, currentWallpaper)
        }
    }

    function cyclePrevForMonitor(screenName) {
        if (!screenName) return

        var currentWallpaper = SessionData.getMonitorWallpaper(screenName)
        if (currentWallpaper) {
            cycleToPrevWallpaper(screenName, currentWallpaper)
        }
    }

    function checkTimeBasedCycling() {
        const currentTime = Qt.formatTime(systemClock.date, "hh:mm")

        if (!SessionData.perMonitorWallpaper) {
            if (currentTime === cachedCyclingTime && currentTime !== lastTimeCheck) {
                lastTimeCheck = currentTime
                cycleToNextWallpaper()
            } else if (currentTime !== cachedCyclingTime) {
                lastTimeCheck = ""
            }
        } else {
            checkPerMonitorTimeBasedCycling(currentTime)
        }
    }

    function checkPerMonitorTimeBasedCycling(currentTime) {
        if (typeof Quickshell === "undefined") return

        var screens = Quickshell.screens
        for (var i = 0; i < screens.length; i++) {
            var screenName = screens[i].name
            var settings = SessionData.getMonitorCyclingSettings(screenName)
            var wallpaper = SessionData.getMonitorWallpaper(screenName)

            if (settings.enabled && settings.mode === "time" && wallpaper && !wallpaper.startsWith("#") && !wallpaper.startsWith("we:")) {
                var lastCheck = monitorLastTimeChecks[screenName] || ""

                if (currentTime === settings.time && currentTime !== lastCheck) {
                    var newChecks = Object.assign({}, monitorLastTimeChecks)
                    newChecks[screenName] = currentTime
                    monitorLastTimeChecks = newChecks
                    cycleNextForMonitor(screenName)
                } else if (currentTime !== settings.time) {
                    var newChecks = Object.assign({}, monitorLastTimeChecks)
                    newChecks[screenName] = ""
                    monitorLastTimeChecks = newChecks
                }
            }
        }
    }

    Timer {
        id: intervalTimer
        interval: cachedCyclingInterval * 1000
        running: false
        repeat: true
        onTriggered: cycleToNextWallpaper()
    }

    SystemClock {
        id: systemClock
        precision: SystemClock.Minutes
        onDateChanged: {
            if ((SessionData.wallpaperCyclingMode === "time" && cyclingActive) || SessionData.perMonitorWallpaper) {
                checkTimeBasedCycling()
            }
        }
    }

    Process {
        id: cyclingProcess

        property string targetScreenName: ""
        property string currentWallpaper: ""

        running: false

        stdout: StdioCollector {
            onStreamFinished: {
                if (text && text.trim()) {
                    const files = text.trim().split('\n').filter(file => file.length > 0)
                    if (files.length <= 1) return

                    const wallpaperList = files.sort()
                    const currentPath = cyclingProcess.currentWallpaper
                    let currentIndex = wallpaperList.findIndex(path => path === currentPath)
                    if (currentIndex === -1) currentIndex = 0

                    const nextIndex = (currentIndex + 1) % wallpaperList.length
                    const nextWallpaper = wallpaperList[nextIndex]

                    if (nextWallpaper && nextWallpaper !== currentPath) {
                        if (cyclingProcess.targetScreenName) {
                            SessionData.setMonitorWallpaper(cyclingProcess.targetScreenName, nextWallpaper)
                        } else {
                            SessionData.setWallpaper(nextWallpaper)
                        }
                    }
                }
            }
        }
    }

    Process {
        id: prevCyclingProcess

        property string targetScreenName: ""
        property string currentWallpaper: ""

        running: false

        stdout: StdioCollector {
            onStreamFinished: {
                if (text && text.trim()) {
                    const files = text.trim().split('\n').filter(file => file.length > 0)
                    if (files.length <= 1) return

                    const wallpaperList = files.sort()
                    const currentPath = prevCyclingProcess.currentWallpaper
                    let currentIndex = wallpaperList.findIndex(path => path === currentPath)
                    if (currentIndex === -1) currentIndex = 0

                    const prevIndex = currentIndex === 0 ? wallpaperList.length - 1 : currentIndex - 1
                    const prevWallpaper = wallpaperList[prevIndex]

                    if (prevWallpaper && prevWallpaper !== currentPath) {
                        if (prevCyclingProcess.targetScreenName) {
                            SessionData.setMonitorWallpaper(prevCyclingProcess.targetScreenName, prevWallpaper)
                        } else {
                            SessionData.setWallpaper(prevWallpaper)
                        }
                    }
                }
            }
        }
    }

}
