pragma Singleton
pragma ComponentBehavior: Bound

import QtCore
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.Common

Singleton {
    id: root

    readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
    readonly property string hyprDmsDir: configDir + "/hypr/dms"
    readonly property string outputsPath: hyprDmsDir + "/outputs.conf"
    readonly property string layoutPath: hyprDmsDir + "/layout.conf"
    readonly property string cursorPath: hyprDmsDir + "/cursor.conf"
    readonly property string windowrulesPath: hyprDmsDir + "/windowrules.conf"

    property int _lastGapValue: -1

    Component.onCompleted: {
        if (CompositorService.isHyprland) {
            Qt.callLater(generateLayoutConfig);
            ensureWindowrulesConfig();
        }
    }

    function ensureWindowrulesConfig() {
        Proc.runCommand("hypr-ensure-windowrules", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && [ ! -f "${windowrulesPath}" ] && touch "${windowrulesPath}" || true`], (output, exitCode) => {
            if (exitCode !== 0)
                console.warn("HyprlandService: Failed to ensure windowrules.conf:", output);
        });
    }

    Connections {
        target: SettingsData
        function onBarConfigsChanged() {
            if (!CompositorService.isHyprland)
                return;
            const newGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
            if (newGaps === root._lastGapValue)
                return;
            root._lastGapValue = newGaps;
            generateLayoutConfig();
        }
    }

    Connections {
        target: CompositorService
        function onIsHyprlandChanged() {
            if (CompositorService.isHyprland)
                generateLayoutConfig();
        }
    }

    function getOutputIdentifier(output, outputName) {
        if (SettingsData.displayNameMode === "model" && output.make && output.model)
            return "desc:" + output.make + " " + output.model + " " + (output.serial || "Unknown");
        return outputName;
    }

    function generateOutputsConfig(outputsData, hyprlandSettings) {
        if (!outputsData || Object.keys(outputsData).length === 0)
            return;

        const settings = hyprlandSettings || SettingsData.hyprlandOutputSettings;
        let lines = ["# Auto-generated by DMS - do not edit manually", ""];
        let monitorv2Blocks = [];

        for (const outputName in outputsData) {
            const output = outputsData[outputName];
            if (!output)
                continue;

            const identifier = getOutputIdentifier(output, outputName);
            const outputSettings = settings[identifier] || {};

            if (outputSettings.disabled) {
                lines.push("monitor = " + identifier + ", disable");
                continue;
            }

            let resolution = "preferred";
            if (output.modes && output.current_mode !== undefined) {
                const mode = output.modes[output.current_mode];
                if (mode)
                    resolution = mode.width + "x" + mode.height + "@" + (mode.refresh_rate / 1000).toFixed(3);
            }

            const x = output.logical?.x ?? 0;
            const y = output.logical?.y ?? 0;
            const position = x + "x" + y;
            const scale = output.logical?.scale ?? 1.0;

            let monitorLine = "monitor = " + identifier + ", " + resolution + ", " + position + ", " + scale;

            const transform = transformToHyprland(output.logical?.transform ?? "Normal");
            if (transform !== 0)
                monitorLine += ", transform, " + transform;

            if (output.vrr_supported) {
                const vrrMode = outputSettings.vrrFullscreenOnly ? 2 : (output.vrr_enabled ? 1 : 0);
                monitorLine += ", vrr, " + vrrMode;
            }

            if (output.mirror && output.mirror.length > 0)
                monitorLine += ", mirror, " + output.mirror;

            if (outputSettings.bitdepth && outputSettings.bitdepth !== 8)
                monitorLine += ", bitdepth, " + outputSettings.bitdepth;

            if (outputSettings.colorManagement && outputSettings.colorManagement !== "auto")
                monitorLine += ", cm, " + outputSettings.colorManagement;

            if (outputSettings.sdrBrightness !== undefined && outputSettings.sdrBrightness !== 1.0)
                monitorLine += ", sdrbrightness, " + outputSettings.sdrBrightness;

            if (outputSettings.sdrSaturation !== undefined && outputSettings.sdrSaturation !== 1.0)
                monitorLine += ", sdrsaturation, " + outputSettings.sdrSaturation;

            lines.push(monitorLine);

            const needsMonitorv2 = outputSettings.supportsHdr || outputSettings.supportsWideColor || outputSettings.sdrMinLuminance !== undefined || outputSettings.sdrMaxLuminance !== undefined || outputSettings.minLuminance !== undefined || outputSettings.maxLuminance !== undefined || outputSettings.maxAvgLuminance !== undefined;

            if (needsMonitorv2) {
                let block = "monitorv2 {\n";
                block += "    output = " + identifier + "\n";

                if (outputSettings.supportsWideColor)
                    block += "    supports_wide_color = true\n";
                if (outputSettings.supportsHdr)
                    block += "    supports_hdr = true\n";
                if (outputSettings.sdrMinLuminance !== undefined)
                    block += "    sdr_min_luminance = " + outputSettings.sdrMinLuminance + "\n";
                if (outputSettings.sdrMaxLuminance !== undefined)
                    block += "    sdr_max_luminance = " + outputSettings.sdrMaxLuminance + "\n";
                if (outputSettings.minLuminance !== undefined)
                    block += "    min_luminance = " + outputSettings.minLuminance + "\n";
                if (outputSettings.maxLuminance !== undefined)
                    block += "    max_luminance = " + outputSettings.maxLuminance + "\n";
                if (outputSettings.maxAvgLuminance !== undefined)
                    block += "    max_avg_luminance = " + outputSettings.maxAvgLuminance + "\n";

                block += "}";
                monitorv2Blocks.push(block);
            }
        }

        if (monitorv2Blocks.length > 0) {
            lines.push("");
            for (const block of monitorv2Blocks)
                lines.push(block);
        }

        lines.push("");

        const content = lines.join("\n");

        Proc.runCommand("hypr-write-outputs", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
            if (exitCode !== 0) {
                console.warn("HyprlandService: Failed to write outputs config:", output);
                return;
            }
            console.info("HyprlandService: Generated outputs config at", outputsPath);
            if (CompositorService.isHyprland)
                reloadConfig();
        });
    }

    function reloadConfig() {
        Proc.runCommand("hyprctl-reload", ["hyprctl", "reload"], (output, exitCode) => {
            if (exitCode !== 0)
                console.warn("HyprlandService: hyprctl reload failed:", output);
        });
    }

    function generateLayoutConfig() {
        if (!CompositorService.isHyprland)
            return;

        const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
        const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
        const defaultBorderSize = 2;

        const cornerRadius = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutRadiusOverride >= 0) ? SettingsData.hyprlandLayoutRadiusOverride : defaultRadius;
        const gaps = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutGapsOverride >= 0) ? SettingsData.hyprlandLayoutGapsOverride : defaultGaps;
        const borderSize = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutBorderSize >= 0) ? SettingsData.hyprlandLayoutBorderSize : defaultBorderSize;

        let content = `# Auto-generated by DMS - do not edit manually

general {
    gaps_in = ${gaps}
    gaps_out = ${gaps}
    border_size = ${borderSize}
}

decoration {
    rounding = ${cornerRadius}
}
`;

        Proc.runCommand("hypr-write-layout", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
            if (exitCode !== 0) {
                console.warn("HyprlandService: Failed to write layout config:", output);
                return;
            }
            console.info("HyprlandService: Generated layout config at", layoutPath);
            reloadConfig();
        });
    }

    function transformToHyprland(transform) {
        switch (transform) {
        case "Normal":
            return 0;
        case "90":
            return 1;
        case "180":
            return 2;
        case "270":
            return 3;
        case "Flipped":
            return 4;
        case "Flipped90":
            return 5;
        case "Flipped180":
            return 6;
        case "Flipped270":
            return 7;
        default:
            return 0;
        }
    }

    function hyprlandToTransform(value) {
        switch (value) {
        case 0:
            return "Normal";
        case 1:
            return "90";
        case 2:
            return "180";
        case 3:
            return "270";
        case 4:
            return "Flipped";
        case 5:
            return "Flipped90";
        case 6:
            return "Flipped180";
        case 7:
            return "Flipped270";
        default:
            return "Normal";
        }
    }

    function generateCursorConfig() {
        if (!CompositorService.isHyprland)
            return;

        const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null;
        if (!settings) {
            Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
                if (exitCode !== 0)
                    console.warn("HyprlandService: Failed to write cursor config:", output);
            });
            return;
        }

        const themeName = settings.theme === "System Default" ? (SettingsData.systemDefaultCursorTheme || "") : settings.theme;
        const size = settings.size || 24;
        const hideOnKeyPress = settings.hyprland?.hideOnKeyPress || false;
        const hideOnTouch = settings.hyprland?.hideOnTouch || false;
        const inactiveTimeout = settings.hyprland?.inactiveTimeout || 0;

        const hasTheme = themeName && themeName.length > 0;
        const hasNonDefaultSize = size !== 24;
        const hasCursorSettings = hideOnKeyPress || hideOnTouch || inactiveTimeout > 0;

        if (!hasTheme && !hasNonDefaultSize && !hasCursorSettings) {
            Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
                if (exitCode !== 0)
                    console.warn("HyprlandService: Failed to write cursor config:", output);
            });
            return;
        }

        let lines = ["# Auto-generated by DMS - do not edit manually", ""];

        if (hasTheme) {
            lines.push(`env = HYPRCURSOR_THEME,${themeName}`);
            lines.push(`env = XCURSOR_THEME,${themeName}`);
        }
        lines.push(`env = HYPRCURSOR_SIZE,${size}`);
        lines.push(`env = XCURSOR_SIZE,${size}`);

        if (hasCursorSettings) {
            lines.push("");
            lines.push("cursor {");
            if (hideOnKeyPress)
                lines.push("    hide_on_key_press = true");
            if (hideOnTouch)
                lines.push("    hide_on_touch = true");
            if (inactiveTimeout > 0)
                lines.push(`    inactive_timeout = ${inactiveTimeout}`);
            lines.push("}");
        }

        lines.push("");
        const content = lines.join("\n");

        Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${cursorPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
            if (exitCode !== 0) {
                console.warn("HyprlandService: Failed to write cursor config:", output);
                return;
            }
            if (hasTheme)
                Proc.runCommand("hyprctl-setcursor", ["hyprctl", "setcursor", themeName, String(size)], () => {});
            reloadConfig();
        });
    }

    function renameWorkspace(newName) {
        if (!Hyprland.focusedWorkspace)
            return;
        const wsId = Hyprland.focusedWorkspace.id;
        if (!wsId)
            return;
        const fullName = wsId + " " + newName;
        Proc.runCommand("hyprland-rename-ws", ["hyprctl", "dispatch", "renameworkspace", String(wsId), fullName], (output, exitCode) => {
            if (exitCode !== 0) {
                console.warn("HyprlandService: Failed to rename workspace:", output);
            }
        });
    }
}
