#include "ukuiwindowhelper.h"
#include "qapplication.h"
#include "qguiapplication.h"
#include "ukuidisplay_p.h"

#include "plasma-shell.h"
#include "ukui-shell-v1.h"

#include <QDebug>
#include <QGuiApplication>
#include <QPair>
#include <QScreen>
#include <QVariant>
#include <QWidget>
#include <QWindow>

#include <QtGui/qpa/qplatformnativeinterface.h>
#include <QtGui/qpa/qplatformwindow_p.h>

// #include <QX11Info>
#include <limits.h>
#include <xcb/xcb.h>

#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>

#include <optional>

class X11Display
{
public:
    static Display *display();
    static void closeDisplay();

private:
    static Display *s_display;
};

Display *X11Display::s_display = nullptr;

Display *X11Display::display()
{
    if (!s_display) {
        s_display = XOpenDisplay(nullptr);
        if (s_display) {
            atexit(closeDisplay);
        }
    }
    return s_display;
}

void X11Display::closeDisplay()
{
    if (s_display) {
        XCloseDisplay(s_display);
        s_display = nullptr;
    }
}

static QMap<QNativeInterface::Private::QWaylandWindow *, wayland::org_kde_plasma_surface_t> s_plasmaSurfaceMap;
static QMap<QNativeInterface::Private::QWaylandWindow *, wayland::ukui_surface_v1_t> s_ukuiSurfaceMap;
static QMap<QNativeInterface::Private::QWaylandWindow *, wayland::ukui_decoration_v1_t> s_ukuiDecorationMap;

class UkuiWindowHelperPrivate : public QObject
{
public:
    explicit UkuiWindowHelperPrivate(UkuiWindowHelper *windowHelper, QWidget *widget);
    explicit UkuiWindowHelperPrivate(UkuiWindowHelper *windowHelper, QWindow *window);
    ~UkuiWindowHelperPrivate();

    void removeTitleBar();
    void setWindowRole(UkuiWindowHelper::WindowRole role);
    void setWindowState(UkuiWindowHelper::States state);
    void setWindowIcon(const QString &iconName);
    void setSkipTaskBar(bool skip);
    void setSkipSwitcher(bool skip);
    void setPanelAutoHide(bool autoHide);
    void grabKeyboard();
    void openUnderCursor(QPoint pos);
    void setBlurEffect(QRegion region, int level = 0, bool enable = true);
    void setSlideEffect(UkuiWindowHelper::Position position, int offset = 0, bool enable = true);
    void showTileFlyout(QRect rect);
    void setDecorationCompoents(UkuiWindowHelper::DecorationComponents components);
    void requestGetCurrentScreen();
    QScreen *currentScreen() const;
    void setOpaqueRegion(QRegion);
    void setPosition(QPoint pos);
    QPoint position() const;
    void setWindowStateEx(UkuiWindowHelper::States mask, UkuiWindowHelper::States states);

private:
    bool eventFilter(QObject *obj, QEvent *event) override;

    void ukui_shell_v1_current_output(const std::string &output_name, const std::string &seat_name);
    void ukui_shell_v1_done();

    wayland::org_kde_plasma_surface_role getPlasmaSurfaceRole(UkuiWindowHelper::WindowRole role);
    wayland::ukui_surface_v1_role getUkuiSurfaceRole(UkuiWindowHelper::WindowRole role);

    void initShellSurface();
    void sendPendingRequests();

    QNativeInterface::Private::QWaylandWindow *m_waylandWindow = nullptr;

    wayland::ukui_shell_v1_t m_ukuiShellv1;
    wayland::ukui_surface_v1_t m_ukuiSurfacev1;
    wayland::ukui_decoration_v1_t m_ukuiDecorationv1;

    wayland::org_kde_plasma_shell_t m_plasmaShell;
    wayland::org_kde_plasma_surface_t m_plasmaSurface;

    UkuiWindowHelper *m_windowHelper = nullptr;
    QWidget *m_widget = nullptr;
    QWindow *m_window = nullptr;
    QString m_currentOutputName;
    QString m_currentSeatName;
    QScreen *m_currentScreen = nullptr;

    std::optional<QPoint> m_currentPos;
    std::optional<UkuiWindowHelper::States> m_currentStatesMask;
    std::optional<UkuiWindowHelper::States> m_currentStates;
    std::optional<bool> m_titleBarRemoved;
    std::optional<QString> m_iconName;
    std::optional<bool> m_skipTaskBar;
    std::optional<bool> m_skipSwitcher;
    std::optional<bool> m_panelAutoHide;
    std::optional<bool> m_grabKeyboard;
    std::optional<QPoint> m_openUnderCursor;
    std::optional<UkuiWindowHelper::WindowRole> m_windowRole;
};

UkuiWindowHelperPrivate::UkuiWindowHelperPrivate(UkuiWindowHelper *windowHelper, QWidget *widget)
    : m_windowHelper(windowHelper)
    , m_widget(widget)
{
    if (!m_widget) {
        qWarning() << "Creating a UkuiWindowHelperPrivate for a null QWidget(nullptr)!";
    }
    m_widget->installEventFilter(this);
    m_ukuiShellv1 = wayland::ukui_shell_v1_t(ukuiDisplay()->bind(wayland::detail::ukui_shell_v1_interface.name, m_ukuiShellv1, wayland::detail::ukui_shell_v1_interface.version));
    if (m_ukuiShellv1) {
        m_ukuiShellv1.on_current_output() = std::bind(&UkuiWindowHelperPrivate::ukui_shell_v1_current_output, this, std::placeholders::_1, std::placeholders::_2);
        m_ukuiShellv1.on_done() = std::bind(&UkuiWindowHelperPrivate::ukui_shell_v1_done, this);
    }

    m_plasmaShell = wayland::org_kde_plasma_shell_t(ukuiDisplay()->bind(wayland::detail::org_kde_plasma_shell_interface.name, m_plasmaShell, wayland::detail::org_kde_plasma_shell_interface.version));
}

UkuiWindowHelperPrivate::UkuiWindowHelperPrivate(UkuiWindowHelper *windowHelper, QWindow *window)
    : m_windowHelper(windowHelper)
    , m_window(window)
{
    if (!m_window) {
        qWarning() << "Creating a UkuiWindowHelperPrivate for a QWindow(nullptr)!";
    }
    m_window->installEventFilter(this);
    m_ukuiShellv1 = wayland::ukui_shell_v1_t(ukuiDisplay()->bind(wayland::detail::ukui_shell_v1_interface.name, m_ukuiShellv1, wayland::detail::ukui_shell_v1_interface.version));
    if (m_ukuiShellv1) {
        m_ukuiShellv1.on_current_output() = std::bind(&UkuiWindowHelperPrivate::ukui_shell_v1_current_output, this, std::placeholders::_1, std::placeholders::_2);
        m_ukuiShellv1.on_done() = std::bind(&UkuiWindowHelperPrivate::ukui_shell_v1_done, this);
    }

    m_plasmaShell = wayland::org_kde_plasma_shell_t(ukuiDisplay()->bind(wayland::detail::org_kde_plasma_shell_interface.name, m_plasmaShell, wayland::detail::org_kde_plasma_shell_interface.version));
}

UkuiWindowHelperPrivate::~UkuiWindowHelperPrivate()
{
}

bool UkuiWindowHelperPrivate::eventFilter(QObject *obj, QEvent *event)
{
    if (obj != m_widget && obj != m_window) {
        return QObject::eventFilter(obj, event);
    }

    if (event->type() == QEvent::PlatformSurface) {
        if (m_waylandWindow)
            return QObject::eventFilter(obj, event);

        QWindow *window = m_window ? m_window : m_widget->windowHandle();
        if (!window)
            return QObject::eventFilter(obj, event);

        m_waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
        if (!m_waylandWindow)
            return QObject::eventFilter(obj, event);

        initShellSurface();

        connect(m_waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed, m_windowHelper, [this]() {
            s_plasmaSurfaceMap.remove(m_waylandWindow);
            s_ukuiSurfaceMap.remove(m_waylandWindow);

            m_plasmaSurface = nullptr;
            m_ukuiSurfacev1 = nullptr;
            m_ukuiDecorationv1 = nullptr;
            m_waylandWindow = nullptr;
        });
    }

    return QObject::eventFilter(obj, event);
}

void UkuiWindowHelperPrivate::removeTitleBar()
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_no_titlebar", true);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_no_titlebar", true);
    }

    if (m_ukuiDecorationv1) {
        m_ukuiDecorationv1.set_no_titlebar(1);
    }
    m_titleBarRemoved = true;

    if (QGuiApplication::platformName() == "xcb") {
        Display *display = X11Display::display();
        if (!display) {
            return;
        }

        Atom atom = XInternAtom(display, "_KWIN_UKUI_DECORAION", False);
        if (atom == None) {
            return;
        }

        unsigned long data = 1;
        Window winId = m_widget ? m_widget->winId() : (m_window ? m_window->winId() : 0);
        if (winId != 0) {
            XChangeProperty(display, winId, atom, atom, 32, PropModeReplace, reinterpret_cast<unsigned char *>(&data), 1);
            XFlush(display);
        }
    }
}

static QMap<UkuiWindowHelper::WindowRole, QStringList> roleMap = {
    {UkuiWindowHelper::WindowRole::Normal, {"_NET_WM_WINDOW_TYPE_NORMAL"}},
    {UkuiWindowHelper::WindowRole::Desktop, {"_NET_WM_WINDOW_TYPE_DESKTOP"}},
    {UkuiWindowHelper::WindowRole::Panel, {"_UKUI_NET_WM_WINDOW_TYPE_PANEL", "_NET_WM_WINDOW_TYPE_DOCK"}},
    {UkuiWindowHelper::WindowRole::OnScreenDisplay, {"_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY", "_NET_WM_WINDOW_TYPE_NOTIFICATION"}},
    {UkuiWindowHelper::WindowRole::Notification, {"_NET_WM_WINDOW_TYPE_NOTIFICATION", "_NET_WM_WINDOW_TYPE_UTILITY"}},
    {UkuiWindowHelper::WindowRole::Tooltip, {"_NET_WM_WINDOW_TYPE_TOOLTIP"}},
    {UkuiWindowHelper::WindowRole::CriticalNotification, {"_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION", "_NET_WM_WINDOW_TYPE_NOTIFICATION"}},
    {UkuiWindowHelper::WindowRole::AppletPopup, {"_NET_WM_WINDOW_TYPE_APPLET_POPUP"}},
    {UkuiWindowHelper::WindowRole::ScreenLock, {"_UKUI_NET_WM_WINDOW_TYPE_SCREENLOCK"}},
    {UkuiWindowHelper::WindowRole::Watermark, {"_UKUI_NET_WM_WINDOW_TYPE_WATERMARK"}},
    {UkuiWindowHelper::WindowRole::SystemWindow, {"_UKUI_NET_WM_WINDOW_TYPE_SYSTEMWINDOW"}},
    {UkuiWindowHelper::WindowRole::InputPanel, {"_UKUI_NET_WM_WINDOW_TYPE_INPUTPANEL"}},
    {UkuiWindowHelper::WindowRole::Logout, {"_UKUI_NET_WM_WINDOW_TYPE_LOGOUT"}},
    {UkuiWindowHelper::WindowRole::ScreenLockNotification, {"_UKUI_NET_WM_WINDOW_TYPE_SCREENLOCKNOTIFICATION"}},
    {UkuiWindowHelper::WindowRole::Switcher, {"_UKUI_NET_WM_WINDOW_TYPE_SWITCHER"}},
    {UkuiWindowHelper::WindowRole::Authentication, {"_UKUI_NET_WM_WINDOW_TYPE_AUTHENTICATION"}},
    {UkuiWindowHelper::WindowRole::Override, {"_NET_WM_WINDOW_TYPE_OVERRIDE", "_NET_WM_WINDOW_TYPE_NORMAL"}},
    {UkuiWindowHelper::WindowRole::Dialog, {"_NET_WM_WINDOW_TYPE_DIALOG"}},
    {UkuiWindowHelper::WindowRole::Menu, {"_NET_WM_WINDOW_TYPE_MENU"}},
    {UkuiWindowHelper::WindowRole::TopMenu, {"_KDE_NET_WM_WINDOW_TYPE_TOPMENU", "_NET_WM_WINDOW_TYPE_DOCK"}},
    {UkuiWindowHelper::WindowRole::ToolBar, {"_NET_WM_WINDOW_TYPE_TOOLBAR"}},
    {UkuiWindowHelper::WindowRole::Dock, {"_NET_WM_WINDOW_TYPE_DOCK"}},
    {UkuiWindowHelper::WindowRole::Utility, {"_NET_WM_WINDOW_TYPE_UTILITY", "_NET_WM_WINDOW_TYPE_DIALOG"}},
    {UkuiWindowHelper::WindowRole::Splash, {"_NET_WM_WINDOW_TYPE_SPLASH", "_NET_WM_WINDOW_TYPE_DOCK"}},
    {UkuiWindowHelper::WindowRole::DropdownMenu, {"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", "_NET_WM_WINDOW_TYPE_MENU"}},
    {UkuiWindowHelper::WindowRole::PopupMenu, {"_NET_WM_WINDOW_TYPE_POPUP_MENU", "_NET_WM_WINDOW_TYPE_MENU"}},
    {UkuiWindowHelper::WindowRole::ComboBox, {"_NET_WM_WINDOW_TYPE_COMBO"}},
    {UkuiWindowHelper::WindowRole::DNDIcon, {"_NET_WM_WINDOW_TYPE_DND"}},
};

void UkuiWindowHelperPrivate::setWindowRole(UkuiWindowHelper::WindowRole role)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_role_v1", qBound(0, (int)role, 100));
    } else if (m_window) {
        m_window->setProperty("ukui_surface_role_v1", qBound(0, (int)role, 100));
    }

    if (m_plasmaSurface) {
        m_plasmaSurface.set_role(static_cast<uint32_t>(getPlasmaSurfaceRole(role)));
        return;
    } else if (m_ukuiSurfacev1) {
        m_ukuiSurfacev1.set_role(static_cast<uint32_t>(getUkuiSurfaceRole(role)));
        return;
    }
    m_windowRole = role;

    if (QGuiApplication::platformName() == "xcb") {
        Display *display = X11Display::display();
        if (!display) {
            return;
        }

        Atom prop = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
        if (prop == None) {
            return;
        }

        QStringList types = roleMap.value(role);
        if (types.isEmpty()) {
            return;
        }

        QVector<Atom> atomList;
        for (const QString &type : types) {
            Atom atom = XInternAtom(display, type.toUtf8().constData(), False);
            if (atom != None) {
                atomList.append(atom);
            }
        }

        if (atomList.isEmpty()) {
            return;
        }

        Window win = m_widget ? m_widget->winId() : (m_window ? m_window->winId() : 0);
        if (!win)
            return;

        XChangeProperty(X11Display::display(), win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)atomList.constData(), atomList.size());
    }
}

void UkuiWindowHelperPrivate::setWindowState(UkuiWindowHelper::States states)
{
    QPair<uint32_t, uint32_t> pair(0x3ff, states);
    if (m_widget) {
        m_widget->setProperty("ukui_surface_state", QVariant::fromValue(pair));
    } else if (m_window) {
        m_window->setProperty("ukui_surface_state", QVariant::fromValue(pair));
    }

    setWindowStateEx(states, states);

    if (QGuiApplication::platformName() == "xcb") {
        Display *display = X11Display::display();
        if (!display) {
            return;
        }

        Atom prop = XInternAtom(display, "_NET_WM_STATE", False);
        if (prop == None) {
            return;
        }

        Window win = m_widget ? m_widget->winId() : (m_window ? m_window->winId() : 0);
        if (!win)
            return;

        Atom actualType;
        int actualFormat;
        unsigned long nItems, bytesAfter;
        Atom *currentAtoms = nullptr;

        QVector<Atom> finalAtoms;
        QStringList atomList = {"_NET_WM_STATE_ABOVE", "_NET_WM_STATE_BELOW"};

        if (Success == XGetWindowProperty(display, win, prop, 0, 1024, False, XA_ATOM, &actualType, &actualFormat, &nItems, &bytesAfter, (unsigned char **)&currentAtoms)) {
            for (unsigned long i = 0; i < nItems; ++i) {
                char *name = XGetAtomName(display, currentAtoms[i]);
                if (name) {
                    QString nameStr = QString::fromUtf8(name);
                    if (!atomList.contains(nameStr))
                        finalAtoms.append(currentAtoms[i]);
                    XFree(name);
                }
            }
            if (currentAtoms)
                XFree(currentAtoms);
        }

        if (states & UkuiWindowHelper::State::KeepAbove) {
            Atom atom = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
            if (atom != None) {
                finalAtoms.append(atom);
            }
        }
        if (states & UkuiWindowHelper::State::KeepBelow) {
            Atom atom = XInternAtom(display, "_NET_WM_STATE_BELOW", False);
            if (atom != None) {
                finalAtoms.append(atom);
            }
        }

        if (finalAtoms.isEmpty()) {
            return;
        }

        XChangeProperty(X11Display::display(), win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)finalAtoms.constData(), finalAtoms.size());
    }
}

void UkuiWindowHelperPrivate::setWindowIcon(const QString &iconName)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_icon", iconName);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_icon", iconName);
    }

    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.set_icon(iconName.toStdString());
    m_iconName = iconName;
}

void UkuiWindowHelperPrivate::setSkipTaskBar(bool skip)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_skip_taskbar", skip);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_skip_taskbar", skip);
    }

    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.set_skip_taskbar(skip ? 1 : 0);
    if (m_plasmaSurface)
        m_plasmaSurface.set_skip_taskbar(skip ? 1 : 0);
    m_skipTaskBar = skip;

    if (QGuiApplication::platformName() == "xcb") {
        Display *display = X11Display::display();
        if (!display) {
            return;
        }

        Atom prop = XInternAtom(display, "_NET_WM_STATE", False);
        if (prop == None) {
            return;
        }

        Window win = m_widget ? m_widget->winId() : (m_window ? m_window->winId() : 0);
        if (!win)
            return;

        Atom actualType;
        int actualFormat;
        unsigned long nItems, bytesAfter;
        Atom *currentAtoms = nullptr;

        QVector<Atom> finalAtoms;
        QString atomStr = "_NET_WM_STATE_SKIP_TASKBAR";

        if (Success == XGetWindowProperty(display, win, prop, 0, 1024, False, XA_ATOM, &actualType, &actualFormat, &nItems, &bytesAfter, (unsigned char **)&currentAtoms)) {
            for (unsigned long i = 0; i < nItems; ++i) {
                char *name = XGetAtomName(display, currentAtoms[i]);
                if (name) {
                    QString nameStr = QString::fromUtf8(name);
                    if (nameStr != atomStr)
                        finalAtoms.append(currentAtoms[i]);
                    XFree(name);
                }
            }
            if (currentAtoms)
                XFree(currentAtoms);
        }

        if (skip) {
            Atom skipAtom = XInternAtom(display, atomStr.toUtf8().constData(), False);
            if (skipAtom != None) {
                finalAtoms.append(skipAtom);
            }
        }

        if (finalAtoms.isEmpty()) {
            XDeleteProperty(display, win, prop);
        } else {
            XChangeProperty(X11Display::display(), win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)finalAtoms.constData(), finalAtoms.size());
        }
    }
}

void UkuiWindowHelperPrivate::setSkipSwitcher(bool skip)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_skip_switcher", skip);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_skip_switcher", skip);
    }

    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.set_skip_switcher(skip ? 1 : 0);
    if (m_plasmaSurface)
        m_plasmaSurface.set_skip_switcher(skip ? 1 : 0);
    m_skipSwitcher = skip;

    if (QGuiApplication::platformName() == "xcb") {
        Display *display = X11Display::display();
        if (!display) {
            return;
        }

        Atom prop = XInternAtom(display, "_NET_WM_STATE", False);
        if (prop == None) {
            return;
        }

        Window win = m_widget ? m_widget->winId() : (m_window ? m_window->winId() : 0);
        if (!win)
            return;

        Atom actualType;
        int actualFormat;
        unsigned long nItems, bytesAfter;
        Atom *currentAtoms = nullptr;

        QVector<Atom> finalAtoms;
        QString atomStr = "_KDE_NET_WM_STATE_SKIP_SWITCHER";

        if (Success == XGetWindowProperty(display, win, prop, 0, 1024, False, XA_ATOM, &actualType, &actualFormat, &nItems, &bytesAfter, (unsigned char **)&currentAtoms)) {
            for (unsigned long i = 0; i < nItems; ++i) {
                char *name = XGetAtomName(display, currentAtoms[i]);
                if (name) {
                    QString nameStr = QString::fromUtf8(name);
                    if (nameStr != atomStr)
                        finalAtoms.append(currentAtoms[i]);
                    XFree(name);
                }
            }
            if (currentAtoms)
                XFree(currentAtoms);
        }

        if (skip) {
            Atom skipAtom = XInternAtom(display, atomStr.toUtf8().constData(), False);
            if (skipAtom != None) {
                finalAtoms.append(skipAtom);
            }
        }

        if (finalAtoms.isEmpty()) {
            XDeleteProperty(display, win, prop);
        } else {
            XChangeProperty(display, win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)finalAtoms.constData(), finalAtoms.size());
        }
    }
}

void UkuiWindowHelperPrivate::setPanelAutoHide(bool autoHide)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_panel_auto_hide", autoHide);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_panel_auto_hide", autoHide);
    }
    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.set_panel_auto_hide(autoHide ? 1 : 0);
    if (m_plasmaSurface)
        m_plasmaSurface.set_panel_behavior(autoHide ? 2 : 1);
    m_panelAutoHide = autoHide;
}

void UkuiWindowHelperPrivate::grabKeyboard()
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_grab_keyboard", true);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_grab_keyboard", true);
    }
    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.grab_keyboard(nullptr);
    m_grabKeyboard = true;
}

void UkuiWindowHelperPrivate::openUnderCursor(QPoint pos)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_open_under_cursor", pos);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_open_under_cursor", pos);
    }

    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.open_under_cursor(pos.x(), pos.y());
    m_openUnderCursor = pos;
}

void UkuiWindowHelperPrivate::setBlurEffect(QRegion region, int level, bool enable)
{
    QPair<QRegion, int> pair(region, level);
    if (!enable)
        pair.second = -1;

    if (m_widget) {
        m_widget->setProperty("ukui_surface_blur", QVariant::fromValue(pair));
    } else if (m_window) {
        m_window->setProperty("ukui_surface_blur", QVariant::fromValue(pair));
    }

    if (QGuiApplication::platformName() == "xcb") {
        Display *display = X11Display::display();
        if (!display)
            return;

        Atom blurAtom = XInternAtom(display, "_KDE_NET_WM_BLUR_BEHIND_REGION", False);
        if (blurAtom == None)
            return;

        Window win = m_widget ? m_widget->winId() : (m_window ? m_window->winId() : 0);
        if (!win)
            return;

        if (enable) {
            QVector<unsigned long> data;
            data.reserve(region.rectCount() * 4 + 1); // 每个 QRect 占4个uint，最后加1是 level

            qreal dpr = qApp->devicePixelRatio(); // 设备像素缩放
            for (const QRect &r : region) {
                data << static_cast<unsigned long>(std::floor(r.x() * dpr))
                     << static_cast<unsigned long>(std::floor(r.y() * dpr))
                     << static_cast<unsigned long>(std::ceil(r.width() * dpr))
                     << static_cast<unsigned long>(std::ceil(r.height() * dpr));
            }

            data << static_cast<unsigned long>(level); // 添加 level（模糊强度）

            XChangeProperty(display, win, blurAtom, XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<const unsigned char *>(data.constData()), data.size());
            XFlush(display);
        } else {
            XDeleteProperty(display, win, blurAtom);
            XFlush(display);
        }
    }
}

void UkuiWindowHelperPrivate::setSlideEffect(UkuiWindowHelper::Position position, int offset, bool enable)
{
    QPair<int, int> pair((int)position, offset);

    if (!enable)
        pair.first = -1;

    if (m_widget) {
        m_widget->setProperty("ukui_surface_slide", QVariant::fromValue(pair));
    } else if (m_window) {
        m_window->setProperty("ukui_surface_slide", QVariant::fromValue(pair));
    }

    if (QGuiApplication::platformName() == "xcb") {
        Display *display = X11Display::display();
        if (!display)
            return;

        Atom slideAtom = XInternAtom(display, "_KDE_SLIDE", False);
        if (slideAtom == None)
            return;

        Window winId = m_widget ? m_widget->winId() : (m_window ? m_window->winId() : 0);
        if (!winId)
            return;

        if (!enable) {
            XDeleteProperty(display, winId, slideAtom);
            XFlush(display);
            return;
        }

        unsigned long data[2];
        data[0] = offset;

        switch (position) {
        case UkuiWindowHelper::Position::Left:
            data[1] = 0;
            break;
        case UkuiWindowHelper::Position::Top:
            data[1] = 1;
            break;
        case UkuiWindowHelper::Position::Right:
            data[1] = 2;
            break;
        case UkuiWindowHelper::Position::Bottom:
        default:
            data[1] = 3;
            break;
        }

        XChangeProperty(display, winId, slideAtom, slideAtom, 32, PropModeReplace, reinterpret_cast<unsigned char *>(data), 2);
        XFlush(display);
    }
}

void UkuiWindowHelperPrivate::showTileFlyout(QRect rect)
{
    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.show_tile_flyout(nullptr, rect.x(), rect.y(), rect.width(), rect.height());
}

void UkuiWindowHelperPrivate::setDecorationCompoents(UkuiWindowHelper::DecorationComponents components)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_decoration_components", (uint32_t)components);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_decoration_components", (uint32_t)components);
    }
}

void UkuiWindowHelperPrivate::requestGetCurrentScreen()
{
    if (m_ukuiShellv1)
        m_ukuiShellv1.get_current_output();
}

QScreen *UkuiWindowHelperPrivate::currentScreen() const
{
    return m_currentScreen;
}

void UkuiWindowHelperPrivate::setOpaqueRegion(QRegion region)
{
    if (m_widget) {
        m_widget->setProperty("ukui_surface_opaque_region", region);
    } else if (m_window) {
        m_window->setProperty("ukui_surface_opaque_region", region);
    }
}

void UkuiWindowHelperPrivate::setPosition(QPoint pos)
{
    if (m_plasmaSurface) {
        m_plasmaSurface.set_position(pos.x(), pos.y());
    } else if (m_ukuiSurfacev1) {
        m_ukuiSurfacev1.set_position(pos.x(), pos.y());
    }
    m_currentPos = pos;
}

QPoint UkuiWindowHelperPrivate::position() const
{
    return m_currentPos.value_or(QPoint(0, 0));
}

void UkuiWindowHelperPrivate::setWindowStateEx(UkuiWindowHelper::States mask, UkuiWindowHelper::States states)
{
    if (m_ukuiSurfacev1)
        m_ukuiSurfacev1.set_state(static_cast<uint32_t>(mask), static_cast<uint32_t>(states));

    m_currentStatesMask = mask;
    m_currentStates = states;
}

void UkuiWindowHelperPrivate::ukui_shell_v1_current_output(const std::string &output_name, const std::string &seat_name)
{
    m_currentOutputName = QString::fromUtf8(output_name);
    m_currentSeatName = QString::fromUtf8(seat_name);
}

void UkuiWindowHelperPrivate::ukui_shell_v1_done()
{
    for (QScreen *screen : QGuiApplication::screens()) {
        if (screen->name() == m_currentOutputName) {
            m_currentScreen = screen;
            Q_EMIT m_windowHelper->currentScreenChanged(screen);
            return;
        }
    }
}

wayland::org_kde_plasma_surface_role UkuiWindowHelperPrivate::getPlasmaSurfaceRole(UkuiWindowHelper::WindowRole role)
{
    switch (role) {
    case UkuiWindowHelper::WindowRole::Normal:
        return wayland::org_kde_plasma_surface_role::normal;
    case UkuiWindowHelper::WindowRole::Desktop:
        return wayland::org_kde_plasma_surface_role::desktop;
    case UkuiWindowHelper::WindowRole::Panel:
        return wayland::org_kde_plasma_surface_role::panel;
    case UkuiWindowHelper::WindowRole::OnScreenDisplay:
        return wayland::org_kde_plasma_surface_role::onscreendisplay;
    case UkuiWindowHelper::WindowRole::Notification:
        return wayland::org_kde_plasma_surface_role::notification;
    case UkuiWindowHelper::WindowRole::Tooltip:
        return wayland::org_kde_plasma_surface_role::tooltip;
    case UkuiWindowHelper::WindowRole::CriticalNotification:
        return wayland::org_kde_plasma_surface_role::criticalnotification;
    case UkuiWindowHelper::WindowRole::AppletPopup:
        return wayland::org_kde_plasma_surface_role::appletpopup;
    // remap ukui specific roles
    case UkuiWindowHelper::WindowRole::Watermark:
        return wayland::org_kde_plasma_surface_role::onscreendisplay;
    case UkuiWindowHelper::WindowRole::ScreenLockNotification:
        return wayland::org_kde_plasma_surface_role::notification;
    case UkuiWindowHelper::WindowRole::Dock:
        return wayland::org_kde_plasma_surface_role::panel;
    default:
        return wayland::org_kde_plasma_surface_role::normal;
    }
}

wayland::ukui_surface_v1_role UkuiWindowHelperPrivate::getUkuiSurfaceRole(UkuiWindowHelper::WindowRole role)
{
    switch (role) {
    case UkuiWindowHelper::WindowRole::Normal:
        return wayland::ukui_surface_v1_role::normal;
    case UkuiWindowHelper::WindowRole::Desktop:
        return wayland::ukui_surface_v1_role::desktop;
    case UkuiWindowHelper::WindowRole::Panel:
        return wayland::ukui_surface_v1_role::panel;
    case UkuiWindowHelper::WindowRole::OnScreenDisplay:
        return wayland::ukui_surface_v1_role::onscreendisplay;
    case UkuiWindowHelper::WindowRole::Notification:
        return wayland::ukui_surface_v1_role::notification;
    case UkuiWindowHelper::WindowRole::Tooltip:
        return wayland::ukui_surface_v1_role::tooltip;
    case UkuiWindowHelper::WindowRole::CriticalNotification:
        return wayland::ukui_surface_v1_role::criticalnotification;
    case UkuiWindowHelper::WindowRole::AppletPopup:
        return wayland::ukui_surface_v1_role::appletpopup;
    case UkuiWindowHelper::WindowRole::Watermark:
        return wayland::ukui_surface_v1_role::watermark;
    case UkuiWindowHelper::WindowRole::ScreenLock:
        return wayland::ukui_surface_v1_role::screenlock;
    case UkuiWindowHelper::WindowRole::SystemWindow:
        return wayland::ukui_surface_v1_role::systemwindow;
    case UkuiWindowHelper::WindowRole::InputPanel:
        return wayland::ukui_surface_v1_role::inputpanel;
    case UkuiWindowHelper::WindowRole::Logout:
        return wayland::ukui_surface_v1_role::logout;
    case UkuiWindowHelper::WindowRole::ScreenLockNotification:
        return wayland::ukui_surface_v1_role::screenlocknotification;
    case UkuiWindowHelper::WindowRole::Switcher:
        return wayland::ukui_surface_v1_role::switcher;
    case UkuiWindowHelper::WindowRole::Authentication:
        return wayland::ukui_surface_v1_role::authentication;
    // remap some roles
    case UkuiWindowHelper::WindowRole::Dock:
        return wayland::ukui_surface_v1_role::panel;
    default:
        return wayland::ukui_surface_v1_role::normal;
    }
}

void UkuiWindowHelperPrivate::initShellSurface()
{
    if (!m_waylandWindow)
        return;

    if (s_plasmaSurfaceMap.contains(m_waylandWindow)) {
        m_plasmaSurface = s_plasmaSurfaceMap.value(m_waylandWindow);
        return;
    }

    if (s_ukuiSurfaceMap.contains(m_waylandWindow)) {
        m_ukuiSurfacev1 = s_ukuiSurfaceMap.value(m_waylandWindow);
        return;
    }

    QWindow *window = m_window ? m_window : m_widget->windowHandle();
    if (!window)
        return;

    wayland::surface_t surface(m_waylandWindow->surface(), wayland::proxy_t::wrapper_type::foreign);
    if (!surface)
        return;

    if (m_plasmaShell) {
        m_plasmaSurface = m_plasmaShell.get_surface(surface);
        if (m_plasmaSurface)
            s_plasmaSurfaceMap.insert(m_waylandWindow, m_plasmaSurface);
    }

    if (m_ukuiShellv1) {
        m_ukuiSurfacev1 = m_ukuiShellv1.create_surface(surface);
        m_ukuiDecorationv1 = m_ukuiShellv1.create_decoration(surface);
        if (m_ukuiSurfacev1)
            s_ukuiSurfaceMap.insert(m_waylandWindow, m_ukuiSurfacev1);

        if (m_ukuiDecorationv1)
            s_ukuiDecorationMap.insert(m_waylandWindow, m_ukuiDecorationv1);
    }

    sendPendingRequests();
}

void UkuiWindowHelperPrivate::sendPendingRequests()
{
    if (m_windowRole.has_value())
        setWindowRole(m_windowRole.value());

    if (m_currentPos.has_value())
        setPosition(m_currentPos.value());

    if (m_currentStatesMask.has_value() && m_currentStates.has_value())
        setWindowStateEx(m_currentStatesMask.value(), m_currentStates.value());

    if (m_titleBarRemoved.has_value())
        removeTitleBar();

    if (m_iconName.has_value())
        setWindowIcon(m_iconName.value());

    if (m_skipTaskBar.has_value())
        setSkipTaskBar(m_skipTaskBar.value());

    if (m_skipSwitcher.has_value())
        setSkipSwitcher(m_skipSwitcher.value());

    if (m_panelAutoHide.has_value())
        setPanelAutoHide(m_panelAutoHide.value());

    if (m_grabKeyboard.has_value())
        grabKeyboard();

    if (m_openUnderCursor.has_value())
        openUnderCursor(m_openUnderCursor.value());
}

UkuiWindowHelper::UkuiWindowHelper(QWidget *widget)
    : p(new UkuiWindowHelperPrivate(this, widget))
{
    connect(widget, &QObject::destroyed, this, &QObject::deleteLater);
}

UkuiWindowHelper::UkuiWindowHelper(QWindow *window)
    : p(new UkuiWindowHelperPrivate(this, window))
{
    connect(window, &QObject::destroyed, this, &QObject::deleteLater);
}

UkuiWindowHelper::~UkuiWindowHelper()
{
    delete p;
    p = nullptr;
}

void UkuiWindowHelper::removeTitleBar()
{
    p->removeTitleBar();
}

void UkuiWindowHelper::setWindowRole(WindowRole role)
{
    p->setWindowRole(role);
}

void UkuiWindowHelper::setWindowState(States states)
{
    p->setWindowState(states);
}

void UkuiWindowHelper::setWindowIcon(const QString &iconName)
{
    p->setWindowIcon(iconName);
}

void UkuiWindowHelper::setSkipTaskBar(bool skip)
{
    p->setSkipTaskBar(skip);
}

void UkuiWindowHelper::setSkipSwitcher(bool skip)
{
    p->setSkipSwitcher(skip);
}

void UkuiWindowHelper::setPanelAutoHide(bool autoHide)
{
    p->setPanelAutoHide(autoHide);
}

void UkuiWindowHelper::grabKeyboard()
{
    p->grabKeyboard();
}

void UkuiWindowHelper::openUnderCursor(QPoint pos)
{
    p->openUnderCursor(pos);
}

void UkuiWindowHelper::setBlurEffect(QRegion region, int level, bool enable)
{
    p->setBlurEffect(region, level, enable);
}

void UkuiWindowHelper::setSlideEffect(Position position, int offset, bool enable)
{
    p->setSlideEffect(position, offset, enable);
}

UkuiWindowHelper::States UkuiWindowHelper::defaultState() const
{
    return UkuiWindowHelper::States(0xff);
}

void UkuiWindowHelper::showTileFlyout()
{
    p->showTileFlyout(QRect());
}

void UkuiWindowHelper::showTileFlyout(QRect rect)
{
    p->showTileFlyout(rect);
}

void UkuiWindowHelper::setDecorationState(DecorationStates state)
{
    p->setDecorationCompoents((DecorationComponents)(int)state);
}

void UkuiWindowHelper::setDecorationCompoents(DecorationComponents components)
{
    p->setDecorationCompoents(components);
}

void UkuiWindowHelper::requestGetCurrentScreen()
{
    p->requestGetCurrentScreen();
}

QScreen *UkuiWindowHelper::currentScreen() const
{
    return p->currentScreen();
}

QScreen *UkuiWindowHelper::getCurrentScreenBlocking()
{
    if (QGuiApplication::platformName() != "wayland") {
        qWarning() << "getCurrentScreenBlocking() is only available on Wayland!";
        return nullptr;
    }

    p->requestGetCurrentScreen();
    wl_display_roundtrip(ukuiDisplay()->display());
    return p->currentScreen();
}

void UkuiWindowHelper::setOpaqueRegion(QRegion region)
{
    p->setOpaqueRegion(region);
}

void UkuiWindowHelper::setPosition(QPoint point)
{
    p->setPosition(point);
}

QPoint UkuiWindowHelper::position() const
{
    return p->position();
}

void UkuiWindowHelper::setWindowStateEx(UkuiWindowHelper::States mask, UkuiWindowHelper::States states)
{
    p->setWindowStateEx(mask, states);
}