#include "HyprCtl.hpp"
#include "output/Monitor.hpp"

#include <algorithm>
#include <format>
#include <fstream>
#include <iterator>
#include <netinet/in.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/poll.h>
#include <filesystem>
#include <ranges>
#include <sys/eventfd.h>

#include <sstream>
#include <string>
#include <typeindex>
#include <numeric>

#include <hyprutils/string/String.hpp>
#include <hyprutils/string/VarList.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
#include <aquamarine/input/Input.hpp>

#include "../config/shared/complex/ComplexDataTypes.hpp"
#include "../config/legacy/ConfigManager.hpp"
#include "../config/lua/ConfigManager.hpp"
#include "../config/ConfigValue.hpp"
#include "../config/shared/parserUtils/ParserUtils.hpp"
#include "../config/shared/complex/ComplexDataTypes.hpp"
#include "../config/shared/inotify/ConfigWatcher.hpp"
#include "../config/shared/workspace/WorkspaceRuleManager.hpp"
#include "../config/shared/monitor/MonitorRuleManager.hpp"
#include "../config/shared/animation/AnimationTree.hpp"
#include "../config/supplementary/jeremy/Jeremy.hpp"
#include "../config/values/ConfigValues.hpp"
#include "../pointer/cursor/CursorManager.hpp"
#include "../errorOverlay/Overlay.hpp"
#include "../devices/IPointer.hpp"
#include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "../debug/log/RollingLogFollow.hpp"
#include "../config/ConfigManager.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../helpers/SystemInfo.hpp"
#include "../desktop/view/LayerSurface.hpp"
#include "../desktop/view/Group.hpp"
#include "../desktop/rule/Engine.hpp"
#include "../desktop/history/WindowHistoryTracker.hpp"
#include "../desktop/state/FocusState.hpp"
#include "../state/MonitorState.hpp"
#include "../state/WorkspacePlacementController.hpp"
#include "../state/WorkspaceState.hpp"
#include "../version.h"

#include "../Compositor.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../managers/fullscreen/FullscreenController.hpp"
#include "../plugins/PluginSystem.hpp"
#include "../animation/AnimationManager.hpp"
#include "../notification/NotificationOverlay.hpp"
#include "../render/Renderer.hpp"
#include "../render/OpenGL.hpp"
#include "../layout/space/Space.hpp"
#include "../layout/algorithm/Algorithm.hpp"
#include "../layout/algorithm/TiledAlgorithm.hpp"
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"

using namespace Render::GL;

#if defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/ucred.h>
#define CRED_T   xucred
#define CRED_LVL SOL_LOCAL
#define CRED_OPT LOCAL_PEERCRED
#define CRED_PID cr_pid
#elif defined(__NetBSD__)
#define CRED_T   unpcbid
#define CRED_LVL SOL_LOCAL
#define CRED_OPT LOCAL_PEEREID
#define CRED_PID unp_pid
#else
#if defined(__OpenBSD__)
#define CRED_T sockpeercred
#else
#define CRED_T ucred
#endif
#define CRED_LVL SOL_SOCKET
#define CRED_OPT SO_PEERCRED
#define CRED_PID pid
#endif

static void trimTrailingComma(std::string& str) {
    if (!str.empty() && str.back() == ',')
        str.pop_back();
}

static std::string formatToString(uint32_t drmFormat) {
    switch (drmFormat) {
        case DRM_FORMAT_XRGB2101010: return "XRGB2101010";
        case DRM_FORMAT_XBGR2101010: return "XBGR2101010";
        case DRM_FORMAT_XRGB8888: return "XRGB8888";
        case DRM_FORMAT_XBGR8888: return "XBGR8888";
        default: break;
    }

    return "Invalid";
}

static std::string availableModesForOutput(PHLMONITOR pMonitor, eHyprCtlOutputFormat format) {
    std::string result;

    for (auto const& m : pMonitor->m_output->modes) {
        if (format == FORMAT_NORMAL)
            result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
        else
            result += std::format("\"{}x{}@{:.2f}Hz\",", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
    }

    trimTrailingComma(result);

    return result;
}

const std::array<const char*, Monitor::CMonitor::SC_CHECKS_COUNT> SOLITARY_REASONS_JSON = {
    "\"UNKNOWN\"",   "\"NOTIFICATION\"", "\"LOCK\"",      "\"WORKSPACE\"", "\"WINDOWED\"", "\"DND\"",        "\"SPECIAL\"",  "\"ALPHA\"",       "\"OFFSET\"",
    "\"CANDIDATE\"", "\"OPAQUE\"",       "\"TRANSFORM\"", "\"OVERLAYS\"",  "\"FLOAT\"",    "\"WORKSPACES\"", "\"SURFACES\"", "\"CONFIGERROR\"", "\"FADEOUT\"",
};

const std::array<const char*, Monitor::CMonitor::SC_CHECKS_COUNT> SOLITARY_REASONS_TEXT = {
    "unknown reason",    "notification",     "session lock",     "invalid workspace", "windowed mode", "dnd active",
    "special workspace", "alpha channel",    "workspace offset", "missing candidate", "not opaque",    "surface transformations",
    "other overlays",    "floating windows", "other workspaces", "subsurfaces",       "config error",  "fadeout in progress",
};

std::string CHyprCtl::getSolitaryBlockedReason(PHLMONITOR m, eHyprCtlOutputFormat format) {
    const auto reasons = m->isSolitaryBlocked(true);
    if (!reasons)
        return "null";

    std::string reasonStr = "";
    const auto  TEXTS     = format == eHyprCtlOutputFormat::FORMAT_JSON ? SOLITARY_REASONS_JSON : SOLITARY_REASONS_TEXT;

    for (uint32_t i = 0; i < Monitor::CMonitor::SC_CHECKS_COUNT; i++) {
        if (reasons & (1 << i)) {
            if (reasonStr != "")
                reasonStr += ",";
            reasonStr += TEXTS[i];
        }
    }

    return format == eHyprCtlOutputFormat::FORMAT_JSON ? "[" + reasonStr + "]" : reasonStr;
}

const std::array<const char*, Monitor::CMonitor::DS_CHECKS_COUNT> DS_REASONS_JSON = {
    "\"UNKNOWN\"",   "\"USER\"",    "\"WINDOWED\"",  "\"CONTENT\"", "\"MIRROR\"", "\"RECORD\"", "\"SW\"",
    "\"CANDIDATE\"", "\"SURFACE\"", "\"TRANSFORM\"", "\"DMA\"",     "\"FAILED\"", "\"CM\"",
};

const std::array<const char*, Monitor::CMonitor::DS_CHECKS_COUNT> DS_REASONS_TEXT = {
    "unknown reason",    "user settings",   "windowed mode",           "content type",   "monitor mirrors",   "screen record/screenshot", "software renders/cursors",
    "missing candidate", "invalid surface", "surface transformations", "invalid buffer", "activation failed", "color management",
};

std::string CHyprCtl::getDSBlockedReason(PHLMONITOR m, eHyprCtlOutputFormat format) {
    const auto reasons = m->isDSBlocked(true);
    if (!reasons)
        return "null";

    std::string reasonStr = "";
    const auto  TEXTS     = format == eHyprCtlOutputFormat::FORMAT_JSON ? DS_REASONS_JSON : DS_REASONS_TEXT;

    for (int i = 0; i < Monitor::CMonitor::DS_CHECKS_COUNT; i++) {
        if (reasons & (1 << i)) {
            if (reasonStr != "")
                reasonStr += ",";
            reasonStr += TEXTS[i];
        }
    }

    return format == eHyprCtlOutputFormat::FORMAT_JSON ? "[" + reasonStr + "]" : reasonStr;
}

const std::array<const char*, Monitor::CMonitor::TC_CHECKS_COUNT> TEARING_REASONS_JSON = {
    "\"UNKNOWN\"", "\"NOT_TORN\"", "\"USER\"", "\"ZOOM\"", "\"SUPPORT\"", "\"CANDIDATE\"", "\"WINDOW\"", "\"HW_CURSOR\"",
};

const std::array<const char*, Monitor::CMonitor::TC_CHECKS_COUNT> TEARING_REASONS_TEXT = {"unknown reason",           "next frame is not torn", "user settings",   "zoom",
                                                                                          "not supported by monitor", "missing candidate",      "window settings", "hw cursor"};

std::string                                                       CHyprCtl::getTearingBlockedReason(PHLMONITOR m, eHyprCtlOutputFormat format) {
    const auto reasons = m->isTearingBlocked(true);
    if (!reasons || (reasons == Monitor::CMonitor::TC_NOT_TORN && m->m_tearingState.activelyTearing))
        return "null";

    std::string reasonStr = "";
    const auto  TEXTS     = format == eHyprCtlOutputFormat::FORMAT_JSON ? TEARING_REASONS_JSON : TEARING_REASONS_TEXT;

    for (int i = 0; i < Monitor::CMonitor::TC_CHECKS_COUNT; i++) {
        if (reasons & (1 << i)) {
            if (reasonStr != "")
                reasonStr += ",";
            reasonStr += TEXTS[i];
        }
    }

    return format == eHyprCtlOutputFormat::FORMAT_JSON ? "[" + reasonStr + "]" : reasonStr;
}

std::string CHyprCtl::getMonitorData(PHLMONITOR m, eHyprCtlOutputFormat format) {
    std::string result;
    if (!m->m_output || m->m_id == -1)
        return "";

    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {

        result += std::format(
            R"#({{
    "id": {},
    "name": "{}",
    "description": "{}",
    "make": "{}",
    "model": "{}",
    "serial": "{}",
    "width": {},
    "height": {},
    "physicalWidth": {},
    "physicalHeight": {},
    "refreshRate": {:.5f},
    "x": {},
    "y": {},
    "activeWorkspace": {{
        "id": {},
        "name": "{}"
    }},
    "specialWorkspace": {{
        "id": {},
        "name": "{}"
    }},
    "reserved": [{}, {}, {}, {}],
    "scale": {:.2f},
    "transform": {},
    "focused": {},
    "dpmsStatus": {},
    "vrr": {},
    "solitary": "{:x}",
    "solitaryBlockedBy": {},
    "activelyTearing": {},
    "tearingBlockedBy": {},
    "directScanoutTo": "{:x}",
    "directScanoutBlockedBy": {},
    "disabled": {},
    "currentFormat": "{}",
    "mirrorOf": "{}",
    "availableModes": [{}],
    "colorManagementPreset": "{}",
    "sdrBrightness": {:.2f},
    "sdrSaturation": {:.2f},
    "sdrMinLuminance": {:.2f},
    "sdrMaxLuminance": {},
    "hardwareCursorsInUse": {}
}},)#",

            m->m_id, escapeJSONStrings(m->m_name), escapeJSONStrings(m->m_shortDescription), escapeJSONStrings(m->m_output->make), escapeJSONStrings(m->m_output->model),
            escapeJSONStrings(m->m_output->serial), sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), sc<int>(m->m_output->physicalSize.x),
            sc<int>(m->m_output->physicalSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->activeWorkspaceID(),
            (!m->m_activeWorkspace ? "" : escapeJSONStrings(m->m_activeWorkspace->m_name)), m->activeSpecialWorkspaceID(),
            escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc<int>(m->m_reservedArea.left()), sc<int>(m->m_reservedArea.top()),
            sc<int>(m->m_reservedArea.right()), sc<int>(m->m_reservedArea.bottom()), m->m_scale, sc<int>(m->m_transform),
            (m == Desktop::focusState()->monitor() ? "true" : "false"), (m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"),
            rc<uint64_t>(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), (m->m_tearingState.activelyTearing ? "true" : "false"),
            getTearingBlockedReason(m, format), rc<uint64_t>(m->m_lastScanout.get()), getDSBlockedReason(m, format), (m->m_enabled ? "false" : "true"),
            formatToString(m->m_output->state->state().drmFormat), m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format),
            (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness), (m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance),
            (!m->shouldUseSoftwareCursors() ? "true" : "false"));

    } else {
        result += std::format(
            "Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tphysical size (mm): {}x{}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
            "special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
            "dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tsolitaryBlockedBy: {}\n\tactivelyTearing: {}\n\ttearingBlockedBy: {}\n\tdirectScanoutTo: "
            "{:x}\n\tdirectScanoutBlockedBy: {}\n\tdisabled: "
            "{}\n\tcurrentFormat: {}\n\tmirrorOf: "
            "{}\n\tavailableModes: {}\n\tcolorManagementPreset: {}\n\tsdrBrightness: {:.2f}\n\tsdrSaturation: {:.2f}\n\tsdrMinLuminance: {:.2f}\n\tsdrMaxLuminance: "
            "{}\n\thardwareCursorsInUse: {}\n\n",
            m->m_name, m->m_id, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->m_shortDescription,
            m->m_output->make, m->m_output->model, sc<int>(m->m_output->physicalSize.x), sc<int>(m->m_output->physicalSize.y), m->m_output->serial, m->activeWorkspaceID(),
            (!m->m_activeWorkspace ? "" : m->m_activeWorkspace->m_name), m->activeSpecialWorkspaceID(), (m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""),
            sc<int>(m->m_reservedArea.left()), sc<int>(m->m_reservedArea.top()), sc<int>(m->m_reservedArea.right()), sc<int>(m->m_reservedArea.bottom()), m->m_scale,
            sc<int>(m->m_transform), (m == Desktop::focusState()->monitor() ? "yes" : "no"), sc<int>(m->m_dpmsStatus), m->m_output->state->state().adaptiveSync,
            rc<uint64_t>(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), m->m_tearingState.activelyTearing, getTearingBlockedReason(m, format),
            rc<uint64_t>(m->m_lastScanout.get()), getDSBlockedReason(m, format), !m->m_enabled, formatToString(m->m_output->state->state().drmFormat),
            m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format), (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness),
            (m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance), (!m->shouldUseSoftwareCursors()));
    }

    return result;
}

static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
    CVarList vars(request, 0, ' ');
    auto     allMonitors = false;

    if (vars.size() > 2)
        return "too many args";

    if (vars.size() == 2 && vars[1] == "all")
        allMonitors = true;

    std::string result = "";
    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "[";

        for (auto const& m : allMonitors ? State::monitorState()->allMonitors() : State::monitorState()->monitors()) {
            result += CHyprCtl::getMonitorData(m, format);
        }

        trimTrailingComma(result);

        result += "]";
    } else {
        for (auto const& m : allMonitors ? State::monitorState()->allMonitors() : State::monitorState()->monitors()) {
            if (!m->m_output || m->m_id == -1)
                continue;

            result += CHyprCtl::getMonitorData(m, format);
        }
    }

    return result;
}

static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
    const auto tags = w->m_ruleApplicator->m_tagKeeper.getTags();

    if (format == eHyprCtlOutputFormat::FORMAT_JSON)
        return std::ranges::fold_left(tags, std::string(),
                                      [](const std::string& a, const std::string& b) { return a.empty() ? std::format("\"{}\"", b) : std::format("{}, \"{}\"", a, b); });
    else
        return std::ranges::fold_left(tags, std::string(), [](const std::string& a, const std::string& b) { return a.empty() ? b : a + ", " + b; });
}

static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
    const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
    if (!w->m_group)
        return isJson ? "" : "0";

    std::ostringstream result;

    for (const auto& curr : w->m_group->windows()) {
        if (isJson)
            result << std::format("\"0x{:x}\"", rc<uintptr_t>(curr.get()));
        else
            result << std::format("{:x}", rc<uintptr_t>(curr.get()));

        if (curr != w->m_group->windows().back())
            result << (isJson ? ", " : ",");
    }

    return result.str();
}

std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
    auto getFocusHistoryID = [](PHLWINDOW wnd) -> int {
        const auto& HISTORY = Desktop::History::windowTracker()->fullHistory();
        for (size_t i = 0; i < HISTORY.size(); ++i) {
            if (HISTORY[i].lock() == wnd)
                return HISTORY.size() - i - 1; // reverse order for backwards compat
        }
        return -1;
    };

    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        return std::format(
            R"#({{
    "address": "0x{:x}",
    "mapped": {},
    "hidden": {},
    "visible": {},
    "acceptsInput": {},
    "at": [{}, {}],
    "size": [{}, {}],
    "workspace": {{
        "id": {},
        "name": "{}"
    }},
    "floating": {},
    "monitor": {},
    "class": "{}",
    "title": "{}",
    "initialClass": "{}",
    "initialTitle": "{}",
    "pid": {},
    "xwayland": {},
    "pinned": {},
    "pinFullscreened": {},
    "fullscreen": {},
    "fullscreenClient": {},
    "fullscreenHandler": "{}",
    "allowedOverFullscreen": {},
    "grouped": [{}],
    "tags": [{}],
    "swallowing": "0x{:x}",
    "focusHistoryID": {},
    "inhibitingIdle": {},
    "xdgTag": "{}",
    "xdgDescription": "{}",
    "contentType": "{}",
    "tearingHint": {},
    "stableId": "{:x}"
}},)#",
            rc<uintptr_t>(w.get()), (w->m_isMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (w->visible() ? "true" : "false"),
            (w->acceptsInput() ? "true" : "false"), sc<int>(w->position(Desktop::View::IGeometric::GEOMETRIC_GOAL).x),
            sc<int>(w->position(Desktop::View::IGeometric::GEOMETRIC_GOAL).y), sc<int>(w->size(Desktop::View::IGeometric::GEOMETRIC_GOAL).x),
            sc<int>(w->size(Desktop::View::IGeometric::GEOMETRIC_GOAL).y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
            escapeJSONStrings(!w->m_workspace ? "" : w->m_workspace->m_name), (sc<int>(w->m_isFloating) == 1 ? "true" : "false"), w->monitorID(), escapeJSONStrings(w->m_class),
            escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle), w->getPID(), (sc<int>(w->m_isX11) == 1 ? "true" : "false"),
            (w->m_pinned ? "true" : "false"), (w->m_pinFullscreened ? "true" : "false"), sc<uint8_t>(Fullscreen::controller()->getFullscreenModes(w).internal),
            sc<uint8_t>(Fullscreen::controller()->getFullscreenModes(w).client), escapeJSONStrings(Fullscreen::controller()->getFullscreenHandlerNameAsString(w)),
            (w->m_allowedOverFullscreen ? "true" : "false"), getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowee.get()), getFocusHistoryID(w),
            (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")),
            escapeJSONStrings(NContentType::toString(w->getContentType())), (w->m_tearingHint ? "true" : "false"), w->m_stableID);
    } else {
        return std::format(
            "Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tvisible: {}\n\tacceptsInput: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: "
            "{}\n\tclass: {}\n\ttitle: "
            "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
            "{}\n\txwayland: {}\n\tpinned: {}\n\tpinFullscreened: "
            "{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tfullscreenHandler: {}\n\tallowedOverFullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: "
            "{}\n\tinhibitingIdle: "
            "{}\n\txdgTag: "
            "{}\n\txdgDescription: {}\n\tcontentType: {}\n\ttearingHint: {}\n\tstableID: {:x}\n\n",
            rc<uintptr_t>(w.get()), w->m_title, sc<int>(w->m_isMapped), sc<int>(w->isHidden()), sc<int>(w->visible()), sc<int>(w->acceptsInput()),
            sc<int>(w->position(Desktop::View::IGeometric::GEOMETRIC_GOAL).x), sc<int>(w->position(Desktop::View::IGeometric::GEOMETRIC_GOAL).y),
            sc<int>(w->size(Desktop::View::IGeometric::GEOMETRIC_GOAL).x), sc<int>(w->size(Desktop::View::IGeometric::GEOMETRIC_GOAL).y),
            w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID, (!w->m_workspace ? "" : w->m_workspace->m_name), sc<int>(w->m_isFloating), w->monitorID(), w->m_class,
            w->m_title, w->m_initialClass, w->m_initialTitle, w->getPID(), sc<int>(w->m_isX11), sc<int>(w->m_pinned), sc<int>(w->m_pinFullscreened),
            sc<uint8_t>(Fullscreen::controller()->getFullscreenModes(w).internal), sc<uint8_t>(Fullscreen::controller()->getFullscreenModes(w).client),
            Fullscreen::controller()->getFullscreenHandlerNameAsString(w), sc<int>(w->m_allowedOverFullscreen), getGroupedData(w, format), getTagsData(w, format),
            rc<uintptr_t>(w->m_swallowee.get()), getFocusHistoryID(w), sc<int>(g_pInputManager->isWindowInhibiting(w, false)), w->xdgTag().value_or(""),
            w->xdgDescription().value_or(""), NContentType::toString(w->getContentType()), sc<int>(w->m_tearingHint), w->m_stableID);
    }
}

static std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string result = "";
    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "[";

        for (auto const& w : Desktop::windowState()->windows()) {
            if (!w->m_isMapped && !g_pHyprCtl->m_currentRequestParams.all)
                continue;

            result += CHyprCtl::getWindowData(w, format);
        }

        trimTrailingComma(result);

        result += "]";
    } else {
        for (auto const& w : Desktop::windowState()->windows()) {
            if (!w->m_isMapped && !g_pHyprCtl->m_currentRequestParams.all)
                continue;

            result += CHyprCtl::getWindowData(w, format);
        }

        if (result.empty())
            return "no open windows";
    }
    return result;
}

std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) {
    const auto  PLASTW   = w->getLastFocusedWindow();
    const auto  PMONITOR = w->m_monitor.lock();

    std::string layoutName = "unknown";
    if (w->m_space && w->m_space->algorithm() && w->m_space->algorithm()->tiledAlgo()) {
        const auto& TILED_ALGO = w->m_space->algorithm()->tiledAlgo();
        layoutName             = Layout::Supplementary::algoMatcher()->getNameForTiledAlgo(&typeid(*TILED_ALGO.get()));
    }

    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        return std::format(R"#({{
    "id": {},
    "name": "{}",
    "monitor": "{}",
    "monitorID": {},
    "windows": {},
    "hasfullscreen": {},
    "lastwindow": "0x{:x}",
    "lastwindowtitle": "{}",
    "ispersistent": {},
    "tiledLayout": "{}"
}})#",
                           w->m_id, escapeJSONStrings(w->m_name), escapeJSONStrings(PMONITOR ? PMONITOR->m_name : "?"),
                           escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->m_id) : "null"), w->getWindowCount(),
                           Fullscreen::controller()->hasFullscreen(w) ? "true" : "false", rc<uintptr_t>(PLASTW.get()), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "",
                           w->isPersistent() ? "true" : "false", escapeJSONStrings(layoutName));
    } else {
        return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: "
                           "{}\n\ttiledLayout: {}\n\n",
                           w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindowCount(),
                           sc<int>(Fullscreen::controller()->hasFullscreen(w)), rc<uintptr_t>(PLASTW.get()), PLASTW ? PLASTW->m_title : "", sc<int>(w->isPersistent()), layoutName);
    }
}

static std::string getWorkspaceRuleData(const Config::CWorkspaceRule& r, eHyprCtlOutputFormat format) {
    const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; };
    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        const std::string monitor     = r.m_monitor.empty() ? "" : std::format(",\n    \"monitor\": \"{}\"", escapeJSONStrings(r.m_monitor));
        const std::string enabled     = std::format(",\n    \"enabled\": {}", boolToString(r.isEnabled()));
        const std::string default_    = sc<bool>(r.m_isDefault) ? std::format(",\n    \"default\": {}", boolToString(r.m_isDefault.value())) : "";
        const std::string persistent  = sc<bool>(r.m_isPersistent) ? std::format(",\n    \"persistent\": {}", boolToString(r.m_isPersistent.value())) : "";
        const std::string gapsIn      = sc<bool>(r.m_gapsIn) ?
            std::format(",\n    \"gapsIn\": [{}, {}, {}, {}]", r.m_gapsIn.value().m_top, r.m_gapsIn.value().m_right, r.m_gapsIn.value().m_bottom, r.m_gapsIn.value().m_left) :
            "";
        const std::string gapsOut     = sc<bool>(r.m_gapsOut) ?
            std::format(",\n    \"gapsOut\": [{}, {}, {}, {}]", r.m_gapsOut.value().m_top, r.m_gapsOut.value().m_right, r.m_gapsOut.value().m_bottom, r.m_gapsOut.value().m_left) :
            "";
        const std::string borderSize  = sc<bool>(r.m_borderSize) ? std::format(",\n    \"borderSize\": {}", r.m_borderSize.value()) : "";
        const std::string border      = sc<bool>(r.m_noBorder) ? std::format(",\n    \"border\": {}", boolToString(!r.m_noBorder.value())) : "";
        const std::string rounding    = sc<bool>(r.m_noRounding) ? std::format(",\n    \"rounding\": {}", boolToString(!r.m_noRounding.value())) : "";
        const std::string decorate    = sc<bool>(r.m_decorate) ? std::format(",\n    \"decorate\": {}", boolToString(r.m_decorate.value())) : "";
        const std::string shadow      = sc<bool>(r.m_noShadow) ? std::format(",\n    \"shadow\": {}", boolToString(!r.m_noShadow.value())) : "";
        const std::string defaultName = r.m_defaultName.has_value() ? std::format(",\n    \"defaultName\": \"{}\"", escapeJSONStrings(r.m_defaultName.value())) : "";
        const std::string onCreatedEmpty =
            r.m_onCreatedEmptyRunCmd.has_value() ? std::format(",\n    \"onCreatedEmpty\": \"{}\"", escapeJSONStrings(r.m_onCreatedEmptyRunCmd.value())) : "";

        std::string result = std::format(R"#({{
    "workspaceString": "{}"{}{}{}{}{}{}{}{}{}{}{}{}{}
}})#",
                                         escapeJSONStrings(r.m_workspaceString), enabled, monitor, default_, persistent, gapsIn, gapsOut, borderSize, border, rounding, decorate,
                                         shadow, defaultName, onCreatedEmpty);

        return result;
    } else {
        const std::string monitor        = std::format("\tmonitor: {}\n", r.m_monitor.empty() ? "<unset>" : escapeJSONStrings(r.m_monitor));
        const std::string enabled        = std::format("\tenabled: {}\n", boolToString(r.isEnabled()));
        const std::string default_       = std::format("\tdefault: {}\n", sc<bool>(r.m_isDefault) ? boolToString(r.m_isDefault.value()) : "<unset>");
        const std::string persistent     = std::format("\tpersistent: {}\n", sc<bool>(r.m_isPersistent) ? boolToString(r.m_isPersistent.value()) : "<unset>");
        const std::string gapsIn         = sc<bool>(r.m_gapsIn) ?
            std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.m_gapsIn.value().m_top), std::to_string(r.m_gapsIn.value().m_right),
                        std::to_string(r.m_gapsIn.value().m_bottom), std::to_string(r.m_gapsIn.value().m_left)) :
            std::format("\tgapsIn: <unset>\n");
        const std::string gapsOut        = sc<bool>(r.m_gapsOut) ?
            std::format("\tgapsOut: {} {} {} {}\n", std::to_string(r.m_gapsOut.value().m_top), std::to_string(r.m_gapsOut.value().m_right),
                        std::to_string(r.m_gapsOut.value().m_bottom), std::to_string(r.m_gapsOut.value().m_left)) :
            std::format("\tgapsOut: <unset>\n");
        const std::string borderSize     = std::format("\tborderSize: {}\n", sc<bool>(r.m_borderSize) ? std::to_string(r.m_borderSize.value()) : "<unset>");
        const std::string border         = std::format("\tborder: {}\n", sc<bool>(r.m_noBorder) ? boolToString(!r.m_noBorder.value()) : "<unset>");
        const std::string rounding       = std::format("\trounding: {}\n", sc<bool>(r.m_noRounding) ? boolToString(!r.m_noRounding.value()) : "<unset>");
        const std::string decorate       = std::format("\tdecorate: {}\n", sc<bool>(r.m_decorate) ? boolToString(r.m_decorate.value()) : "<unset>");
        const std::string shadow         = std::format("\tshadow: {}\n", sc<bool>(r.m_noShadow) ? boolToString(!r.m_noShadow.value()) : "<unset>");
        const std::string defaultName    = std::format("\tdefaultName: {}\n", r.m_defaultName.value_or("<unset>"));
        const std::string onCreatedEmpty = std::format("\tonCreatedEmpty: {}\n", r.m_onCreatedEmptyRunCmd.value_or("<unset>"));

        std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.m_workspaceString), enabled, monitor, default_, persistent, gapsIn,
                                         gapsOut, borderSize, border, rounding, decorate, shadow, defaultName, onCreatedEmpty);

        return result;
    }
}

static std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
    if (!Desktop::focusState()->monitor())
        return "unsafe state";

    std::string result = "";
    auto        w      = Desktop::focusState()->monitor()->m_activeWorkspace;

    if (!valid(w))
        return "internal error";

    return CHyprCtl::getWorkspaceData(w, format);
}

static std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string result = "";

    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "[";
        for (auto const& w : State::workspaceState()->workspaces()) {
            result += CHyprCtl::getWorkspaceData(w.lock(), format);
            result += ",";
        }

        trimTrailingComma(result);
        result += "]";
    } else {
        for (auto const& w : State::workspaceState()->workspaces()) {
            result += CHyprCtl::getWorkspaceData(w.lock(), format);
        }
    }

    return result;
}

static std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string result = "";
    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "[";
        for (auto const& r : Config::workspaceRuleMgr()->getAllWorkspaceRules()) {
            result += getWorkspaceRuleData(*r, format);
            result += ",";
        }

        trimTrailingComma(result);
        result += "]";
    } else {
        for (auto const& r : Config::workspaceRuleMgr()->getAllWorkspaceRules()) {
            result += getWorkspaceRuleData(*r, format);
        }
    }

    return result;
}

static std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
    const auto PWINDOW = Desktop::focusState()->window();

    if (!validMapped(PWINDOW))
        return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";

    auto result = CHyprCtl::getWindowData(PWINDOW, format);

    if (format == eHyprCtlOutputFormat::FORMAT_JSON)
        result.pop_back();

    return result;
}

static std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string result = "";

    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "{\n";

        for (auto const& mon : State::monitorState()->monitors()) {
            result += std::format(
                R"#("{}": {{
    "levels": {{
)#",
                escapeJSONStrings(mon->m_name));

            int layerLevel = 0;
            for (auto const& level : mon->m_layerSurfaceLayers) {
                result += std::format(
                    R"#(
        "{}": [
)#",
                    layerLevel);
                for (auto const& layer : level) {
                    result += std::format(
                        R"#(                {{
                    "address": "0x{:x}",
                    "x": {},
                    "y": {},
                    "w": {},
                    "h": {},
                    "alpha": {},
                    "namespace": "{}",
                    "pid": {}
                }},)#",
                        rc<uintptr_t>(layer.get()), layer->m_geometry.x, layer->m_geometry.y, layer->m_geometry.width, layer->m_geometry.height,
                        std::clamp(sc<double>(layer->alpha().goal()), 0.0, 1.0), escapeJSONStrings(layer->m_namespace), layer->getPID());
                }

                trimTrailingComma(result);

                if (!level.empty())
                    result += "\n        ";

                result += "],";

                layerLevel++;
            }

            trimTrailingComma(result);

            result += "\n    }\n},";
        }

        trimTrailingComma(result);

        result += "\n}\n";

    } else {
        for (auto const& mon : State::monitorState()->monitors()) {
            result += std::format("Monitor {}:\n", mon->m_name);
            int                                     layerLevel = 0;
            static const std::array<std::string, 4> levelNames = {"background", "bottom", "top", "overlay"};
            for (auto const& level : mon->m_layerSurfaceLayers) {
                result += std::format("\tLayer level {} ({}):\n", layerLevel, levelNames[layerLevel]);

                for (auto const& layer : level) {
                    result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, a: {}, namespace: {}, pid: {}\n", rc<uintptr_t>(layer.get()), layer->m_geometry.x,
                                          layer->m_geometry.y, layer->m_geometry.width, layer->m_geometry.height, std::clamp(sc<double>(layer->alpha().goal()), 0.0, 1.0),
                                          layer->m_namespace, layer->getPID());
                }

                layerLevel++;
            }
            result += "\n\n";
        }
    }

    return result;
}

static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string result     = "";
    std::string currErrors = Config::mgr()->getErrors();
    CVarList    errLines(currErrors, 0, '\n');
    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "[";
        for (const auto& line : errLines) {
            result += std::format(
                R"#(
	"{}",)#",

                escapeJSONStrings(line));
        }
        trimTrailingComma(result);
        result += "\n]\n";
    } else {
        for (const auto& line : errLines) {
            result += std::format("{}\n", line);
        }
    }
    return result;
}

static std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string result = "";

    auto        getModState = [](SP<IKeyboard> keyboard, const char* xkbModName) -> bool {
        auto IDX = xkb_keymap_mod_get_index(keyboard->m_xkbKeymap, xkbModName);

        if (IDX == XKB_MOD_INVALID)
            return false;

        return (keyboard->m_modifiersState.locked & (1 << IDX)) > 0;
    };

    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "{\n";
        result += "\"mice\": [\n";

        for (auto const& m : g_pInputManager->m_pointers) {
            result += std::format(
                R"#(    {{
        "address": "0x{:x}",
        "name": "{}",
        "defaultSpeed": {:.5f},
        "scrollFactor": {:.2f}
    }},)#",
                rc<uintptr_t>(m.get()), escapeJSONStrings(m->m_hlName),
                m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f, m->m_scrollFactor.value_or(-1));
        }

        trimTrailingComma(result);
        result += "\n],\n";

        result += "\"keyboards\": [\n";
        for (auto const& k : g_pInputManager->m_keyboards) {
            const auto INDEX_OPT = k->getActiveLayoutIndex();
            const auto KI        = INDEX_OPT.has_value() ? std::to_string(INDEX_OPT.value()) : "none";
            const auto KM        = k->getActiveLayout();
            result += std::format(
                R"#(    {{
        "address": "0x{:x}",
        "name": "{}",
        "rules": "{}",
        "model": "{}",
        "layout": "{}",
        "variant": "{}",
        "options": "{}",
        "active_layout_index": {},
        "active_keymap": "{}",
        "capsLock": {},
        "numLock": {},
        "main": {}
    }},)#",
                rc<uintptr_t>(k.get()), escapeJSONStrings(k->m_hlName), escapeJSONStrings(k->m_currentRules.rules), escapeJSONStrings(k->m_currentRules.model),
                escapeJSONStrings(k->m_currentRules.layout), escapeJSONStrings(k->m_currentRules.variant), escapeJSONStrings(k->m_currentRules.options), KI, escapeJSONStrings(KM),
                (getModState(k, XKB_MOD_NAME_CAPS) ? "true" : "false"), (getModState(k, XKB_MOD_NAME_NUM) ? "true" : "false"), (k->m_active ? "true" : "false"));
        }

        trimTrailingComma(result);
        result += "\n],\n";

        result += "\"tablets\": [\n";

        for (auto const& d : g_pInputManager->m_tabletPads) {
            result += std::format(
                R"#(    {{
        "address": "0x{:x}",
        "type": "tabletPad",
        "belongsTo": {{
            "address": "0x{:x}",
            "name": "{}"
        }}
    }},)#",
                rc<uintptr_t>(d.get()), rc<uintptr_t>(d->m_parent.get()), escapeJSONStrings(d->m_parent ? d->m_parent->m_hlName : ""));
        }

        for (auto const& d : g_pInputManager->m_tablets) {
            result += std::format(
                R"#(    {{
        "address": "0x{:x}",
        "name": "{}"
    }},)#",
                rc<uintptr_t>(d.get()), escapeJSONStrings(d->m_hlName));
        }

        for (auto const& d : g_pInputManager->m_tabletTools) {
            result += std::format(
                R"#(    {{
        "address": "0x{:x}",
        "type": "tabletTool"
    }},)#",
                rc<uintptr_t>(d.get()));
        }

        trimTrailingComma(result);
        result += "\n],\n";

        result += "\"touch\": [\n";

        for (auto const& d : g_pInputManager->m_touches) {
            result += std::format(
                R"#(    {{
        "address": "0x{:x}",
        "name": "{}"
    }},)#",
                rc<uintptr_t>(d.get()), escapeJSONStrings(d->m_hlName));
        }

        trimTrailingComma(result);
        result += "\n],\n";

        result += "\"switches\": [\n";

        for (auto const& d : g_pInputManager->m_switches) {
            result += std::format(
                R"#(    {{
        "address": "0x{:x}",
        "name": "{}"
    }},)#",
                rc<uintptr_t>(&d), escapeJSONStrings(d.pDevice ? d.pDevice->getName() : ""));
        }

        trimTrailingComma(result);
        result += "\n]\n";

        result += "}\n";

    } else {
        result += "mice:\n";

        for (auto const& m : g_pInputManager->m_pointers) {
            result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n\t\t\tscroll factor: {:.2f}\n", rc<uintptr_t>(m.get()), m->m_hlName,
                                  (m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f),
                                  m->m_scrollFactor.value_or(-1));
        }

        result += "\n\nKeyboards:\n";

        for (auto const& k : g_pInputManager->m_keyboards) {
            const auto INDEX_OPT = k->getActiveLayoutIndex();
            const auto KI        = INDEX_OPT.has_value() ? std::to_string(INDEX_OPT.value()) : "none";
            const auto KM        = k->getActiveLayout();
            result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive layout index: {}\n\t\t\tactive keymap: "
                                  "{}\n\t\t\tcapsLock: "
                                  "{}\n\t\t\tnumLock: {}\n\t\t\tmain: {}\n",
                                  rc<uintptr_t>(k.get()), k->m_hlName, k->m_currentRules.rules, k->m_currentRules.model, k->m_currentRules.layout, k->m_currentRules.variant,
                                  k->m_currentRules.options, KI, KM, (getModState(k, XKB_MOD_NAME_CAPS) ? "yes" : "no"), (getModState(k, XKB_MOD_NAME_NUM) ? "yes" : "no"),
                                  (k->m_active ? "yes" : "no"));
        }

        result += "\n\nTablets:\n";

        for (auto const& d : g_pInputManager->m_tabletPads) {
            result +=
                std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", rc<uintptr_t>(d.get()), rc<uintptr_t>(d->m_parent.get()), d->m_parent ? d->m_parent->m_hlName : "");
        }

        for (auto const& d : g_pInputManager->m_tablets) {
            result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", rc<uintptr_t>(d.get()), d->m_hlName, d->aq()->physicalSize.x, d->aq()->physicalSize.y);
        }

        for (auto const& d : g_pInputManager->m_tabletTools) {
            result += std::format("\tTablet Tool at {:x}\n", rc<uintptr_t>(d.get()));
        }

        result += "\n\nTouch:\n";

        for (auto const& d : g_pInputManager->m_touches) {
            result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", rc<uintptr_t>(d.get()), d->m_hlName);
        }

        result += "\n\nSwitches:\n";

        for (auto const& d : g_pInputManager->m_switches) {
            result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", rc<uintptr_t>(&d), d.pDevice ? d.pDevice->getName() : "");
        }
    }

    return result;
}

static std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string ret = "";
    if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
        ret += "animations:\n";

        for (auto const& ac : Config::animationTree()->getAnimationConfig()) {
            ret += std::format("\n\tname: {}\n\t\toverridden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, sc<int>(ac.second->overridden),
                               ac.second->internalBezier, ac.second->internalEnabled, ac.second->internalSpeed, ac.second->internalStyle);
        }

        ret += "beziers:\n";

        for (auto const& bz : Animation::mgr()->getAllBeziers()) {
            auto& controlPoints = bz.second->getControlPoints();
            ret += std::format("\n\tname: {}\n\t\tX0: {:.2f}\n\t\tY0: {:.2f}\n\t\tX1: {:.2f}\n\t\tY1: {:.2f}", bz.first, controlPoints[1].x, controlPoints[1].y, controlPoints[2].x,
                               controlPoints[2].y);
        }
    } else {
        // json

        ret += "[[";
        for (auto const& ac : Config::animationTree()->getAnimationConfig()) {
            ret += std::format(R"#(
{{
    "name": "{}",
    "overridden": {},
    "bezier": "{}",
    "enabled": {},
    "speed": {:.2f},
    "style": "{}"
}},)#",
                               ac.first, ac.second->overridden ? "true" : "false", escapeJSONStrings(ac.second->internalBezier), ac.second->internalEnabled ? "true" : "false",
                               ac.second->internalSpeed, escapeJSONStrings(ac.second->internalStyle));
        }

        ret[ret.length() - 1] = ']';

        ret += ",\n[";

        for (auto const& bz : Animation::mgr()->getAllBeziers()) {
            auto& controlPoints = bz.second->getControlPoints();
            ret += std::format(R"#(
{{
    "name": "{}",
    "X0": {:.2f},
    "Y0": {:.2f},
    "X1": {:.2f},
    "Y1": {:.2f}
}},)#",
                               escapeJSONStrings(bz.first), controlPoints[1].x, controlPoints[1].y, controlPoints[2].x, controlPoints[2].y);
        }

        trimTrailingComma(ret);

        ret += "]]";
    }

    return ret;
}

static std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string result = "";

    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "[\n\"log\":\"";
        result += escapeJSONStrings(Log::logger->rolling());
        result += "\"]";
    } else
        result = Log::logger->rolling();

    return result;
}

static std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string ret       = "";
    const auto  SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
    if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
        for (auto const& sh : SHORTCUTS) {
            ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
        }
        if (ret.empty())
            ret = "none";
    } else {
        ret += "[";
        for (auto const& sh : SHORTCUTS) {
            ret += std::format(R"#(
{{
    "name": "{}",
    "description": "{}"
}},)#",
                               escapeJSONStrings(sh.appid + ":" + sh.id), escapeJSONStrings(sh.description));
        }
        trimTrailingComma(ret);
        ret += "]\n";
    }

    return ret;
}

static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string ret = "";
    if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
        for (auto const& kb : g_pKeybindManager->m_keybinds) {
            ret += "bind";
            if (kb->locked)
                ret += "l";
            if (kb->mouse)
                ret += "m";
            if (kb->release)
                ret += "r";
            if (kb->repeat)
                ret += "e";
            if (kb->nonConsuming)
                ret += "n";
            if (kb->autoConsuming)
                ret += "a";
            if (kb->hasDescription)
                ret += "d";
            if (kb->allowInputCapture)
                ret += "x";

            ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdescription: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb->modmask,
                               kb->submap.name, kb->key.empty() ? kb->displayKey : kb->key, kb->keycode, kb->catchAll, kb->description, kb->handler, kb->arg);
        }
    } else {
        // json
        ret += "[";
        for (auto const& kb : g_pKeybindManager->m_keybinds) {
            ret += std::format(
                R"#(
{{
    "locked": {},
    "mouse": {},
    "release": {},
    "repeat": {},
    "longPress": {},
    "non_consuming": {},
    "auto_consuming": {},
    "has_description": {},
    "modmask": {},
    "submap": "{}",
    "submap_universal": "{}",
    "key": "{}",
    "keycode": {},
    "catch_all": {},
    "description": "{}",
    "allow_input_capture": {},
    "dispatcher": "{}",
    "arg": "{}"
}},)#",
                kb->locked ? "true" : "false", kb->mouse ? "true" : "false", kb->release ? "true" : "false", kb->repeat ? "true" : "false", kb->longPress ? "true" : "false",
                kb->nonConsuming ? "true" : "false", kb->autoConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap.name),
                kb->submapUniversal, escapeJSONStrings(kb->key), kb->keycode, kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description),
                kb->allowInputCapture ? "true" : "false", escapeJSONStrings(kb->handler), escapeJSONStrings(kb->arg));
        }
        trimTrailingComma(ret);
        ret += "]";
    }

    return ret;
}

std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
    return Helpers::SystemInfo::getVersion(format);
}

static std::string statusRequest(eHyprCtlOutputFormat format, std::string request) {
    return Helpers::SystemInfo::getStatus(format);
}

std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) {

    auto result = Helpers::SystemInfo::getSystemInfo();

    if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.sysInfoConfig) {
        result += "\n\n======Config-Start======\n";
        result += Config::mgr()->getConfigString();
        result += "\n======Config-End========\n";
    }

    return result;
}

static std::string evalRequest(eHyprCtlOutputFormat format, std::string request) {
    if (Config::mgr()->type() != Config::CONFIG_LUA)
        return "eval is only supported with the lua config manager";

    auto luaMgr = dynamicPointerCast<Config::Lua::CConfigManager>(WP<Config::IConfigManager>(Config::mgr()));

    // strip the command name ("eval ") from the request
    auto code = request.substr(request.find_first_of(' ') + 1);

    auto err = luaMgr->eval(code, request.starts_with("repl "));
    if (err)
        return *err;

    return "ok";
}

static std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
    // get rid of the dispatch keyword
    in = in.substr(in.find_first_of(' ') + 1);

    if (Config::mgr()->type() == Config::CONFIG_LUA) {
        // For lua, this is just a wrapper for `eval("hl.dispatch(in)")
        std::string evalStr = std::format("return hl.dispatch({})", in);
        auto        luaMgr  = dynamicPointerCast<Config::Lua::CConfigManager>(WP<Config::IConfigManager>(Config::mgr()));
        auto        ret     = luaMgr->eval(evalStr).value_or("ok");

        if (ret.starts_with("ok") || in.contains("(") /* this likely means the user is passing a valid lua dispatch string */)
            return ret;

        // the user likely is trying to dispatch old hyprlang stuff via lua, let them know
        return ret + "\n\n → Note: dispatch in lua is a shorthand for hl.dispatch(...), your syntax might need to be updated.";
    }

    const auto DISPATCHSTR = in.substr(0, in.find_first_of(' '));

    auto       DISPATCHARG = std::string();
    if (sc<int>(in.find_first_of(' ')) != -1)
        DISPATCHARG = in.substr(in.find_first_of(' ') + 1);

    const auto DISPATCHER = g_pKeybindManager->m_dispatchers.find(DISPATCHSTR);
    if (DISPATCHER == g_pKeybindManager->m_dispatchers.end())
        return "Invalid dispatcher";

    SDispatchResult res = DISPATCHER->second(DISPATCHARG);

    Log::logger->log(Log::DEBUG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error);

    return res.success ? "ok" : res.error;
}

static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {

    if (Config::mgr()->type() != Config::CONFIG_LEGACY)
        return "keyword can't work with non-legacy parsers. Use eval.";

    WP<Config::Legacy::CConfigManager> mgr = dynamicPointerCast<Config::Legacy::CConfigManager>(WP<Config::IConfigManager>(Config::mgr()));

    // Find the first space to strip the keyword keyword
    auto const firstSpacePos = in.find_first_of(' ');
    if (firstSpacePos == std::string::npos) // Handle the case where there's no space found (invalid input)
        return "Invalid input: no space found";

    // Strip the keyword
    in = in.substr(firstSpacePos + 1);

    // Find the next space for the COMMAND and VALUE
    auto const secondSpacePos = in.find_first_of(' ');
    if (secondSpacePos == std::string::npos) // Handle the case where there's no second space (invalid input)
        return "Invalid input: command and value not properly formatted";

    // Extract COMMAND and VALUE
    const auto COMMAND = in.substr(0, secondSpacePos);
    const auto VALUE   = in.substr(secondSpacePos + 1);

    // If COMMAND is empty, handle accordingly
    if (COMMAND.empty())
        return "Invalid input: command is empty";

    g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = true;

    std::string retval = mgr->parseKeyword(COMMAND, VALUE);

    g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = false;

    if (COMMAND == "source") {
        Config::monitorRuleMgr()->scheduleReload();
        g_pEventLoopManager->doLater([mgr] { mgr->reloadRules(); });
    }

    // if we are executing a dynamic source we have to reload everything, so every if will have a check for source.
    if (COMMAND.contains("monitor"))
        g_pEventLoopManager->doLater([] { Config::monitorRuleMgr()->scheduleReload(); });

    if (COMMAND.contains("input") || COMMAND.contains("device") || COMMAND == "source") {
        g_pInputManager->setKeyboardLayout();     // update kb layout
        g_pInputManager->setPointerConfigs();     // update mouse cfgs
        g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs
        g_pInputManager->setTabletConfigs();      // update tablets
    }

    if (COMMAND.contains("general:layout") || (COMMAND.contains("workspace") && VALUE.contains("layout:")))
        Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts();

    if (COMMAND.contains("decoration:screen_shader") || COMMAND == "source")
        g_pHyprRenderer->m_reloadScreenShader = true;

    if (COMMAND.contains("blur") || COMMAND == "source") {
        for (auto const& m : State::monitorState()->monitors()) {
            if (m)
                m->m_blurFBDirty = true;
        }
    }

    if (COMMAND.contains("misc:disable_autoreload"))
        Config::watcher()->update();

    // decorations will probably need a repaint
    if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") {
        static auto PZOOMFACTOR = CConfigValue<Config::FLOAT>("cursor:zoom_factor");
        for (auto const& m : State::monitorState()->monitors()) {
            *(m->m_cursorZoom) = *PZOOMFACTOR;
            g_pHyprRenderer->damageMonitor(m);
            if (m->m_activeWorkspace)
                m->m_activeWorkspace->m_space->recalculate();
        }
    }

    if (COMMAND.contains("windowrule ") || COMMAND.contains("windowrule["))
        mgr->reloadRules();

    if (COMMAND.contains("layerrule") || COMMAND.contains("layerrule[")) {
        mgr->reloadRules();
        // Damage all monitors to redraw static layers.
        for (auto const& m : State::monitorState()->monitors()) {
            g_pHyprRenderer->damageMonitor(m);
        }
    }

    if (COMMAND.contains("workspace"))
        State::workspacePlacementController()->ensurePersistentWorkspacesPresent(
            nullptr, [](PHLWORKSPACE ws, PHLMONITOR mon, bool noWarp) { State::workspacePlacementController()->moveWorkspaceToMonitor(ws, mon, noWarp); });

    Log::logger->log(Log::DEBUG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);

    if (retval.empty())
        return "ok";

    return retval;
}

static std::string reloadRequest(eHyprCtlOutputFormat format, std::string request) {

    if (request.ends_with("full-reset")) {
        // perform a full config reset. First, reset config manager and load it anew. Then,
        // flush all config value caches. This will momentarily degrade performance
        // but a config load will be slow enough for this not to matter probably
        Config::mgr().reset();
        Config::Supplementary::Jeremy::flushCachedCfgPath();
        Config::initConfigManager();
        CConfigValueBase::flushCaches();
        Config::mgr()->init();
        CConfigValueBase::flushCaches();

        return "ok";
    }

    Config::mgr()->reload();

    return "ok";
}

static std::string killRequest(eHyprCtlOutputFormat format, std::string request) {
    g_pInputManager->setClickMode(CLICKMODE_KILL);

    return "ok";
}

static std::string splashRequest(eHyprCtlOutputFormat format, std::string request) {
    return g_pCompositor->m_currentSplash;
}

static std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) {
    const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor();

    if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
        return std::format("{}, {}", sc<int>(CURSORPOS.x), sc<int>(CURSORPOS.y));
    } else {
        return std::format(R"#(
{{
    "x": {},
    "y": {}
}}
)#",
                           sc<int>(CURSORPOS.x), sc<int>(CURSORPOS.y));
    }

    return "error";
}

static std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
    // split by ; ignores ; inside [] and adds ; on last command

    request                     = request.substr(9);
    std::string       reply     = "";
    const std::string DELIMITER = "\n\n\n";
    int               bracket   = 0;
    size_t            idx       = 0;

    for (size_t i = 0; i <= request.size(); ++i) {
        char ch = (i < request.size()) ? request[i] : ';';
        if (ch == '[')
            ++bracket;
        else if (ch == ']')
            --bracket;
        else if (ch == ';' && bracket == 0) {
            if (idx < i)
                reply += g_pHyprCtl->getReply(trim(request.substr(idx, i - idx))).append(DELIMITER);
            idx = i + 1;
            continue;
        }
    }

    return reply.substr(0, std::max(sc<int>(reply.size() - DELIMITER.size()), 0));
}

static std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) {
    CVarList    vars(request, 0, ' ');

    const auto  SIZESTR = vars[vars.size() - 1];
    std::string theme   = "";
    for (size_t i = 1; i < vars.size() - 1; ++i)
        theme += vars[i] + " ";
    if (!theme.empty())
        theme.pop_back();

    int size = 0;
    try {
        size = std::stoi(SIZESTR);
    } catch (...) { return "size not int"; }

    if (size <= 0)
        return "size not positive";

    if (!Pointer::Cursor::mgr()->changeTheme(theme, size))
        return "failed to set cursor";

    return "ok";
}

static std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
    CVarList      vars(request, 0, ' ');

    const auto    KB  = vars[1];
    const auto    CMD = vars[2];

    SP<IKeyboard> pKeyboard;

    auto          updateKeyboard = [](const SP<IKeyboard> KEEB, const std::string& CMD) -> std::optional<std::string> {
        const auto         LAYOUTS      = xkb_keymap_num_layouts(KEEB->m_xkbKeymap);
        xkb_layout_index_t activeLayout = 0;
        while (activeLayout < LAYOUTS) {
            if (xkb_state_layout_index_is_active(KEEB->m_xkbState, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1)
                break;

            activeLayout++;
        }

        if (CMD == "next")
            KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1);
        else if (CMD == "prev")
            KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked,
                                  activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
        else {
            int requestedLayout = 0;
            try {
                requestedLayout = std::stoi(CMD);
            } catch (std::exception& e) { return "invalid arg 2"; }

            if (requestedLayout < 0 || sc<uint64_t>(requestedLayout) > LAYOUTS - 1) {
                return "layout idx out of range of " + std::to_string(LAYOUTS);
            }

            KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked, requestedLayout);
        }

        return std::nullopt;
    };

    if (KB == "main" || KB == "active" || KB == "current") {
        for (auto const& k : g_pInputManager->m_keyboards) {
            if (!k->m_active)
                continue;

            pKeyboard = k;
            break;
        }
    } else if (KB == "all") {
        std::string result = "";
        for (auto const& k : g_pInputManager->m_keyboards) {
            auto res = updateKeyboard(k, CMD);
            if (res.has_value())
                result += *res + "\n";
        }
        return result.empty() ? "ok" : result;
    } else {
        auto k = std::ranges::find_if(g_pInputManager->m_keyboards, [&](const auto& other) { return other->m_hlName == deviceNameToInternalString(KB); });

        if (k == g_pInputManager->m_keyboards.end())
            return "device not found";

        pKeyboard = *k;
    }

    if (!pKeyboard)
        return "no device";

    auto result = updateKeyboard(pKeyboard, CMD);

    if (result.has_value())
        return *result;

    return "ok";
}

static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
    CVarList    vars(request, 0, ' ');

    std::string errorMessage = "";

    if (vars.size() < 3) {
        ErrorOverlay::overlay()->destroy();

        if (vars.size() == 2 && !vars[1].contains("dis"))
            return "var 1 not color or disable";

        return "ok";
    }

    const CHyprColor COLOR = Config::ParserUtils::parseColor(vars[1]).value_or(0);

    for (size_t i = 2; i < vars.size(); ++i)
        errorMessage += vars[i] + ' ';

    if (errorMessage.empty()) {
        ErrorOverlay::overlay()->destroy();
    } else {
        errorMessage.pop_back(); // pop last space
        ErrorOverlay::overlay()->queueCreate(errorMessage, COLOR);
    }

    return "ok";
}

static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) {
    CVarList vars(request, 0, ' ');

    if (vars.size() < 3)
        return "not enough args";

    const auto WINREGEX = vars[1];
    const auto PROP     = vars[2];

    const auto PWINDOW = Desktop::viewState()->query().selector(WINREGEX).runWindow();

    if (!PWINDOW)
        return "window not found";

    const bool FORMNORM = format == FORMAT_NORMAL;

    auto       sizeToString = [&](bool max) -> std::string {
        auto sizeValue = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
        if (max)
            sizeValue = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D(INFINITY, INFINITY));

        if (FORMNORM)
            return std::format("{} {}", sizeValue.x, sizeValue.y);
        else {
            std::string xSizeString = (sizeValue.x != INFINITY) ? std::to_string(sizeValue.x) : "null";
            std::string ySizeString = (sizeValue.y != INFINITY) ? std::to_string(sizeValue.y) : "null";
            return std::format(R"({{"{}": [{},{}]}})", PROP, xSizeString, ySizeString);
        }
    };

    auto alphaToString = [&](Desktop::Types::COverridableVar<Desktop::Types::SAlphaValue>& alpha, bool getAlpha) -> std::string {
        if (FORMNORM) {
            if (getAlpha)
                return std::format("{}", alpha.valueOrDefault().alpha);
            else
                return std::format("{}", alpha.valueOrDefault().overridden);
        } else {
            if (getAlpha)
                return std::format(R"({{"{}": {}}})", PROP, alpha.valueOrDefault().alpha);
            else
                return std::format(R"({{"{}": {}}})", PROP, alpha.valueOrDefault().overridden);
        }
    };

    auto borderColorToString = [&](bool active) -> std::string {
        static auto PACTIVECOL              = CConfigValue<Config::IComplexConfigValue>("general:col.active_border");
        static auto PINACTIVECOL            = CConfigValue<Config::IComplexConfigValue>("general:col.inactive_border");
        static auto PNOGROUPACTIVECOL       = CConfigValue<Config::IComplexConfigValue>("general:col.nogroup_border_active");
        static auto PNOGROUPINACTIVECOL     = CConfigValue<Config::IComplexConfigValue>("general:col.nogroup_border");
        static auto PGROUPACTIVECOL         = CConfigValue<Config::IComplexConfigValue>("group:col.border_active");
        static auto PGROUPINACTIVECOL       = CConfigValue<Config::IComplexConfigValue>("group:col.border_inactive");
        static auto PGROUPACTIVELOCKEDCOL   = CConfigValue<Config::IComplexConfigValue>("group:col.border_locked_active");
        static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Config::IComplexConfigValue>("group:col.border_locked_inactive");

        const bool  GROUPLOCKED = PWINDOW->m_group ? PWINDOW->m_group->locked() : false;

        if (active) {
            auto* const       ACTIVECOL            = (Config::CGradientValueData*)(PACTIVECOL.ptr());
            auto* const       NOGROUPACTIVECOL     = (Config::CGradientValueData*)(PNOGROUPACTIVECOL.ptr());
            auto* const       GROUPACTIVECOL       = (Config::CGradientValueData*)(PGROUPACTIVECOL.ptr());
            auto* const       GROUPACTIVELOCKEDCOL = (Config::CGradientValueData*)(PGROUPACTIVELOCKEDCOL.ptr());
            const auto* const ACTIVECOLOR =
                !PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);

            std::string borderColorString = PWINDOW->m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR).toString();
            if (FORMNORM)
                return borderColorString;
            else
                return std::format(R"({{"{}": "{}"}})", PROP, borderColorString);
        } else {
            auto* const       INACTIVECOL            = (Config::CGradientValueData*)(PINACTIVECOL.ptr());
            auto* const       NOGROUPINACTIVECOL     = (Config::CGradientValueData*)(PNOGROUPINACTIVECOL.ptr());
            auto* const       GROUPINACTIVECOL       = (Config::CGradientValueData*)(PGROUPINACTIVECOL.ptr());
            auto* const       GROUPINACTIVELOCKEDCOL = (Config::CGradientValueData*)(PGROUPINACTIVELOCKEDCOL.ptr());
            const auto* const INACTIVECOLOR          = !PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? INACTIVECOL : NOGROUPINACTIVECOL) :
                                                                           (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);

            std::string       borderColorString = PWINDOW->m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR).toString();
            if (FORMNORM)
                return borderColorString;
            else
                return std::format(R"({{"{}": "{}"}})", PROP, borderColorString);
        }
    };

    auto windowPropToString = [&](auto& prop) -> std::string {
        if (FORMNORM)
            return std::format("{}", prop.valueOrDefault());
        else
            return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
    };

    if (PROP == "animation") {
        auto& animationStyle = PWINDOW->m_ruleApplicator->animationStyle();
        if (FORMNORM)
            return animationStyle.valueOr("(unset)");
        else
            return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
    } else if (PROP == "max_size")
        return sizeToString(true);
    else if (PROP == "min_size")
        return sizeToString(false);
    else if (PROP == "opacity")
        return alphaToString(PWINDOW->m_ruleApplicator->alpha(), true);
    else if (PROP == "opacity_inactive")
        return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), true);
    else if (PROP == "opacity_fullscreen")
        return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), true);
    else if (PROP == "opacity_override")
        return alphaToString(PWINDOW->m_ruleApplicator->alpha(), false);
    else if (PROP == "opacity_inactive_override")
        return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), false);
    else if (PROP == "opacity_fullscreen_override")
        return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), false);
    else if (PROP == "active_border_color")
        return borderColorToString(true);
    else if (PROP == "inactive_border_color")
        return borderColorToString(false);
    else if (PROP == "allows_input")
        return windowPropToString(PWINDOW->m_ruleApplicator->allowsInput());
    else if (PROP == "decorate")
        return windowPropToString(PWINDOW->m_ruleApplicator->decorate());
    else if (PROP == "focus_on_activate")
        return windowPropToString(PWINDOW->m_ruleApplicator->focusOnActivate());
    else if (PROP == "keep_aspect_ratio")
        return windowPropToString(PWINDOW->m_ruleApplicator->keepAspectRatio());
    else if (PROP == "nearest_neighbor")
        return windowPropToString(PWINDOW->m_ruleApplicator->nearestNeighbor());
    else if (PROP == "no_anim")
        return windowPropToString(PWINDOW->m_ruleApplicator->noAnim());
    else if (PROP == "no_blur")
        return windowPropToString(PWINDOW->m_ruleApplicator->noBlur());
    else if (PROP == "no_dim")
        return windowPropToString(PWINDOW->m_ruleApplicator->noDim());
    else if (PROP == "no_focus")
        return windowPropToString(PWINDOW->m_ruleApplicator->noFocus());
    else if (PROP == "no_max_size")
        return windowPropToString(PWINDOW->m_ruleApplicator->noMaxSize());
    else if (PROP == "no_shadow")
        return windowPropToString(PWINDOW->m_ruleApplicator->noShadow());
    else if (PROP == "no_shortcuts_inhibit")
        return windowPropToString(PWINDOW->m_ruleApplicator->noShortcutsInhibit());
    else if (PROP == "opaque")
        return windowPropToString(PWINDOW->m_ruleApplicator->opaque());
    else if (PROP == "dim_around")
        return windowPropToString(PWINDOW->m_ruleApplicator->dimAround());
    else if (PROP == "force_rgbx")
        return windowPropToString(PWINDOW->m_ruleApplicator->RGBX());
    else if (PROP == "sync_fullscreen")
        return windowPropToString(PWINDOW->m_ruleApplicator->syncFullscreen());
    else if (PROP == "immediate")
        return windowPropToString(PWINDOW->m_ruleApplicator->tearing());
    else if (PROP == "xray")
        return windowPropToString(PWINDOW->m_ruleApplicator->xray());
    else if (PROP == "render_unfocused")
        return windowPropToString(PWINDOW->m_ruleApplicator->renderUnfocused());
    else if (PROP == "no_follow_mouse")
        return windowPropToString(PWINDOW->m_ruleApplicator->noFollowMouse());
    else if (PROP == "no_screen_share")
        return windowPropToString(PWINDOW->m_ruleApplicator->noScreenShare());
    else if (PROP == "no_vrr")
        return windowPropToString(PWINDOW->m_ruleApplicator->noVRR());
    else if (PROP == "no_auto_hdr")
        return windowPropToString(PWINDOW->m_ruleApplicator->noAutoHDR());
    else if (PROP == "persistent_size")
        return windowPropToString(PWINDOW->m_ruleApplicator->persistentSize());
    else if (PROP == "stay_focused")
        return windowPropToString(PWINDOW->m_ruleApplicator->stayFocused());
    else if (PROP == "idle_inhibit")
        return windowPropToString(PWINDOW->m_ruleApplicator->idleInhibitMode());
    else if (PROP == "border_size")
        return windowPropToString(PWINDOW->m_ruleApplicator->borderSize());
    else if (PROP == "rounding")
        return windowPropToString(PWINDOW->m_ruleApplicator->rounding());
    else if (PROP == "rounding_power")
        return windowPropToString(PWINDOW->m_ruleApplicator->roundingPower());
    else if (PROP == "scroll_mouse")
        return windowPropToString(PWINDOW->m_ruleApplicator->scrollMouse());
    else if (PROP == "scroll_touchpad")
        return windowPropToString(PWINDOW->m_ruleApplicator->scrollTouchpad());

    return "prop not found";
}

static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
    std::string curitem = "";

    auto        nextItem = [&]() {
        auto idx = request.find_first_of(' ');

        if (idx != std::string::npos) {
            curitem = request.substr(0, idx);
            request = request.substr(idx + 1);
        } else {
            curitem = request;
            request = "";
        }

        curitem = trim(curitem);
    };

    nextItem();
    nextItem();

    const auto VAR = Config::mgr()->getConfigValue(curitem);

    if (!VAR.dataptr)
        return "no such option";

    const auto VAL  = VAR.dataptr;
    const auto TYPE = std::type_index(*VAR.type);

    if (format == FORMAT_NORMAL) {
        if (TYPE == typeid(Config::INTEGER))
            return std::format("int: {}\nset: {}", **rc<Config::INTEGER* const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(Config::BOOL))
            return std::format("bool: {}\nset: {}", **rc<Config::BOOL* const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(Config::FLOAT))
            return std::format("float: {:2f}\nset: {}", **rc<Config::FLOAT* const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(Config::VEC2))
            return std::format("vec2: [{}, {}]\nset: {}", (*rc<Config::VEC2* const*>(VAL))->x, (*rc<Config::VEC2* const*>(VAL))->y, VAR.setByUser);
        else if (TYPE == typeid(Hyprlang::VEC2))
            return std::format("vec2: [{}, {}]\nset: {}", (*rc<Config::VEC2* const*>(VAL))->x, (*rc<Config::VEC2* const*>(VAL))->y, VAR.setByUser);
        else if (TYPE == typeid(Hyprlang::STRING))
            return std::format("str: {}\nset: {}", *rc<Hyprlang::STRING const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(Config::STRING))
            return std::format("str: {}\nset: {}", **rc<Config::STRING* const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(void*))
            return std::format("custom type: {}\nset: {}", rc<Config::IComplexConfigValue*>((*rc<Hyprlang::CUSTOMTYPE* const*>(VAL))->getData())->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::IComplexConfigValue))
            return std::format("custom type: {}\nset: {}", (*rc<Config::IComplexConfigValue* const*>(VAL))->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::CCssGapData))
            return std::format("css gap data: {}\nset: {}", (*rc<Config::CCssGapData* const*>(VAL))->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::CGradientValueData))
            return std::format("gradient data: {}\nset: {}", (*rc<Config::CGradientValueData* const*>(VAL))->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::CFontWeightConfigValueData))
            return std::format("font weight data: {}\nset: {}", (*rc<Config::CFontWeightConfigValueData* const*>(VAL))->toString(), VAR.setByUser);
    } else {
        if (TYPE == typeid(Config::INTEGER))
            return std::format(R"({{"option": "{}", "int": {}, "set": {} }})", curitem, **rc<Config::INTEGER* const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(Config::BOOL))
            return std::format(R"({{"option": "{}", "bool": {}, "set": {} }})", curitem, (**rc<Config::BOOL* const*>(VAL)) ? "true" : "false", VAR.setByUser);
        else if (TYPE == typeid(Config::FLOAT))
            return std::format(R"({{"option": "{}", "float": {:2f}, "set": {} }})", curitem, **rc<Config::FLOAT* const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(Config::VEC2))
            return std::format(R"({{"option": "{}", "vec2": [{},{}], "set": {} }})", curitem, (*rc<Config::VEC2* const*>(VAL))->x, (*rc<Config::VEC2* const*>(VAL))->y,
                               VAR.setByUser);
        else if (TYPE == typeid(Hyprlang::VEC2))
            return std::format(R"({{"option": "{}", "vec2": [{},{}], "set": {} }})", curitem, (*rc<Config::VEC2* const*>(VAL))->x, (*rc<Config::VEC2* const*>(VAL))->y,
                               VAR.setByUser);
        else if (TYPE == typeid(Hyprlang::STRING))
            return std::format(R"({{"option": "{}", "str": "{}", "set": {} }})", curitem, escapeJSONStrings(*rc<Hyprlang::STRING const*>(VAL)), VAR.setByUser);
        else if (TYPE == typeid(Config::STRING))
            return std::format(R"({{"option": "{}", "str": "{}", "set": {} }})", curitem, **rc<Config::STRING* const*>(VAL), VAR.setByUser);
        else if (TYPE == typeid(void*))
            return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem,
                               rc<Config::IComplexConfigValue*>((*rc<Hyprlang::CUSTOMTYPE* const*>(VAL))->getData())->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::IComplexConfigValue))
            return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem, (*rc<Config::IComplexConfigValue* const*>(VAL))->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::CCssGapData))
            return std::format(R"({{"option": "{}", "css": "{}", "set": {} }})", curitem, (*rc<Config::CCssGapData* const*>(VAL))->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::CGradientValueData))
            return std::format(R"({{"option": "{}", "gradient": "{}", "set": {} }})", curitem, (*rc<Config::CGradientValueData* const*>(VAL))->toString(), VAR.setByUser);
        else if (TYPE == typeid(Config::CFontWeightConfigValueData))
            return std::format(R"({{"option": "{}", "font_weight": "{}", "set": {} }})", curitem, (*rc<Config::CFontWeightConfigValueData* const*>(VAL))->toString(),
                               VAR.setByUser);
    }

    return "invalid type (internal error)";
}

static std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) {
    CVarList   vars(request, 0, ' ');
    const auto PWINDOW = Desktop::viewState()->query().selector(vars[1]).runWindow();

    if (!PWINDOW)
        return "none";

    std::string result = "";
    if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
        result += "[";
        for (auto const& wd : PWINDOW->m_windowDecorations) {
            result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},";
        }

        trimTrailingComma(result);
        result += "]";
    } else {
        result = +"Decoration\tPriority\n";
        for (auto const& wd : PWINDOW->m_windowDecorations) {
            result += wd->getDisplayName() + "\t" + std::to_string(wd->getPositioningInfo().priority) + "\n";
        }
    }

    return result;
}

static std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
    CVarList vars(request, 0, ' ');

    if (vars.size() < 2)
        return "not enough args";

    const auto MODE = vars[1];

    bool       added = false;

    if (!vars[3].empty()) {
        for (auto const& m : State::monitorState()->allMonitors()) {
            if (m->m_name == vars[3])
                return "Name already taken";
        }
    }

    if (MODE == "create" || MODE == "add") {
        if (State::monitorState()->query().name(vars[3]).run())
            return "A real monitor already uses that name.";

        for (auto const& impl : g_pCompositor->m_aqBackend->getImplementations() | std::views::reverse) {
            auto type = impl->type();

            if (type == Aquamarine::AQ_BACKEND_HEADLESS && (vars[2] == "headless" || vars[2] == "auto")) {
                added = true;
                impl->createOutput(vars[3]);
                break;
            }

            if (type == Aquamarine::AQ_BACKEND_WAYLAND && (vars[2] == "wayland" || vars[2] == "auto")) {
                added = true;
                impl->createOutput(vars[3]);
                break;
            }
        }

        if (!added)
            return "no backend replied to the request";

    } else if (MODE == "destroy" || MODE == "remove") {
        const auto PMONITOR = State::monitorState()->query().name(vars[2]).run();

        if (!PMONITOR)
            return "output not found";

        if (!PMONITOR->m_createdByUser)
            return "cannot remove a real display. Use the monitor keyword.";

        PMONITOR->m_output->destroy();
    }

    return "ok";
}

static std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
    CVarList vars(request, 0, ' ');

    if (vars.size() < 2)
        return "not enough args";

    const auto OPERATION = vars[1];
    const auto PATH      = vars[2];

    if (OPERATION == "load") {
        if (vars.size() < 3)
            return "not enough args";

        g_pHyprCtl->m_currentRequestParams.pendingPromise = CPromise<std::string>::make([PATH](SP<CPromiseResolver<std::string>> resolver) {
            g_pPluginSystem->loadPlugin(PATH)->then([resolver, PATH](SP<CPromiseResult<CPlugin*>> result) {
                if (result->hasError()) {
                    resolver->reject(result->error());
                    return;
                }

                resolver->resolve("ok");
            });
        });

        return "ok";
    } else if (OPERATION == "unload") {
        if (vars.size() < 3)
            return "not enough args";

        const auto PLUGIN = g_pPluginSystem->getPluginByPath(PATH);

        if (!PLUGIN)
            return "plugin not loaded";

        g_pPluginSystem->unloadPlugin(PLUGIN);
    } else if (OPERATION == "list") {
        const auto  PLUGINS = g_pPluginSystem->getAllPlugins();
        std::string result  = "";

        if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
            result += "[";

            if (PLUGINS.empty())
                return "[]";

            for (auto const& p : PLUGINS) {
                result += std::format(
                    R"#(
{{
    "name": "{}",
    "author": "{}",
    "handle": "{:x}",
    "version": "{}",
    "description": "{}"
}},)#",
                    escapeJSONStrings(p->m_name), escapeJSONStrings(p->m_author), rc<uintptr_t>(p->m_handle), escapeJSONStrings(p->m_version), escapeJSONStrings(p->m_description));
            }
            trimTrailingComma(result);
            result += "]";
        } else {
            if (PLUGINS.empty())
                return "no plugins loaded";

            for (auto const& p : PLUGINS) {
                result += std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->m_name, p->m_author, rc<uintptr_t>(p->m_handle), p->m_version,
                                      p->m_description);
            }
        }

        return result;
    } else {
        return "unknown opt";
    }

    return "ok";
}

static std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
    CVarList vars(request, 0, ' ');

    if (vars.size() < 5)
        return "not enough args";

    const auto ICON = vars[1];

    if (!isNumber(ICON))
        return "invalid arg 1";

    int icon = -1;
    try {
        icon = std::stoi(ICON);
    } catch (std::exception& e) { return "invalid arg 1"; }

    if (icon > ICON_NONE || icon < 0)
        icon = ICON_NONE;

    const auto TIME = vars[2];
    int        time = 0;
    try {
        time = std::stoi(TIME);
    } catch (std::exception& e) { return "invalid arg 2"; }

    const auto COLOR_RESULT = Config::ParserUtils::parseColor(vars[3]);
    if (!COLOR_RESULT)
        return "invalid arg 3";
    CHyprColor color = *COLOR_RESULT;

    size_t     msgidx   = 4;
    float      fontsize = 13.f;
    if (vars[msgidx].length() > 9 && vars[msgidx].compare(0, 9, "fontsize:") == 0) {
        const auto FONTSIZE = vars[msgidx].substr(9);

        if (!isNumber(FONTSIZE, true))
            return "invalid fontsize kwarg";

        try {
            fontsize = std::stoi(FONTSIZE);
        } catch (std::exception& e) { return "invalid fontsize karg"; }

        ++msgidx;
    }

    if (vars.size() <= msgidx)
        return "not enough args";

    const auto MESSAGE = vars.join(" ", msgidx);

    Notification::overlay()->addNotification(MESSAGE, color, time, sc<eIcons>(icon), fontsize);

    return "ok";
}

static std::string dispatchDismissNotify(eHyprCtlOutputFormat format, std::string request) {
    CVarList vars(request, 0, ' ');

    int      amount = -1;

    if (vars.size() > 1) {
        const auto AMOUNT = vars[1];
        if (!isNumber(AMOUNT))
            return "invalid arg 1";

        try {
            amount = std::stoi(AMOUNT);
        } catch (std::exception& e) { return "invalid arg 1"; }
    }

    Notification::overlay()->dismissNotifications(amount);

    return "ok";
}

static std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
    std::string lockedStr = g_pSessionLockManager->isSessionLocked() ? "true" : "false";
    if (format == eHyprCtlOutputFormat::FORMAT_JSON)
        lockedStr = std::format(R"#(
{{
    "locked": {}
}}
)#",
                                lockedStr);
    return lockedStr;
}

static std::string getDescriptions(eHyprCtlOutputFormat format, std::string request) {
    return Config::Values::getAsJson();
}

static std::string submapRequest(eHyprCtlOutputFormat format, std::string request) {
    std::string submap = g_pKeybindManager->getCurrentSubmap().name;
    if (submap.empty())
        submap = "default";

    return format == FORMAT_JSON ? std::format("\"{}\"\n", escapeJSONStrings(submap)) : (submap + "\n");
}

static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
    CVarList vars(request, 0, ' ');

    if (vars.size() > 2)
        return "too many args";

    if (g_pHyprOpenGL && g_pHyprRenderer->reloadShaders(vars.size() == 2 ? vars[1] : ""))
        return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
    else
        return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
}

CHyprCtl::CHyprCtl() {
    registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
    registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
    registerCommand(SHyprCtlCommand{"activeworkspace", true, activeWorkspaceRequest});
    registerCommand(SHyprCtlCommand{"clients", true, clientsRequest});
    registerCommand(SHyprCtlCommand{"kill", true, killRequest});
    registerCommand(SHyprCtlCommand{"activewindow", true, activeWindowRequest});
    registerCommand(SHyprCtlCommand{"layers", true, layersRequest});
    registerCommand(SHyprCtlCommand{"version", true, versionRequest});
    registerCommand(SHyprCtlCommand{"devices", true, devicesRequest});
    registerCommand(SHyprCtlCommand{"splash", true, splashRequest});
    registerCommand(SHyprCtlCommand{"cursorpos", true, cursorPosRequest});
    registerCommand(SHyprCtlCommand{"binds", true, bindsRequest});
    registerCommand(SHyprCtlCommand{"globalshortcuts", true, globalShortcutsRequest});
    registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest});
    registerCommand(SHyprCtlCommand{"animations", true, animationsRequest});
    registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
    registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
    registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
    registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
    registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
    registerCommand(SHyprCtlCommand{"status", true, statusRequest});

    registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = false, .fn = reloadShaders});
    registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
    registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
    registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
    registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
    registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
    registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp});
    registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
    registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
    registerCommand(SHyprCtlCommand{"output", false, dispatchOutput});
    registerCommand(SHyprCtlCommand{"dispatch", false, dispatchRequest});
    registerCommand(SHyprCtlCommand{"keyword", false, dispatchKeyword});
    registerCommand(SHyprCtlCommand{"setcursor", false, dispatchSetCursor});
    registerCommand(SHyprCtlCommand{"getoption", false, dispatchGetOption});
    registerCommand(SHyprCtlCommand{"decorations", false, decorationRequest});
    registerCommand(SHyprCtlCommand{"[[BATCH]]", false, dispatchBatch});
    registerCommand(SHyprCtlCommand{"eval", false, evalRequest});
    registerCommand(SHyprCtlCommand{"repl", false, evalRequest});
    startHyprCtlSocket();
}

CHyprCtl::~CHyprCtl() {
    if (m_eventSource)
        wl_event_source_remove(m_eventSource);
    if (!m_socketPath.empty())
        unlink(m_socketPath.c_str());
}

SP<SHyprCtlCommand> CHyprCtl::registerCommand(SHyprCtlCommand cmd) {
    return m_commands.emplace_back(makeShared<SHyprCtlCommand>(cmd));
}

void CHyprCtl::unregisterCommand(const SP<SHyprCtlCommand>& cmd) {
    std::erase(m_commands, cmd);
}

std::string CHyprCtl::getReply(std::string request) {
    auto format                          = eHyprCtlOutputFormat::FORMAT_NORMAL;
    bool reloadAll                       = false;
    m_currentRequestParams.all           = false;
    m_currentRequestParams.sysInfoConfig = false;

    // process flags for non-batch requests
    if (!request.starts_with("[[BATCH]]") && request.contains("/")) {
        long unsigned int sepIndex = 0;
        for (const auto& c : request) {
            if (c == '/') { // stop at separator
                break;
            }

            // after whitespace assume the first word as a keyword,
            // so its value can have slashes (e.g., a path)
            if (c == ' ') {
                sepIndex = request.size();
                break;
            }

            sepIndex++;

            if (c == 'j')
                format = eHyprCtlOutputFormat::FORMAT_JSON;
            else if (c == 'r')
                reloadAll = true;
            else if (c == 'a')
                m_currentRequestParams.all = true;
            else if (c == 'c')
                m_currentRequestParams.sysInfoConfig = true;
        }

        if (sepIndex < request.size())
            request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string
    }

    std::string result = "";

    // parse exact cmds first, then non-exact.
    for (auto const& cmd : m_commands) {
        if (!cmd->exact)
            continue;

        if (cmd->name == request) {
            result = cmd->fn(format, request);
            break;
        }
    }

    if (result.empty())
        for (auto const& cmd : m_commands) {
            if (cmd->exact)
                continue;

            if (request.starts_with(cmd->name)) {
                result = cmd->fn(format, request);
                break;
            }
        }

    if (result.empty())
        return "unknown request";

    if (reloadAll) {
        Config::monitorRuleMgr()->scheduleReload();
        Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts();

        g_pInputManager->setKeyboardLayout();     // update kb layout
        g_pInputManager->setPointerConfigs();     // update mouse cfgs
        g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs
        g_pInputManager->setTabletConfigs();      // update tablets

        g_pHyprRenderer->m_reloadScreenShader = true;

        for (auto const& m : State::monitorState()->monitors()) {
            if (m)
                m->m_blurFBDirty = true;
        }

        for (auto const& w : Desktop::windowState()->windows()) {
            if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible())
                continue;

            Desktop::Rule::ruleEngine()->updateAllRules();
        }

        for (const auto& ws : State::workspaceState()->workspaces()) {
            if (!ws)
                continue;

            ws->updateWindows();
            ws->updateWindowData();
            ws->updateWindowDecos();
        }

        for (auto const& m : State::monitorState()->monitors()) {
            g_pHyprRenderer->damageMonitor(m);
        }
    }

    return result;
}

std::string CHyprCtl::makeDynamicCall(const std::string& input) {
    return getReply(input);
}

static bool successWrite(int fd, const std::string& data, bool needLog = true) {
    size_t                 totalWritten = 0;
    size_t                 remaining    = data.length();
    size_t                 waitsDone    = 0;
    constexpr const size_t MAX_WAITS    = 20; // 2000µs = 2ms

    while (totalWritten < data.length()) {
        ssize_t written = write(fd, data.c_str() + totalWritten, remaining);

        if (waitsDone > MAX_WAITS) {
            Log::logger->log(Log::ERR, "Couldn't write to socket. Buffer was full and the client couldn't read in time.");
            return false;
        }

        if (written < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // socket buffer full, wait a bit and retry
                std::this_thread::sleep_for(std::chrono::microseconds(100));
                waitsDone++;
                continue;
            }
            if (needLog)
                Log::logger->log(Log::ERR, "Couldn't write to socket. Error: {}", strerror(errno));
            return false;
        }

        waitsDone = 0;

        totalWritten += written;
        remaining -= written;
    }

    return true;
}

static void runWritingDebugLogThread(const int conn) {
    using namespace std::chrono_literals;
    Log::logger->log(Log::DEBUG, "In followlog thread, got connection, start writing: {}", conn);
    //will be finished, when reading side close connection
    std::thread([conn]() {
        while (Log::SRollingLogFollow::get().isRunning()) {
            if (Log::SRollingLogFollow::get().isEmpty(conn)) {
                std::this_thread::sleep_for(1000ms);
                continue;
            }

            auto line = Log::SRollingLogFollow::get().getLog(conn);
            if (!successWrite(conn, line))
                // We cannot write, when connection is closed. So thread will successfully exit by itself
                break;

            std::this_thread::sleep_for(100ms);
        }
        close(conn);
        Log::SRollingLogFollow::get().stopFor(conn);
    }).detach();
}

static bool isFollowUpRollingLogRequest(const std::string& request) {
    return request.contains("rollinglog") && request.contains("f");
}

static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
    if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
        return 0;

    if (!g_pHyprCtl->m_socketFD.isValid())
        return 0;

    sockaddr_in            clientAddress;
    socklen_t              clientSize = sizeof(clientAddress);

    const auto             ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_socketFD.get(), rc<sockaddr*>(&clientAddress), &clientSize, SOCK_CLOEXEC);

    std::array<char, 1024> readBuffer;

    // try to get creds
    CRED_T   creds;
    uint32_t len = sizeof(creds);
    if (getsockopt(ACCEPTEDCONNECTION, CRED_LVL, CRED_OPT, &creds, &len) == -1)
        Log::logger->log(Log::ERR, "Hyprctl: failed to get peer creds");
    else {
        g_pHyprCtl->m_currentRequestParams.pid = creds.CRED_PID;
        Log::logger->log(Log::DEBUG, "Hyprctl: new connection from pid {}", creds.CRED_PID);
    }

    //
    pollfd pollfds[1] = {
        {
            .fd     = ACCEPTEDCONNECTION,
            .events = POLLIN,
        },
    };

    int ret = poll(pollfds, 1, 5000);

    if (ret <= 0) {
        close(ACCEPTEDCONNECTION);
        return 0;
    }

    std::string request;
    while (true) {
        readBuffer.fill(0);
        auto messageSize = read(ACCEPTEDCONNECTION, readBuffer.data(), 1023);
        if (messageSize < 1)
            break;
        std::string recvd = readBuffer.data();
        request += recvd;
        if (messageSize < 1023)
            break;
    }

    std::string reply = "";

    try {
        reply = g_pHyprCtl->getReply(request);
    } catch (std::exception& e) {
        Log::logger->log(Log::ERR, "Error in request: {}", e.what());
        reply = "Err: " + std::string(e.what());
    }

    if (g_pHyprCtl->m_currentRequestParams.pendingPromise) {
        // we have a promise pending
        g_pHyprCtl->m_currentRequestParams.pendingPromise->then([ACCEPTEDCONNECTION, request](SP<CPromiseResult<std::string>> result) {
            const auto RES = result->hasError() ? result->error() : result->result();
            successWrite(ACCEPTEDCONNECTION, RES);

            // No rollinglog or ensureMonitor here. These are only for plugins for now.

            close(ACCEPTEDCONNECTION);
        });

        g_pHyprCtl->m_currentRequestParams.pendingPromise.reset();
    } else {
        successWrite(ACCEPTEDCONNECTION, reply);

        if (isFollowUpRollingLogRequest(request)) {
            Log::logger->log(Log::DEBUG, "Followup rollinglog request received. Starting thread to write to socket.");
            Log::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION);
            runWritingDebugLogThread(ACCEPTEDCONNECTION);
            Log::logger->log(Log::DEBUG, Log::SRollingLogFollow::get().debugInfo());
        } else
            close(ACCEPTEDCONNECTION);

        g_pHyprCtl->m_currentRequestParams.pid = 0;
    }

    return 0;
}

void CHyprCtl::startHyprCtlSocket() {
    m_socketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};

    if (!m_socketFD.isValid()) {
        Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
        return;
    }

    sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX};

    m_socketPath = g_pCompositor->m_instancePath + "/.socket.sock";

    snprintf(SERVERADDRESS.sun_path, sizeof(SERVERADDRESS.sun_path), "%s", m_socketPath.c_str());

    if (bind(m_socketFD.get(), rc<sockaddr*>(&SERVERADDRESS), SUN_LEN(&SERVERADDRESS)) < 0) {
        Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
        return;
    }

    // 10 max queued.
    listen(m_socketFD.get(), 10);

    Log::logger->log(Log::DEBUG, "Hypr socket started at {}", m_socketPath);

    m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_socketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
}
