pragma Singleton
pragma ComponentBehavior: Bound

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

Singleton {
    id: root

    property bool brightnessAvailable: devices.length > 0
    property var devices: []
    property var deviceBrightness: ({})
    property var deviceBrightnessUserSet: ({})
    property var deviceMaxCache: ({})
    property var userControlledDevices: ({})
    property var pendingOsdDevices: ({})
    property int brightnessVersion: 0
    property string currentDevice: ""
    property string lastIpcDevice: ""
    property int brightnessLevel: {
        brightnessVersion;
        const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice);
        if (!deviceToUse) {
            return 50;
        }

        return getDeviceBrightness(deviceToUse);
    }
    property int maxBrightness: 100
    property bool brightnessInitialized: false
    property bool suppressOsd: true

    signal brightnessChanged(bool showOsd)
    signal deviceSwitched

    property bool nightModeActive: nightModeEnabled

    property bool nightModeEnabled: false
    property bool automationAvailable: false
    property bool gammaControlAvailable: false

    property var gammaState: ({})
    property int gammaCurrentTemp: gammaState?.currentTemp ?? 0
    property string gammaNextTransition: gammaState?.nextTransition ?? ""
    property string gammaSunriseTime: gammaState?.sunriseTime ?? ""
    property string gammaSunsetTime: gammaState?.sunsetTime ?? ""
    property string gammaDawnTime: gammaState?.dawnTime ?? ""
    property string gammaNightTime: gammaState?.nightTime ?? ""
    property bool gammaIsDay: gammaState?.isDay ?? true
    property real gammaSunPosition: gammaState?.sunPosition ?? 0
    property int gammaLowTemp: gammaState?.config?.LowTemp ?? 0
    property int gammaHighTemp: gammaState?.config?.HighTemp ?? 0

    function markDeviceUserControlled(deviceId) {
        const newControlled = Object.assign({}, userControlledDevices);
        newControlled[deviceId] = Date.now();
        userControlledDevices = newControlled;
    }

    function isDeviceUserControlled(deviceId) {
        const controlTime = userControlledDevices[deviceId];
        if (!controlTime) {
            return false;
        }
        return (Date.now() - controlTime) < 1000;
    }

    function clearDeviceUserControlled(deviceId) {
        const newControlled = Object.assign({}, userControlledDevices);
        delete newControlled[deviceId];
        userControlledDevices = newControlled;
    }

    function markDevicePendingOsd(deviceId) {
        const newPending = Object.assign({}, pendingOsdDevices);
        newPending[deviceId] = true;
        pendingOsdDevices = newPending;
    }

    function clearDevicePendingOsd(deviceId) {
        const newPending = Object.assign({}, pendingOsdDevices);
        delete newPending[deviceId];
        pendingOsdDevices = newPending;
    }

    function updateSingleDevice(device) {
        if (device.class === "leds") {
            return;
        }

        const isUserControlled = isDeviceUserControlled(device.id);
        if (isUserControlled) {
            return;
        }

        const deviceIndex = devices.findIndex(d => d.id === device.id);
        if (deviceIndex !== -1) {
            const newDevices = [...devices];
            const cachedMax = deviceMaxCache[device.id];

            let displayMax = cachedMax || (device.class === "ddc" ? device.max : 100);
            if (displayMax > 0 && !cachedMax) {
                const newCache = Object.assign({}, deviceMaxCache);
                newCache[device.id] = displayMax;
                deviceMaxCache = newCache;
            }

            newDevices[deviceIndex] = {
                "id": device.id,
                "name": device.id,
                "class": device.class,
                "current": device.current,
                "percentage": device.currentPercent,
                "max": device.max,
                "backend": device.backend,
                "displayMax": displayMax
            };
            devices = newDevices;
        }

        const isExponential = SessionData.getBrightnessExponential(device.id);
        const userSetValue = deviceBrightnessUserSet[device.id];

        let displayValue = device.currentPercent;
        if (isExponential) {
            if (userSetValue !== undefined) {
                const exponent = SessionData.getBrightnessExponent(device.id);
                const expectedHardware = Math.round(Math.pow(userSetValue / 100.0, exponent) * 100.0);
                if (Math.abs(device.currentPercent - expectedHardware) > 2) {
                    const newUserSet = Object.assign({}, deviceBrightnessUserSet);
                    delete newUserSet[device.id];
                    deviceBrightnessUserSet = newUserSet;
                    SessionData.clearBrightnessUserSetValue(device.id);
                    displayValue = linearToExponential(device.currentPercent, device.id);
                } else {
                    displayValue = userSetValue;
                }
            } else {
                displayValue = linearToExponential(device.currentPercent, device.id);
            }
        }

        const oldValue = deviceBrightness[device.id];
        const newBrightness = Object.assign({}, deviceBrightness);
        newBrightness[device.id] = displayValue;
        deviceBrightness = newBrightness;
        brightnessVersion++;

        const isPendingOsd = pendingOsdDevices[device.id] === true;
        if (isPendingOsd) {
            clearDevicePendingOsd(device.id);
            if (!suppressOsd) {
                brightnessChanged(true);
            }
            return;
        }

        if (!brightnessInitialized || oldValue === displayValue) {
            return;
        }
        if (suppressOsd) {
            return;
        }
        brightnessChanged(true);
    }

    function updateFromBrightnessState(state) {
        if (!state || !state.devices) {
            return;
        }

        const newMaxCache = Object.assign({}, deviceMaxCache);
        devices = state.devices.map(d => {
            const cachedMax = deviceMaxCache[d.id];
            let displayMax = cachedMax || (d.class === "ddc" ? d.max : 100);
            if (displayMax > 0 && !cachedMax) {
                newMaxCache[d.id] = displayMax;
            }
            return {
                "id": d.id,
                "name": d.id,
                "class": d.class,
                "current": d.current,
                "percentage": d.currentPercent,
                "max": d.max,
                "backend": d.backend,
                "displayMax": displayMax
            };
        });
        deviceMaxCache = newMaxCache;

        const newBrightness = {};
        let anyDeviceBrightnessChanged = false;

        for (const device of state.devices) {
            const isExponential = SessionData.getBrightnessExponential(device.id);
            const userSetValue = deviceBrightnessUserSet[device.id];
            const oldValue = deviceBrightness[device.id];

            if (isExponential) {
                if (userSetValue !== undefined) {
                    newBrightness[device.id] = userSetValue;
                } else {
                    newBrightness[device.id] = linearToExponential(device.currentPercent, device.id);
                }
            } else {
                newBrightness[device.id] = device.currentPercent;
            }

            const newValue = newBrightness[device.id];
            if (oldValue !== undefined && oldValue !== newValue) {
                anyDeviceBrightnessChanged = true;
            }
        }
        deviceBrightness = newBrightness;
        brightnessVersion++;

        brightnessAvailable = devices.length > 0;

        if (devices.length > 0 && !currentDevice) {
            const lastDevice = SessionData.lastBrightnessDevice || "";
            const deviceExists = devices.some(d => d.id === lastDevice);
            if (deviceExists) {
                setCurrentDevice(lastDevice, false);
            } else {
                const backlight = devices.find(d => d.class === "backlight");
                const nonKbdDevice = devices.find(d => !d.id.includes("kbd"));
                const defaultDevice = backlight || nonKbdDevice || devices[0];
                setCurrentDevice(defaultDevice.id, false);
            }
        }

        const shouldShowOsd = brightnessInitialized && anyDeviceBrightnessChanged;

        if (!brightnessInitialized) {
            brightnessInitialized = true;
        }

        if (shouldShowOsd) {
            brightnessChanged(true);
        }
    }

    function setBrightness(percentage, device, suppressOsd) {
        const actualDevice = device === "" ? getDefaultDevice() : (device || currentDevice || getDefaultDevice());
        if (!actualDevice) {
            console.warn("DisplayService: No device selected for brightness change");
            return;
        }

        if (actualDevice !== lastIpcDevice) {
            lastIpcDevice = actualDevice;
        }

        const deviceInfo = getCurrentDeviceInfoByName(actualDevice);
        const isExponential = SessionData.getBrightnessExponential(actualDevice);

        let minValue = 0;
        let maxValue = 100;

        switch (true) {
        case isExponential:
            minValue = 1;
            maxValue = 100;
            break;
        default:
            minValue = (deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc")) ? 1 : 0;
            maxValue = deviceInfo?.displayMax || 100;
            break;
        }

        if (maxValue <= 0) {
            console.warn("DisplayService: Invalid max value for device", actualDevice, "- skipping brightness change");
            return;
        }

        const clampedValue = Math.max(minValue, Math.min(maxValue, percentage));

        if (!DMSService.isConnected) {
            console.warn("DisplayService: Not connected to DMS");
            return;
        }

        const isLedDevice = deviceInfo?.class === "leds";

        if (suppressOsd) {
            markDeviceUserControlled(actualDevice);
        } else if (!isLedDevice) {
            markDevicePendingOsd(actualDevice);
        }

        const newBrightness = Object.assign({}, deviceBrightness);
        newBrightness[actualDevice] = clampedValue;
        deviceBrightness = newBrightness;
        brightnessVersion++;

        if (isLedDevice && !suppressOsd) {
            brightnessChanged(true);
        }

        if (isExponential) {
            const newUserSet = Object.assign({}, deviceBrightnessUserSet);
            newUserSet[actualDevice] = clampedValue;
            deviceBrightnessUserSet = newUserSet;
            SessionData.setBrightnessUserSetValue(actualDevice, clampedValue);
        }

        const params = {
            "device": actualDevice,
            "percent": clampedValue
        };
        if (isExponential) {
            params.exponential = true;
            params.exponent = SessionData.getBrightnessExponent(actualDevice);
        }

        DMSService.sendRequest("brightness.setBrightness", params, response => {
            if (response.error) {
                console.error("DisplayService: Failed to set brightness:", response.error);
                ToastService.showError(I18n.tr("Failed to set brightness"), response.error, "", "brightness");
            } else {
                ToastService.dismissCategory("brightness");
            }
        });
    }

    function setCurrentDevice(deviceName, saveToSession = false) {
        if (currentDevice === deviceName) {
            return;
        }

        currentDevice = deviceName;
        lastIpcDevice = deviceName;

        if (saveToSession) {
            SessionData.setLastBrightnessDevice(deviceName);
        }

        deviceSwitched();
    }

    function getDeviceBrightness(deviceName) {
        if (!deviceName) {
            return 50;
        }

        if (deviceName in deviceBrightness) {
            return deviceBrightness[deviceName];
        }

        return 50;
    }

    function linearToExponential(linearPercent, deviceName) {
        const exponent = SessionData.getBrightnessExponent(deviceName);
        const hardwarePercent = linearPercent / 100.0;
        const normalizedPercent = Math.pow(hardwarePercent, 1.0 / exponent);
        return Math.round(normalizedPercent * 100.0);
    }

    function getDefaultDevice() {
        for (const device of devices) {
            if (device.class === "backlight") {
                return device.id;
            }
        }
        return devices.length > 0 ? devices[0].id : "";
    }

    function getPinnedDeviceForFocusedScreen() {
        const focusedScreen = CompositorService.getFocusedScreen();
        if (!focusedScreen)
            return "";

        const pins = SettingsData.brightnessDevicePins || {};
        const screenKey = SettingsData.getScreenDisplayName(focusedScreen);
        if (!screenKey)
            return "";

        const pinnedDevice = pins[screenKey];
        if (!pinnedDevice)
            return "";

        const deviceExists = devices.some(d => d.id === pinnedDevice);
        if (!deviceExists)
            return "";

        return pinnedDevice;
    }

    function getPreferredDevice() {
        const pinned = getPinnedDeviceForFocusedScreen();
        if (pinned)
            return pinned;

        return getDefaultDevice();
    }

    function getCurrentDeviceInfo() {
        const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice);
        if (!deviceToUse) {
            return null;
        }

        for (const device of devices) {
            if (device.id === deviceToUse) {
                return device;
            }
        }
        return null;
    }

    function isCurrentDeviceReady() {
        const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice);
        return deviceToUse !== "";
    }

    function getCurrentDeviceInfoByName(deviceName) {
        if (!deviceName) {
            return null;
        }

        for (const device of devices) {
            if (device.id === deviceName) {
                return device;
            }
        }
        return null;
    }

    function getDeviceMax(deviceName) {
        const deviceInfo = getCurrentDeviceInfoByName(deviceName);
        if (!deviceInfo) {
            return 100;
        }
        return deviceInfo.displayMax || 100;
    }

    // Night Mode Functions - Simplified
    function enableNightMode() {
        if (!gammaControlAvailable) {
            ToastService.showWarning("Night mode failed: DMS gamma control not available");
            return;
        }

        nightModeEnabled = true;
        SessionData.setNightModeEnabled(true);

        DMSService.sendRequest("wayland.gamma.setEnabled", {
            "enabled": true
        }, response => {
            if (response.error) {
                console.error("DisplayService: Failed to enable gamma control:", response.error);
                ToastService.showError(I18n.tr("Failed to enable night mode"), response.error, "", "night-mode");
                nightModeEnabled = false;
                SessionData.setNightModeEnabled(false);
                return;
            }
            ToastService.dismissCategory("night-mode");

            if (SessionData.nightModeAutoEnabled) {
                startAutomation();
            } else {
                applyNightModeDirectly();
            }
        });
    }

    function disableNightMode() {
        nightModeEnabled = false;
        SessionData.setNightModeEnabled(false);

        if (!gammaControlAvailable) {
            return;
        }

        DMSService.sendRequest("wayland.gamma.setEnabled", {
            "enabled": false
        }, response => {
            if (response.error) {
                console.error("DisplayService: Failed to disable gamma control:", response.error);
                ToastService.showError(I18n.tr("Failed to disable night mode"), response.error, "", "night-mode");
            } else {
                ToastService.dismissCategory("night-mode");
            }
        });
    }

    function toggleNightMode() {
        if (nightModeEnabled) {
            disableNightMode();
        } else {
            enableNightMode();
        }
    }

    function applyNightModeDirectly() {
        const temperature = SessionData.nightModeTemperature || 4000;

        DMSService.sendRequest("wayland.gamma.setManualTimes", {
            "sunrise": null,
            "sunset": null
        }, response => {
            if (response.error) {
                console.error("DisplayService: Failed to clear manual times:", response.error);
                return;
            }

            DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
                "use": false
            }, response => {
                if (response.error) {
                    console.error("DisplayService: Failed to disable IP location:", response.error);
                    return;
                }

                DMSService.sendRequest("wayland.gamma.setTemperature", {
                    "low": temperature,
                    "high": temperature
                }, response => {
                    if (response.error) {
                        console.error("DisplayService: Failed to set temperature:", response.error);
                        ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
                    } else {
                        ToastService.dismissCategory("night-mode");
                    }
                });
            });
        });
    }

    function startAutomation() {
        if (!automationAvailable) {
            return;
        }

        const mode = SessionData.nightModeAutoMode || "time";

        switch (mode) {
        case "time":
            startTimeBasedMode();
            break;
        case "location":
            startLocationBasedMode();
            break;
        }
    }

    function startTimeBasedMode() {
        const temperature = SessionData.nightModeTemperature || 4000;
        const highTemp = SessionData.nightModeHighTemperature || 6500;
        const sunriseHour = SessionData.nightModeEndHour;
        const sunriseMinute = SessionData.nightModeEndMinute;
        const sunsetHour = SessionData.nightModeStartHour;
        const sunsetMinute = SessionData.nightModeStartMinute;

        const sunrise = `${String(sunriseHour).padStart(2, '0')}:${String(sunriseMinute).padStart(2, '0')}`;
        const sunset = `${String(sunsetHour).padStart(2, '0')}:${String(sunsetMinute).padStart(2, '0')}`;

        DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
            "use": false
        }, response => {
            if (response.error) {
                console.error("DisplayService: Failed to disable IP location:", response.error);
                return;
            }

            DMSService.sendRequest("wayland.gamma.setTemperature", {
                "low": temperature,
                "high": highTemp
            }, response => {
                if (response.error) {
                    console.error("DisplayService: Failed to set temperature:", response.error);
                    ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
                    return;
                }

                DMSService.sendRequest("wayland.gamma.setManualTimes", {
                    "sunrise": sunrise,
                    "sunset": sunset
                }, response => {
                    if (response.error) {
                        console.error("DisplayService: Failed to set manual times:", response.error);
                        ToastService.showError(I18n.tr("Failed to set night mode schedule"), response.error, "", "night-mode");
                    } else {
                        ToastService.dismissCategory("night-mode");
                    }
                });
            });
        });
    }

    function startLocationBasedMode() {
        const temperature = SessionData.nightModeTemperature || 4000;
        const highTemp = SessionData.nightModeHighTemperature || 6500;

        DMSService.sendRequest("wayland.gamma.setManualTimes", {
            "sunrise": null,
            "sunset": null
        }, response => {
            if (response.error) {
                console.error("DisplayService: Failed to clear manual times:", response.error);
                return;
            }

            DMSService.sendRequest("wayland.gamma.setTemperature", {
                "low": temperature,
                "high": highTemp
            }, response => {
                if (response.error) {
                    console.error("DisplayService: Failed to set temperature:", response.error);
                    ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
                    return;
                }

                if (SessionData.nightModeUseIPLocation) {
                    DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
                        "use": true
                    }, response => {
                        if (response.error) {
                            console.error("DisplayService: Failed to enable IP location:", response.error);
                            ToastService.showError(I18n.tr("Failed to enable IP location"), response.error, "", "night-mode");
                        } else {
                            ToastService.dismissCategory("night-mode");
                        }
                    });
                } else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
                    DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
                        "use": false
                    }, response => {
                        if (response.error) {
                            console.error("DisplayService: Failed to disable IP location:", response.error);
                            return;
                        }

                        DMSService.sendRequest("wayland.gamma.setLocation", {
                            "latitude": SessionData.latitude,
                            "longitude": SessionData.longitude
                        }, response => {
                            if (response.error) {
                                console.error("DisplayService: Failed to set location:", response.error);
                                ToastService.showError(I18n.tr("Failed to set night mode location"), response.error, "", "night-mode");
                            } else {
                                ToastService.dismissCategory("night-mode");
                            }
                        });
                    });
                } else {
                    console.warn("DisplayService: Location mode selected but no coordinates set and IP location disabled");
                }
            });
        });
    }

    function setNightModeAutomationMode(mode) {
        SessionData.setNightModeAutoMode(mode);
    }

    function evaluateNightMode() {
        if (!nightModeEnabled) {
            return;
        }

        if (SessionData.nightModeAutoEnabled) {
            restartTimer.nextAction = "automation";
            restartTimer.start();
        } else {
            restartTimer.nextAction = "direct";
            restartTimer.start();
        }
    }

    function checkGammaControlAvailability() {
        if (!DMSService.isConnected) {
            return;
        }

        if (DMSService.apiVersion < 6) {
            gammaControlAvailable = false;
            automationAvailable = false;
            return;
        }

        if (!DMSService.capabilities.includes("gamma")) {
            gammaControlAvailable = false;
            automationAvailable = false;
            return;
        }

        DMSService.sendRequest("wayland.gamma.getState", null, response => {
            if (response.error) {
                gammaControlAvailable = false;
                automationAvailable = false;
                console.error("DisplayService: Gamma control not available:", response.error);
            } else {
                gammaControlAvailable = true;
                automationAvailable = true;

                if (nightModeEnabled) {
                    DMSService.sendRequest("wayland.gamma.setEnabled", {
                        "enabled": true
                    }, enableResponse => {
                        if (enableResponse.error) {
                            console.error("DisplayService: Failed to enable gamma control on startup:", enableResponse.error);
                            return;
                        }

                        evaluateNightMode();
                    });
                }
            }
        });
    }

    Timer {
        id: restartTimer
        property string nextAction: ""
        interval: 250
        repeat: false

        onTriggered: {
            if (nextAction === "automation") {
                startAutomation();
            } else if (nextAction === "direct") {
                applyNightModeDirectly();
            }
            nextAction = "";
        }
    }

    function rescanDevices() {
        if (!DMSService.isConnected) {
            return;
        }

        DMSService.sendRequest("brightness.rescan", null, response => {
            if (response.error) {
                console.error("DisplayService: Failed to rescan brightness devices:", response.error);
            }
        });
    }

    function updateDeviceBrightnessDisplay(deviceName) {
        brightnessVersion++;
        brightnessChanged();
    }

    Timer {
        id: osdSuppressTimer
        interval: 2000
        running: true
        onTriggered: suppressOsd = false
    }

    Component.onCompleted: {
        nightModeEnabled = SessionData.nightModeEnabled;
        deviceBrightnessUserSet = Object.assign({}, SessionData.brightnessUserSetValues);
        if (DMSService.isConnected) {
            checkGammaControlAvailability();
        }
    }

    Timer {
        id: screenChangeRescanTimer
        property int rescanAttempt: 0
        interval: 3000
        repeat: false
        onTriggered: {
            rescanDevices();
            rescanAttempt++;
            if (rescanAttempt < 3) {
                interval = rescanAttempt === 1 ? 5000 : 8000;
                restart();
            } else {
                rescanAttempt = 0;
                interval = 3000;
            }
        }
    }

    Connections {
        target: Quickshell

        function onScreensChanged() {
            screenChangeRescanTimer.rescanAttempt = 0;
            screenChangeRescanTimer.interval = 3000;
            screenChangeRescanTimer.restart();
        }
    }

    Connections {
        target: DMSService

        function onConnectionStateChanged() {
            if (DMSService.isConnected) {
                checkGammaControlAvailability();
            } else {
                brightnessAvailable = false;
                gammaControlAvailable = false;
                automationAvailable = false;
            }
        }

        function onCapabilitiesReceived() {
            checkGammaControlAvailability();
        }

        function onBrightnessStateUpdate(data) {
            updateFromBrightnessState(data);
        }

        function onBrightnessDeviceUpdate(device) {
            updateSingleDevice(device);
        }

        function onLoginctlEvent(event) {
            if (event.event === "unlock" || event.event === "resume") {
                suppressOsd = true;
                osdSuppressTimer.restart();
                evaluateNightMode();
            }
        }

        function onGammaStateUpdate(data) {
            root.gammaState = data;
        }
    }

    // Session Data Connections
    Connections {
        target: SessionData

        function onNightModeEnabledChanged() {
            nightModeEnabled = SessionData.nightModeEnabled;
            evaluateNightMode();
        }

        function onNightModeAutoEnabledChanged() {
            evaluateNightMode();
        }
        function onNightModeAutoModeChanged() {
            evaluateNightMode();
        }
        function onNightModeStartHourChanged() {
            evaluateNightMode();
        }
        function onNightModeStartMinuteChanged() {
            evaluateNightMode();
        }
        function onNightModeEndHourChanged() {
            evaluateNightMode();
        }
        function onNightModeEndMinuteChanged() {
            evaluateNightMode();
        }
        function onNightModeTemperatureChanged() {
            evaluateNightMode();
        }
        function onNightModeHighTemperatureChanged() {
            evaluateNightMode();
        }
        function onLatitudeChanged() {
            evaluateNightMode();
        }
        function onLongitudeChanged() {
            evaluateNightMode();
        }
        function onNightModeUseIPLocationChanged() {
            evaluateNightMode();
        }
    }

    // IPC Handler for external control
    IpcHandler {
        function set(percentage: string, device: string): string {
            if (!root.brightnessAvailable)
                return "Brightness control not available";

            const value = parseInt(percentage);
            if (isNaN(value))
                return "Invalid brightness value: " + percentage;

            const actualDevice = device || root.getPreferredDevice();

            if (actualDevice && !root.devices.some(d => d.id === actualDevice))
                return "Device not found: " + actualDevice;

            const deviceInfo = actualDevice ? root.getCurrentDeviceInfoByName(actualDevice) : null;
            const minValue = (deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc")) ? 1 : 0;
            const clampedValue = Math.max(minValue, Math.min(100, value));

            root.lastIpcDevice = actualDevice;
            if (actualDevice && actualDevice !== root.currentDevice)
                root.setCurrentDevice(actualDevice, false);

            root.setBrightness(clampedValue, actualDevice);

            return actualDevice ? "Brightness set to " + clampedValue + "% on " + actualDevice : "Brightness set to " + clampedValue + "%";
        }

        function increment(step: string, device: string): string {
            if (!root.brightnessAvailable)
                return "Brightness control not available";

            const actualDevice = device || root.getPreferredDevice();

            if (actualDevice && !root.devices.some(d => d.id === actualDevice))
                return "Device not found: " + actualDevice;

            const stepValue = parseInt(step || "5");

            root.lastIpcDevice = actualDevice;
            if (actualDevice && actualDevice !== root.currentDevice)
                root.setCurrentDevice(actualDevice, false);

            const isExponential = SessionData.getBrightnessExponential(actualDevice);
            const currentBrightness = root.getDeviceBrightness(actualDevice);
            const deviceInfo = root.getCurrentDeviceInfoByName(actualDevice);

            const maxValue = isExponential ? 100 : (deviceInfo?.displayMax || 100);
            const newBrightness = Math.min(maxValue, currentBrightness + stepValue);

            root.setBrightness(newBrightness, actualDevice);

            return "Brightness increased by " + stepValue + "%" + (device ? " on " + actualDevice : "");
        }

        function decrement(step: string, device: string): string {
            if (!root.brightnessAvailable)
                return "Brightness control not available";

            const actualDevice = device || root.getPreferredDevice();

            if (actualDevice && !root.devices.some(d => d.id === actualDevice))
                return "Device not found: " + actualDevice;

            const stepValue = parseInt(step || "5");

            root.lastIpcDevice = actualDevice;
            if (actualDevice && actualDevice !== root.currentDevice)
                root.setCurrentDevice(actualDevice, false);

            const isExponential = SessionData.getBrightnessExponential(actualDevice);
            const currentBrightness = root.getDeviceBrightness(actualDevice);
            const deviceInfo = root.getCurrentDeviceInfoByName(actualDevice);

            let minValue = 0;
            switch (true) {
            case isExponential:
                minValue = 1;
                break;
            case deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc"):
                minValue = 1;
                break;
            default:
                minValue = 0;
                break;
            }

            const newBrightness = Math.max(minValue, currentBrightness - stepValue);

            root.setBrightness(newBrightness, actualDevice);

            return "Brightness decreased by " + stepValue + "%" + (device ? " on " + actualDevice : "");
        }

        function status(): string {
            if (!root.brightnessAvailable) {
                return "Brightness control not available";
            }

            return "Device: " + root.currentDevice + " - Brightness: " + root.brightnessLevel + "%";
        }

        function list(): string {
            if (!root.brightnessAvailable) {
                return "No brightness devices available";
            }

            let result = "Available devices:\n";
            for (const device of root.devices) {
                const isExp = SessionData.getBrightnessExponential(device.id);
                result += device.id + " (" + device.class + ")" + (isExp ? " [exponential]" : "") + "\n";
            }
            return result;
        }

        function enableExponential(device: string): string {
            const targetDevice = device || root.currentDevice;
            if (!targetDevice) {
                return "No device specified";
            }

            if (!root.devices.some(d => d.id === targetDevice)) {
                return "Device not found: " + targetDevice;
            }

            SessionData.setBrightnessExponential(targetDevice, true);
            return "Exponential mode enabled for " + targetDevice;
        }

        function disableExponential(device: string): string {
            const targetDevice = device || root.currentDevice;
            if (!targetDevice) {
                return "No device specified";
            }

            if (!root.devices.some(d => d.id === targetDevice)) {
                return "Device not found: " + targetDevice;
            }

            SessionData.setBrightnessExponential(targetDevice, false);
            return "Exponential mode disabled for " + targetDevice;
        }

        function toggleExponential(device: string): string {
            const targetDevice = device || root.currentDevice;
            if (!targetDevice) {
                return "No device specified";
            }

            if (!root.devices.some(d => d.id === targetDevice)) {
                return "Device not found: " + targetDevice;
            }

            const currentState = SessionData.getBrightnessExponential(targetDevice);
            SessionData.setBrightnessExponential(targetDevice, !currentState);
            return "Exponential mode " + (!currentState ? "enabled" : "disabled") + " for " + targetDevice;
        }

        target: "brightness"
    }

    IpcHandler {
        function toggle(): string {
            root.toggleNightMode();
            return root.nightModeEnabled ? "Night mode enabled" : "Night mode disabled";
        }

        function enable(): string {
            root.enableNightMode();
            return "Night mode enabled";
        }

        function disable(): string {
            root.disableNightMode();
            return "Night mode disabled";
        }

        function status(): string {
            if (!root.gammaControlAvailable)
                return "Night mode: unavailable (no gamma control)";

            const parts = ["Night mode: " + (root.nightModeEnabled ? "enabled" : "disabled")];

            if (root.gammaCurrentTemp > 0)
                parts.push("Current temperature: " + root.gammaCurrentTemp + "K");

            parts.push("Target night temperature: " + SessionData.nightModeTemperature + "K");

            if (SessionData.nightModeAutoEnabled) {
                parts.push("Target day temperature: " + SessionData.nightModeHighTemperature + "K");
                parts.push("Automation: " + SessionData.nightModeAutoMode);
                parts.push("Period: " + (root.gammaIsDay ? "day" : "night"));

                if (root.gammaNextTransition)
                    parts.push("Next transition: " + root.gammaNextTransition);
                if (root.gammaSunriseTime)
                    parts.push("Sunrise: " + root.gammaSunriseTime);
                if (root.gammaSunsetTime)
                    parts.push("Sunset: " + root.gammaSunsetTime);
            }

            return parts.join("\n");
        }

        function getCurrentTemp(): string {
            if (!root.gammaControlAvailable)
                return "Gamma control not available";
            if (root.gammaCurrentTemp <= 0)
                return "No current temperature reported";
            return root.gammaCurrentTemp.toString();
        }

        function getTargetTemp(): string {
            return SessionData.nightModeTemperature.toString();
        }

        function getDayTemp(): string {
            return SessionData.nightModeHighTemperature.toString();
        }

        function setTargetTemp(value: string): string {
            if (!value)
                return "Usage: night setTargetTemp <2500-6000>";

            const temp = parseInt(value);
            if (isNaN(temp))
                return "Invalid temperature: " + value;
            if (temp < 2500 || temp > 6000)
                return "Temperature must be between 2500K and 6000K";

            const rounded = Math.round(temp / 500) * 500;
            SessionData.setNightModeTemperature(rounded);

            if (root.nightModeEnabled) {
                switch (true) {
                case SessionData.nightModeAutoEnabled:
                    root.startAutomation();
                    break;
                default:
                    root.applyNightModeDirectly();
                    break;
                }
            }

            if (rounded !== temp)
                return "Night temperature set to " + rounded + "K (rounded from " + temp + "K)";
            return "Night temperature set to " + rounded + "K";
        }

        function setDayTemp(value: string): string {
            if (!value)
                return "Usage: night setDayTemp <2500-6500>";

            const temp = parseInt(value);
            if (isNaN(temp))
                return "Invalid temperature: " + value;
            if (temp < 2500 || temp > 6500)
                return "Temperature must be between 2500K and 6500K";

            const rounded = Math.round(temp / 500) * 500;
            SessionData.setNightModeHighTemperature(rounded);

            if (root.nightModeEnabled && SessionData.nightModeAutoEnabled)
                root.startAutomation();

            if (rounded !== temp)
                return "Day temperature set to " + rounded + "K (rounded from " + temp + "K)";
            return "Day temperature set to " + rounded + "K";
        }

        function getSchedule(): string {
            if (!SessionData.nightModeAutoEnabled)
                return "Automation disabled";

            const parts = ["Mode: " + SessionData.nightModeAutoMode];
            parts.push("Period: " + (root.gammaIsDay ? "day" : "night"));

            if (root.gammaDawnTime)
                parts.push("Dawn: " + root.gammaDawnTime);
            if (root.gammaSunriseTime)
                parts.push("Sunrise: " + root.gammaSunriseTime);
            if (root.gammaSunsetTime)
                parts.push("Sunset: " + root.gammaSunsetTime);
            if (root.gammaNightTime)
                parts.push("Night: " + root.gammaNightTime);
            if (root.gammaNextTransition)
                parts.push("Next transition: " + root.gammaNextTransition);
            if (root.gammaSunPosition > 0)
                parts.push("Sun position: " + root.gammaSunPosition.toFixed(2) + "°");

            return parts.join("\n");
        }

        target: "night"
    }
}
