/*
    SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
    SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>

    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "registry.h"
#include "appmenu.h"
#include "blur.h"
#include "compositor.h"
#include "connection_thread.h"
#include "contrast.h"
#include "datacontroldevicemanager.h"
#include "datadevicemanager.h"
#include "dpms.h"
#include "event_queue.h"
#include "fakeinput.h"
#include "fullscreen_shell.h"
#include "idle.h"
#include "idleinhibit.h"
#include "keystate.h"
#include "logging.h"
#include "output.h"
#include "outputconfiguration.h"
#include "outputdevice.h"
#include "outputdevice_v2.h"
#include "outputmanagement.h"
#include "outputmanagement_v2.h"
#include "plasmashell.h"
#include "plasmavirtualdesktop.h"
#include "plasmawindowmanagement.h"
#include "pointerconstraints.h"
#include "pointergestures.h"
#include "primaryoutput_v1.h"
#include "relativepointer.h"
#include "remote_access.h"
#include "seat.h"
#include "server_decoration.h"
#include "server_decoration_palette.h"
#include "shadow.h"
#include "shell.h"
#include "shm_pool.h"
#include "slide.h"
#include "subcompositor.h"
#include "textinput_p.h"
#include "wayland_pointer_p.h"
#include "xdgdecoration.h"
#include "xdgforeign_v2.h"
#include "xdgoutput.h"
#include "xdgshell.h"
#include "xdgshell_p.h"
#include "clientmanagement.h"
#include "ddeseat.h"
#include "ddeshell.h"
#include "strut.h"
#include "globalproperty.h"
// Qt
#include <QDebug>
// wayland
#include "../compat/wayland-xdg-shell-v5-client-protocol.h"
#include <wayland-appmenu-client-protocol.h>
#include <wayland-blur-client-protocol.h>
#include <wayland-client-protocol.h>
#include <wayland-contrast-client-protocol.h>
#include <wayland-dpms-client-protocol.h>
#include <wayland-fake-input-client-protocol.h>
#include <wayland-fullscreen-shell-client-protocol.h>
#include <wayland-idle-client-protocol.h>
#include <wayland-idle-inhibit-unstable-v1-client-protocol.h>
#include <wayland-keystate-client-protocol.h>
#include <wayland-org_kde_kwin_outputdevice-client-protocol.h>
#include <wayland-kde-output-device-v2-client-protocol.h>
#include <wayland-kde-primary-output-v1-client-protocol.h>
#include <wayland-output-management-client-protocol.h>
#include "wayland-kde-output-management-v2-client-protocol.h"
#include <wayland-plasma-shell-client-protocol.h>
#include <wayland-plasma-virtual-desktop-client-protocol.h>
#include <wayland-plasma-window-management-client-protocol.h>
#include <wayland-pointer-constraints-unstable-v1-client-protocol.h>
#include <wayland-pointer-gestures-unstable-v1-client-protocol.h>
#include <wayland-relativepointer-unstable-v1-client-protocol.h>
#include <wayland-remote-access-client-protocol.h>
#include <wayland-server-decoration-client-protocol.h>
#include <wayland-server-decoration-palette-client-protocol.h>
#include <wayland-shadow-client-protocol.h>
#include <wayland-slide-client-protocol.h>
#include <wayland-text-input-v0-client-protocol.h>
#include <wayland-text-input-v2-client-protocol.h>
#include <wayland-xdg-decoration-unstable-v1-client-protocol.h>
#include <wayland-xdg-foreign-unstable-v2-client-protocol.h>
#include <wayland-xdg-output-unstable-v1-client-protocol.h>
#include <wayland-xdg-shell-client-protocol.h>
#include <wayland-xdg-shell-v6-client-protocol.h>
#include <wayland-client-management-client-protocol.h>
#include <wayland-dde-seat-client-protocol.h>
#include <wayland-dde-shell-client-protocol.h>
#include <wayland-strut-client-protocol.h>
#include <wayland-dde-globalproperty-client-protocol.h>
#include <wayland-wlr-data-control-unstable-v1-client-protocol.h>

/*****
 * How to add another interface:
 * * define a new enum value in Registry::Interface
 * * define the bind<InterfaceName> method
 * * define the create<InterfaceName> method
 * * define the <interfaceName>Announced signal
 * * define the <interfaceName>Removed signal
 * * add a block to s_interfaces
 * * add the BIND macro for the new bind<InterfaceName>
 * * add the CREATE macro for the new create<InterfaceName>
 * * extend registry unit test to verify that it works
 ****/

namespace KWayland
{
namespace Client
{
namespace
{
struct SuppertedInterfaceData {
    quint32 maxVersion;
    QByteArray name;
    const wl_interface *interface;
    void (Registry::*announcedSignal)(quint32, quint32);
    void (Registry::*removedSignal)(quint32);
};
// clang-format off
static const QMap<Registry::Interface, SuppertedInterfaceData> s_interfaces = {
    {Registry::Interface::Compositor, {
        4,
        QByteArrayLiteral("wl_compositor"),
        &wl_compositor_interface,
        &Registry::compositorAnnounced,
        &Registry::compositorRemoved
    }},
    {Registry::Interface::DataDeviceManager, {
        3,
        QByteArrayLiteral("wl_data_device_manager"),
        &wl_data_device_manager_interface,
        &Registry::dataDeviceManagerAnnounced,
        &Registry::dataDeviceManagerRemoved
    }},
    {Registry::Interface::Output, {
        3,
        QByteArrayLiteral("wl_output"),
        &wl_output_interface,
        &Registry::outputAnnounced,
        &Registry::outputRemoved
    }},
    {Registry::Interface::Shm, {
        1,
        QByteArrayLiteral("wl_shm"),
        &wl_shm_interface,
        &Registry::shmAnnounced,
        &Registry::shmRemoved
    }},
    {Registry::Interface::Seat, {
        5,
        QByteArrayLiteral("wl_seat"),
        &wl_seat_interface,
        &Registry::seatAnnounced,
        &Registry::seatRemoved
    }},
    {Registry::Interface::Shell, {
        1,
        QByteArrayLiteral("wl_shell"),
        &wl_shell_interface,
        &Registry::shellAnnounced,
        &Registry::shellRemoved
    }},
    {Registry::Interface::SubCompositor, {
        1,
        QByteArrayLiteral("wl_subcompositor"),
        &wl_subcompositor_interface,
        &Registry::subCompositorAnnounced,
        &Registry::subCompositorRemoved
    }},
    {Registry::Interface::PlasmaShell, {
        6,
        QByteArrayLiteral("org_kde_plasma_shell"),
        &org_kde_plasma_shell_interface,
        &Registry::plasmaShellAnnounced,
        &Registry::plasmaShellRemoved
    }},
    {Registry::Interface::PlasmaVirtualDesktopManagement, {
        2,
        QByteArrayLiteral("org_kde_plasma_virtual_desktop_management"),
        &org_kde_plasma_virtual_desktop_management_interface,
        &Registry::plasmaVirtualDesktopManagementAnnounced,
        &Registry::plasmaVirtualDesktopManagementRemoved
    }},
    {Registry::Interface::PlasmaWindowManagement, {
        15,
        QByteArrayLiteral("org_kde_plasma_window_management"),
        &org_kde_plasma_window_management_interface,
        &Registry::plasmaWindowManagementAnnounced,
        &Registry::plasmaWindowManagementRemoved
    }},
    {Registry::Interface::Idle, {
        1,
        QByteArrayLiteral("org_kde_kwin_idle"),
        &org_kde_kwin_idle_interface,
        &Registry::idleAnnounced,
        &Registry::idleRemoved
    }},
    {Registry::Interface::RemoteAccessManager, {
        1,
        QByteArrayLiteral("org_kde_kwin_remote_access_manager"),
        &org_kde_kwin_remote_access_manager_interface,
        &Registry::remoteAccessManagerAnnounced,
        &Registry::remoteAccessManagerRemoved
    }},
    {Registry::Interface::FakeInput, {
        4,
        QByteArrayLiteral("org_kde_kwin_fake_input"),
        &org_kde_kwin_fake_input_interface,
        &Registry::fakeInputAnnounced,
        &Registry::fakeInputRemoved
    }},
    {Registry::Interface::OutputManagement, {
        4,
        QByteArrayLiteral("org_kde_kwin_outputmanagement"),
        &org_kde_kwin_outputmanagement_interface,
        &Registry::outputManagementAnnounced,
        &Registry::outputManagementRemoved
    }},
    {Registry::Interface::OutputManagementV2, {
        2,
        QByteArrayLiteral("kde_output_management_v2"),
        &kde_output_management_v2_interface,
        &Registry::outputManagementV2Announced,
        &Registry::outputManagementV2Removed
    }},
    {Registry::Interface::OutputDevice, {
        4,
        QByteArrayLiteral("org_kde_kwin_outputdevice"),
        &org_kde_kwin_outputdevice_interface,
        &Registry::outputDeviceAnnounced,
        &Registry::outputDeviceRemoved
    }},
    {Registry::Interface::OutputDeviceV2, {
        2,
        QByteArrayLiteral("kde_output_device_v2"),
        &kde_output_device_v2_interface,
        &Registry::outputDeviceV2Announced,
        &Registry::outputDeviceV2Removed
    }},
    {Registry::Interface::PrimaryOutputV1, {
        1,
        QByteArrayLiteral("kde_primary_output_v1"),
        &kde_primary_output_v1_interface,
        &Registry::primaryOutputV1Announced,
        &Registry::primaryOutputV1Removed
    }},
    {Registry::Interface::Shadow, {
        2,
        QByteArrayLiteral("org_kde_kwin_shadow_manager"),
        &org_kde_kwin_shadow_manager_interface,
        &Registry::shadowAnnounced,
        &Registry::shadowRemoved
    }},
    {Registry::Interface::Blur, {
        1,
        QByteArrayLiteral("org_kde_kwin_blur_manager"),
        &org_kde_kwin_blur_manager_interface,
        &Registry::blurAnnounced,
        &Registry::blurRemoved
    }},
    {Registry::Interface::Contrast, {
        2,
        QByteArrayLiteral("org_kde_kwin_contrast_manager"),
        &org_kde_kwin_contrast_manager_interface,
        &Registry::contrastAnnounced,
        &Registry::contrastRemoved
    }},
    {Registry::Interface::Slide, {
        1,
        QByteArrayLiteral("org_kde_kwin_slide_manager"),
        &org_kde_kwin_slide_manager_interface,
        &Registry::slideAnnounced,
        &Registry::slideRemoved
    }},
    {Registry::Interface::FullscreenShell, {
        1,
        QByteArrayLiteral("_wl_fullscreen_shell"),
        &_wl_fullscreen_shell_interface,
        &Registry::fullscreenShellAnnounced,
        &Registry::fullscreenShellRemoved
    }},
    {Registry::Interface::Dpms, {
        1,
        QByteArrayLiteral("org_kde_kwin_dpms_manager"),
        &org_kde_kwin_dpms_manager_interface,
        &Registry::dpmsAnnounced,
        &Registry::dpmsRemoved
    }},
    {Registry::Interface::ServerSideDecorationManager, {
        1,
        QByteArrayLiteral("org_kde_kwin_server_decoration_manager"),
        &org_kde_kwin_server_decoration_manager_interface,
        &Registry::serverSideDecorationManagerAnnounced,
        &Registry::serverSideDecorationManagerRemoved
    }},
    {Registry::Interface::TextInputManagerUnstableV0, {
        1,
        QByteArrayLiteral("wl_text_input_manager"),
        &wl_text_input_manager_interface,
        &Registry::textInputManagerUnstableV0Announced,
        &Registry::textInputManagerUnstableV0Removed
    }},
    {Registry::Interface::TextInputManagerUnstableV2, {
        1,
        QByteArrayLiteral("zwp_text_input_manager_v2"),
        &zwp_text_input_manager_v2_interface,
        &Registry::textInputManagerUnstableV2Announced,
        &Registry::textInputManagerUnstableV2Removed
    }},
    {Registry::Interface::XdgShellUnstableV5, {
        1,
        QByteArrayLiteral("xdg_shell"),
        &zxdg_shell_v5_interface,
        &Registry::xdgShellUnstableV5Announced,
        &Registry::xdgShellUnstableV5Removed
    }},
    {Registry::Interface::RelativePointerManagerUnstableV1, {
        1,
        QByteArrayLiteral("zwp_relative_pointer_manager_v1"),
        &zwp_relative_pointer_manager_v1_interface,
        &Registry::relativePointerManagerUnstableV1Announced,
        &Registry::relativePointerManagerUnstableV1Removed
    }},
    {Registry::Interface::PointerGesturesUnstableV1, {
        1,
        QByteArrayLiteral("zwp_pointer_gestures_v1"),
        &zwp_pointer_gestures_v1_interface,
        &Registry::pointerGesturesUnstableV1Announced,
        &Registry::pointerGesturesUnstableV1Removed
    }},
    {Registry::Interface::PointerConstraintsUnstableV1, {
        1,
        QByteArrayLiteral("zwp_pointer_constraints_v1"),
        &zwp_pointer_constraints_v1_interface,
        &Registry::pointerConstraintsUnstableV1Announced,
        &Registry::pointerConstraintsUnstableV1Removed
    }},
    {Registry::Interface::XdgExporterUnstableV2, {
        1,
        QByteArrayLiteral("zxdg_exporter_v2"),
        &zxdg_exporter_v2_interface,
        &Registry::exporterUnstableV2Announced,
        &Registry::exporterUnstableV2Removed
    }},
    {Registry::Interface::XdgImporterUnstableV2, {
        1,
        QByteArrayLiteral("zxdg_importer_v2"),
        &zxdg_importer_v2_interface,
        &Registry::importerUnstableV2Announced,
        &Registry::importerUnstableV2Removed
    }},
    {Registry::Interface::XdgShellUnstableV6, {
        1,
        QByteArrayLiteral("zxdg_shell_v6"),
        &zxdg_shell_v6_interface,
        &Registry::xdgShellUnstableV6Announced,
        &Registry::xdgShellUnstableV6Removed
    }},
    {Registry::Interface::IdleInhibitManagerUnstableV1, {
        1,
        QByteArrayLiteral("zwp_idle_inhibit_manager_v1"),
        &zwp_idle_inhibit_manager_v1_interface,
        &Registry::idleInhibitManagerUnstableV1Announced,
        &Registry::idleInhibitManagerUnstableV1Removed
    }},
    {Registry::Interface::AppMenu, {
        1,
        QByteArrayLiteral("org_kde_kwin_appmenu_manager"),
        &org_kde_kwin_appmenu_manager_interface,
        &Registry::appMenuAnnounced,
        &Registry::appMenuRemoved
    }},
    {Registry::Interface::ServerSideDecorationPalette, {
        1,
        QByteArrayLiteral("org_kde_kwin_server_decoration_palette_manager"),
        &org_kde_kwin_server_decoration_palette_manager_interface,
        &Registry::serverSideDecorationPaletteManagerAnnounced,
        &Registry::serverSideDecorationPaletteManagerRemoved
    }},
    {Registry::Interface::XdgOutputUnstableV1, {
        2,
        QByteArrayLiteral("zxdg_output_manager_v1"),
        &zxdg_output_manager_v1_interface,
        &Registry::xdgOutputAnnounced,
        &Registry::xdgOutputRemoved
    }},
    {Registry::Interface::XdgShellStable, {
        1,
        QByteArrayLiteral("xdg_wm_base"),
        &xdg_wm_base_interface,
        &Registry::xdgShellStableAnnounced,
        &Registry::xdgShellStableRemoved
    }},
    {Registry::Interface::XdgDecorationUnstableV1, {
        1,
        QByteArrayLiteral("zxdg_decoration_manager_v1"),
        &zxdg_decoration_manager_v1_interface,
        &Registry::xdgDecorationAnnounced,
        &Registry::xdgDecorationRemoved
    }},
    {Registry::Interface::Keystate, {
        1,
        QByteArrayLiteral("org_kde_kwin_keystate"),
        &org_kde_kwin_keystate_interface,
        &Registry::keystateAnnounced,
        &Registry::keystateRemoved
    }},
    {Registry::Interface::PlasmaActivationFeedback, {
        1,
        QByteArrayLiteral("org_kde_plasma_activation_feedback"),
        &org_kde_plasma_activation_feedback_interface,
        &Registry::plasmaActivationFeedbackAnnounced,
        &Registry::plasmaActivationFeedbackRemoved
    }},
    {Registry::Interface::ClientManagement, {
        1,
        QByteArrayLiteral("com_deepin_client_management"),
        &com_deepin_client_management_interface,
        &Registry::clientManagementAnnounced,
        &Registry::clientManagementRemoved
    }},
    {Registry::Interface::DDEShell, {
        1,
        QByteArrayLiteral("dde_shell"),
        &dde_shell_interface,
        &Registry::ddeShellAnnounced,
        &Registry::ddeShellRemoved
    }},
    {Registry::Interface::GlobalProperty, {
        1,
        QByteArrayLiteral("dde_globalproperty"),
        &dde_globalproperty_interface,
        &Registry::ddeGlobalPropertyAnnounced,
        &Registry::ddeGlobalPropertyRemoved
    }},
    {Registry::Interface::DDESeat, {
        1,
        QByteArrayLiteral("dde_seat"),
        &dde_seat_interface,
        &Registry::ddeSeatAnnounced,
        &Registry::ddeSeatRemoved
    }},
    {Registry::Interface::Strut, {
        1,
        QByteArrayLiteral("com_deepin_kwin_strut"),
        &com_deepin_kwin_strut_interface,
        &Registry::strutAnnounced,
        &Registry::strutRemoved
    }},
    {Registry::Interface::DataControlDeviceManager, {
        1,
        QByteArrayLiteral("zwlr_data_control_manager_v1"),
        &zwlr_data_control_manager_v1_interface,
        &Registry::dataControlDeviceManagerAnnounced,
        &Registry::dataControlDeviceManagerRemoved
    }},
};
// clang-format on

static quint32 maxVersion(const Registry::Interface &interface)
{
    auto it = s_interfaces.find(interface);
    if (it != s_interfaces.end()) {
        return it.value().maxVersion;
    }
    return 0;
}
}

class Q_DECL_HIDDEN Registry::Private
{
public:
    Private(Registry *q);
    void setup();
    bool hasInterface(Interface interface) const;
    AnnouncedInterface interface(Interface interface) const;
    QVector<AnnouncedInterface> interfaces(Interface interface) const;
    Interface interfaceForName(quint32 name) const;
    template<typename T>
    T *bind(Interface interface, uint32_t name, uint32_t version) const;
    template<class T, typename WL>
    T *create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const);

    WaylandPointer<wl_registry, wl_registry_destroy> registry;
    static const struct wl_callback_listener s_callbackListener;
    WaylandPointer<wl_callback, wl_callback_destroy> callback;
    EventQueue *queue = nullptr;

private:
    void handleAnnounce(uint32_t name, const char *interface, uint32_t version);
    void handleRemove(uint32_t name);
    void handleGlobalSync();
    static void globalAnnounce(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version);
    static void globalRemove(void *data, struct wl_registry *registry, uint32_t name);
    static void globalSync(void *data, struct wl_callback *callback, uint32_t serial);

    Registry *q;
    struct InterfaceData {
        Interface interface;
        uint32_t name;
        uint32_t version;
    };
    QList<InterfaceData> m_interfaces;
    static const struct wl_registry_listener s_registryListener;
};

Registry::Private::Private(Registry *q)
    : q(q)
{
}

void Registry::Private::setup()
{
    wl_registry_add_listener(registry, &s_registryListener, this);
    wl_callback_add_listener(callback, &s_callbackListener, this);
}

Registry::Registry(QObject *parent)
    : QObject(parent)
    , d(new Private(this))
{
}

Registry::~Registry()
{
    release();
}

void Registry::release()
{
    d->registry.release();
    d->callback.release();
}

void Registry::destroy()
{
    Q_EMIT registryDestroyed();
    d->registry.destroy();
    d->callback.destroy();
}

void Registry::create(wl_display *display)
{
    Q_ASSERT(display);
    Q_ASSERT(!isValid());
    d->registry.setup(wl_display_get_registry(display));
    d->callback.setup(wl_display_sync(display));
    if (d->queue) {
        d->queue->addProxy(d->registry);
        d->queue->addProxy(d->callback);
    }
}

void Registry::create(ConnectionThread *connection)
{
    create(connection->display());
    connect(connection, &ConnectionThread::connectionDied, this, &Registry::destroy);
}

void Registry::setup()
{
    Q_ASSERT(isValid());
    d->setup();
}

void Registry::setEventQueue(EventQueue *queue)
{
    d->queue = queue;
    if (!queue) {
        return;
    }
    if (d->registry) {
        d->queue->addProxy(d->registry);
    }
    if (d->callback) {
        d->queue->addProxy(d->callback);
    }
}

EventQueue *Registry::eventQueue()
{
    return d->queue;
}

#ifndef K_DOXYGEN
const struct wl_registry_listener Registry::Private::s_registryListener = {globalAnnounce, globalRemove};

const struct wl_callback_listener Registry::Private::s_callbackListener = {globalSync};
#endif

void Registry::Private::globalAnnounce(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version)
{
    auto r = reinterpret_cast<Registry::Private *>(data);
    Q_ASSERT(registry == r->registry);
    r->handleAnnounce(name, interface, version);
}

void Registry::Private::globalRemove(void *data, wl_registry *registry, uint32_t name)
{
    auto r = reinterpret_cast<Registry::Private *>(data);
    Q_ASSERT(registry == r->registry);
    r->handleRemove(name);
}

void Registry::Private::globalSync(void *data, wl_callback *callback, uint32_t serial)
{
    Q_UNUSED(serial)
    auto r = reinterpret_cast<Registry::Private *>(data);
    Q_ASSERT(r->callback == callback);
    r->handleGlobalSync();
    r->callback.release();
}

void Registry::Private::handleGlobalSync()
{
    Q_EMIT q->interfacesAnnounced();
}

namespace
{
static Registry::Interface nameToInterface(const char *interface)
{
    for (auto it = s_interfaces.constBegin(); it != s_interfaces.constEnd(); ++it) {
        if (qstrcmp(interface, it.value().name) == 0) {
            return it.key();
        }
    }
    return Registry::Interface::Unknown;
}
}

void Registry::Private::handleAnnounce(uint32_t name, const char *interface, uint32_t version)
{
    Interface i = nameToInterface(interface);
    Q_EMIT q->interfaceAnnounced(QByteArray(interface), name, version);
    if (i == Interface::Unknown) {
        qCDebug(KWAYLAND_CLIENT) << "Unknown interface announced: " << interface << "/" << name << "/" << version;
        return;
    }
    qCDebug(KWAYLAND_CLIENT) << "Wayland Interface: " << interface << "/" << name << "/" << version;
    m_interfaces.append({i, name, version});
    auto it = s_interfaces.constFind(i);
    if (it != s_interfaces.end()) {
        Q_EMIT(q->*it.value().announcedSignal)(name, version);
    }
}

void Registry::Private::handleRemove(uint32_t name)
{
    auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [name](const InterfaceData &data) {
        return data.name == name;
    });
    if (it != m_interfaces.end()) {
        InterfaceData data = *(it);
        m_interfaces.erase(it);
        auto sit = s_interfaces.find(data.interface);
        if (sit != s_interfaces.end()) {
            Q_EMIT(q->*sit.value().removedSignal)(data.name);
        }
    }
    Q_EMIT q->interfaceRemoved(name);
}

bool Registry::Private::hasInterface(Registry::Interface interface) const
{
    auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [interface](const InterfaceData &data) {
        return data.interface == interface;
    });
    return it != m_interfaces.constEnd();
}

QVector<Registry::AnnouncedInterface> Registry::Private::interfaces(Interface interface) const
{
    QVector<Registry::AnnouncedInterface> retVal;
    for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) {
        const auto &data = *it;
        if (data.interface == interface) {
            retVal << AnnouncedInterface{data.name, data.version};
        }
    }
    return retVal;
}

Registry::AnnouncedInterface Registry::Private::interface(Interface interface) const
{
    const auto all = interfaces(interface);
    if (!all.isEmpty()) {
        return all.last();
    }
    return AnnouncedInterface{0, 0};
}

Registry::Interface Registry::Private::interfaceForName(quint32 name) const
{
    auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [name](const InterfaceData &data) {
        return data.name == name;
    });
    if (it == m_interfaces.constEnd()) {
        return Interface::Unknown;
    }
    return (*it).interface;
}

bool Registry::hasInterface(Registry::Interface interface) const
{
    return d->hasInterface(interface);
}

QVector<Registry::AnnouncedInterface> Registry::interfaces(Interface interface) const
{
    return d->interfaces(interface);
}

Registry::AnnouncedInterface Registry::interface(Interface interface) const
{
    return d->interface(interface);
}

// clang-format off
#define BIND2(__NAME__, __INAME__, __WL__) \
__WL__ *Registry::bind##__NAME__(uint32_t name, uint32_t version) const \
{ \
    return d->bind<__WL__>(Interface::__INAME__, name, qMin(maxVersion(Interface::__INAME__), version)); \
}

#define BIND(__NAME__, __WL__) BIND2(__NAME__, __NAME__, __WL__)
// clang-format on

BIND(Compositor, wl_compositor)
BIND(Output, wl_output)
BIND(Seat, wl_seat)
BIND(Shell, wl_shell)
BIND(Shm, wl_shm)
BIND(SubCompositor, wl_subcompositor)
BIND(FullscreenShell, _wl_fullscreen_shell)
BIND(DataDeviceManager, wl_data_device_manager)
BIND(PlasmaShell, org_kde_plasma_shell)
BIND(PlasmaActivationFeedback, org_kde_plasma_activation_feedback)
BIND(PlasmaVirtualDesktopManagement, org_kde_plasma_virtual_desktop_management)
BIND(PlasmaWindowManagement, org_kde_plasma_window_management)
BIND(Idle, org_kde_kwin_idle)
BIND(RemoteAccessManager, org_kde_kwin_remote_access_manager)
BIND(FakeInput, org_kde_kwin_fake_input)
BIND(OutputManagement, org_kde_kwin_outputmanagement)
BIND(OutputManagementV2, kde_output_management_v2)
BIND(OutputDevice, org_kde_kwin_outputdevice)
BIND(OutputDeviceV2, kde_output_device_v2)
BIND(PrimaryOutputV1, kde_primary_output_v1)
BIND(ServerSideDecorationManager, org_kde_kwin_server_decoration_manager)
BIND(TextInputManagerUnstableV0, wl_text_input_manager)
BIND(TextInputManagerUnstableV2, zwp_text_input_manager_v2)
BIND(XdgShellUnstableV5, xdg_shell)
BIND(XdgShellUnstableV6, zxdg_shell_v6)
BIND(XdgShellStable, xdg_wm_base)
BIND(RelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1)
BIND(PointerGesturesUnstableV1, zwp_pointer_gestures_v1)
BIND(PointerConstraintsUnstableV1, zwp_pointer_constraints_v1)
BIND(XdgExporterUnstableV2, zxdg_exporter_v2)
BIND(XdgImporterUnstableV2, zxdg_importer_v2)
BIND(IdleInhibitManagerUnstableV1, zwp_idle_inhibit_manager_v1)
BIND(Keystate, org_kde_kwin_keystate)
BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager)
BIND2(BlurManager, Blur, org_kde_kwin_blur_manager)
BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager)
BIND2(SlideManager, Slide, org_kde_kwin_slide_manager)
BIND2(DpmsManager, Dpms, org_kde_kwin_dpms_manager)
BIND2(AppMenuManager, AppMenu, org_kde_kwin_appmenu_manager)
BIND2(ServerSideDecorationPaletteManager, ServerSideDecorationPalette, org_kde_kwin_server_decoration_palette_manager)
BIND(XdgOutputUnstableV1, zxdg_output_manager_v1)
BIND(XdgDecorationUnstableV1, zxdg_decoration_manager_v1)
BIND(ClientManagement, com_deepin_client_management)
BIND(DDESeat, dde_seat)
BIND(DDEShell, dde_shell)
BIND(Strut, com_deepin_kwin_strut)
BIND(GlobalProperty, dde_globalproperty)
BIND(DataControlDeviceManager, zwlr_data_control_manager_v1)

#undef BIND
#undef BIND2

template<class T, typename WL>
T *Registry::Private::create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const)
{
    T *t = new T(parent);
    t->setEventQueue(queue);
    t->setup((q->*bindMethod)(name, version));
    QObject::connect(q, &Registry::interfaceRemoved, t, [t, name](quint32 removed) {
        if (name == removed) {
            Q_EMIT t->removed();
        }
    });
    QObject::connect(q, &Registry::registryDestroyed, t, &T::destroy);
    return t;
}

// clang-format off
#define CREATE2(__NAME__, __BINDNAME__) \
__NAME__ *Registry::create##__NAME__(quint32 name, quint32 version, QObject *parent) \
{ \
    return d->create<__NAME__>(name, version, parent, &Registry::bind##__BINDNAME__); \
}

#define CREATE(__NAME__) CREATE2(__NAME__, __NAME__)
// clang-format on

CREATE(Compositor)
CREATE(Seat)
CREATE(Shell)
CREATE(SubCompositor)
CREATE(FullscreenShell)
CREATE(Output)
CREATE(DataDeviceManager)
CREATE(PlasmaShell)
CREATE(PlasmaActivationFeedback)
CREATE(PlasmaVirtualDesktopManagement)
CREATE(PlasmaWindowManagement)
CREATE(Idle)
CREATE(RemoteAccessManager)
CREATE(FakeInput)
CREATE(OutputManagement)
CREATE(OutputManagementV2)
CREATE(OutputDevice)
CREATE(OutputDeviceV2)
CREATE(PrimaryOutputV1)
CREATE(ShadowManager)
CREATE(BlurManager)
CREATE(ContrastManager)
CREATE(SlideManager)
CREATE(DpmsManager)
CREATE(ServerSideDecorationManager)
CREATE2(ShmPool, Shm)
CREATE(AppMenuManager)
CREATE(Keystate)
CREATE(ServerSideDecorationPaletteManager)
CREATE(ClientManagement)
CREATE(DDESeat)
CREATE(DDEShell)
CREATE(Strut)
CREATE(GlobalProperty)

#undef CREATE
#undef CREATE2

XdgExporter *Registry::createXdgExporter(quint32 name, quint32 version, QObject *parent)
{
    // only V1 supported for now
    return d->create<XdgExporterUnstableV2>(name, version, parent, &Registry::bindXdgExporterUnstableV2);
}

XdgImporter *Registry::createXdgImporter(quint32 name, quint32 version, QObject *parent)
{
    // only V1 supported for now
    return d->create<XdgImporterUnstableV2>(name, version, parent, &Registry::bindXdgImporterUnstableV2);
}

TextInputManager *Registry::createTextInputManager(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::TextInputManagerUnstableV0:
        return d->create<TextInputManagerUnstableV0>(name, version, parent, &Registry::bindTextInputManagerUnstableV0);
    case Interface::TextInputManagerUnstableV2:
        return d->create<TextInputManagerUnstableV2>(name, version, parent, &Registry::bindTextInputManagerUnstableV2);
    default:
        return nullptr;
    }
}

XdgShell *Registry::createXdgShell(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::XdgShellUnstableV5:
        return d->create<XdgShellUnstableV5>(name, version, parent, &Registry::bindXdgShellUnstableV5);
    case Interface::XdgShellUnstableV6:
        return d->create<XdgShellUnstableV6>(name, version, parent, &Registry::bindXdgShellUnstableV6);
    case Interface::XdgShellStable:
        return d->create<XdgShellStable>(name, version, parent, &Registry::bindXdgShellStable);
    default:
        return nullptr;
    }
}

RelativePointerManager *Registry::createRelativePointerManager(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::RelativePointerManagerUnstableV1:
        return d->create<RelativePointerManager>(name, version, parent, &Registry::bindRelativePointerManagerUnstableV1);
    default:
        return nullptr;
    }
}

PointerGestures *Registry::createPointerGestures(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::PointerGesturesUnstableV1:
        return d->create<PointerGestures>(name, version, parent, &Registry::bindPointerGesturesUnstableV1);
    default:
        return nullptr;
    }
}

PointerConstraints *Registry::createPointerConstraints(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::PointerConstraintsUnstableV1:
        return d->create<PointerConstraints>(name, version, parent, &Registry::bindPointerConstraintsUnstableV1);
    default:
        return nullptr;
    }
}

IdleInhibitManager *Registry::createIdleInhibitManager(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::IdleInhibitManagerUnstableV1:
        return d->create<IdleInhibitManager>(name, version, parent, &Registry::bindIdleInhibitManagerUnstableV1);
    default:
        return nullptr;
    }
}

XdgOutputManager *Registry::createXdgOutputManager(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::XdgOutputUnstableV1:
        return d->create<XdgOutputManager>(name, version, parent, &Registry::bindXdgOutputUnstableV1);
    default:
        return nullptr;
    }
}

XdgDecorationManager *Registry::createXdgDecorationManager(quint32 name, quint32 version, QObject *parent)
{
    switch (d->interfaceForName(name)) {
    case Interface::XdgDecorationUnstableV1:
        return d->create<XdgDecorationManager>(name, version, parent, &Registry::bindXdgDecorationUnstableV1);
    default:
        return nullptr;
    }
}

DataControlDeviceManager *Registry::createDataControlDeviceManager(quint32 name, quint32 version, QObject *parent)
{
    switch(d->interfaceForName(name)) {
    case Interface::DataControlDeviceManager:
        return d->create<DataControlDeviceManager>(name, version, parent, &Registry::bindDataControlDeviceManager);
    default:
        return nullptr;
    }
}

namespace
{
static const wl_interface *wlInterface(Registry::Interface interface)
{
    auto it = s_interfaces.find(interface);
    if (it != s_interfaces.end()) {
        return it.value().interface;
    }
    return nullptr;
}
}

template<typename T>
T *Registry::Private::bind(Registry::Interface interface, uint32_t name, uint32_t version) const
{
    auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [=](const InterfaceData &data) {
        return data.interface == interface && data.name == name && data.version >= version;
    });
    if (it == m_interfaces.constEnd()) {
        qCDebug(KWAYLAND_CLIENT) << "Don't have interface " << int(interface) << "with name " << name << "and minimum version" << version;
        return nullptr;
    }
    auto t = reinterpret_cast<T *>(wl_registry_bind(registry, name, wlInterface(interface), version));
    if (queue) {
        queue->addProxy(t);
    }
    return t;
}

bool Registry::isValid() const
{
    return d->registry.isValid();
}

wl_registry *Registry::registry()
{
    return d->registry;
}

Registry::operator wl_registry *() const
{
    return d->registry;
}

Registry::operator wl_registry *()
{
    return d->registry;
}

}
}
