// Copyright (C) 2024-2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#pragma once

#include <wlr_fwd.h>
#include "modules/foreign-toplevel/foreigntoplevelmanagerv1.h"

#include <xcb/xcb.h>

#include <wglobal.h>

#include <QHash>
#include <QList>
#include <QMap>
#include <QObject>
#include <QPointer>
#include <QSet>

Q_MOC_INCLUDE("workspace/workspace.h")
Q_MOC_INCLUDE(<wlayersurface.h>)
Q_MOC_INCLUDE(<wxdgtoplevelsurface.h>)
Q_MOC_INCLUDE(<wxwaylandsurface.h>)

class Helper;
class SurfaceWrapper;
class RootSurfaceContainer;
class LayerSurfaceContainer;
class Output;
class Workspace;
class SurfaceContainer;
class IMCandidatePanelManager;
class QmlEngine;
class ForeignToplevelManagerInterfaceV1;
class PrelaunchSplash;
class WineWindowStateManager;
class WineWindowManager;

WAYLIB_SERVER_BEGIN_NAMESPACE
class WServer;
class WXdgToplevelSurface;
class WXdgPopupSurface;
class WXdgShell;
class WLayerShell;
class WLayerSurface;
class WXWayland;
class WToplevelSurface; // forward declare base toplevel
class WInputMethodHelper;
class WInputPopupSurface;
class WSeat;
class WSurface;
class WXWaylandSurface;
WAYLIB_SERVER_END_NAMESPACE

QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickWindow;
QT_END_NAMESPACE

class AppIdResolverManager; // forward declare new protocol manager
class WindowConfigStore;    // forward declare config store
class TreelandWallpaperShellInterfaceV1;
class TreelandWallpaperSurfaceInterfaceV1;

class ShellHandler : public QObject
{
    friend class Helper;
    Q_OBJECT

public:
    explicit ShellHandler(RootSurfaceContainer *rootContainer,
                          WAYLIB_SERVER_NAMESPACE::WServer *server);
    [[nodiscard]] Workspace *workspace() const;
    [[nodiscard]] SurfaceContainer *popupContainer() const;
    [[nodiscard]] RootSurfaceContainer *rootSurfaceContainer() const;
    [[nodiscard]] ForeignToplevelManagerInterfaceV1 *foreignToplevel() const;

    void createComponent(QmlEngine *engine, QQuickItem *parentItem);
    void init(WAYLIB_SERVER_NAMESPACE::WServer *server, WAYLIB_SERVER_NAMESPACE::WSeat *seat);
    [[nodiscard]] WAYLIB_SERVER_NAMESPACE::WXWayland *createXWayland(
        WAYLIB_SERVER_NAMESPACE::WServer *server,
        WAYLIB_SERVER_NAMESPACE::WSeat *seat,
        wlr_compositor *compositor,
        bool lazy);
    // FIXME: never call removeXWayland in treeland.cpp
    void removeXWayland(WAYLIB_SERVER_NAMESPACE::WXWayland *xwayland);

    WAYLIB_SERVER_NAMESPACE::WXWayland *defaultXWaylandSocket() const;
    void setupDockPreview(QObject *dockPreview);

    TreelandWallpaperShellInterfaceV1 *wallpaperShell() const
    {
        return m_wallpaperShell;
    }

    [[nodiscard]] WAYLIB_SERVER_NAMESPACE::WInputMethodHelper *inputMethodHelper() const { return m_inputMethodHelper; }

Q_SIGNALS:
    void surfaceWrapperAdded(SurfaceWrapper *wrapper);
    void surfaceWrapperAboutToRemove(SurfaceWrapper *wrapper);

private Q_SLOTS:
    void onXdgToplevelSurfaceAdded(WAYLIB_SERVER_NAMESPACE::WXdgToplevelSurface *surface);
    void onXdgToplevelSurfaceRemoved(WAYLIB_SERVER_NAMESPACE::WXdgToplevelSurface *surface);

    void onXdgPopupSurfaceAdded(WAYLIB_SERVER_NAMESPACE::WXdgPopupSurface *surface);
    void onXdgPopupSurfaceRemoved(WAYLIB_SERVER_NAMESPACE::WXdgPopupSurface *surface);

    void onXWaylandSurfaceAdded(WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface);

    void onLayerSurfaceAdded(WAYLIB_SERVER_NAMESPACE::WLayerSurface *surface);
    void onLayerSurfaceRemoved(WAYLIB_SERVER_NAMESPACE::WLayerSurface *surface);

    void onInputPopupSurfaceV2Added(WAYLIB_SERVER_NAMESPACE::WInputPopupSurface *surface);

    void onDockPreview(std::vector<SurfaceWrapper *> surfaces,
                       WAYLIB_SERVER_NAMESPACE::WSurface *target,
                       QPoint pos,
                       ForeignToplevelManagerInterfaceV1::PreviewDirection direction);
    void onDockPreviewTooltip(QString tooltip,
                              WAYLIB_SERVER_NAMESPACE::WSurface *target,
                              QPoint pos,
                              ForeignToplevelManagerInterfaceV1::PreviewDirection direction);

    void setupDockPreview();
    void onInputPopupSurfaceV2Removed(WAYLIB_SERVER_NAMESPACE::WInputPopupSurface *surface);
    void onWindowMenuClosed();

private:
    void setupSurfaceActiveWatcher(SurfaceWrapper *wrapper);
    void onSurfaceInactivationRequested(SurfaceWrapper *wrapper);
    void setupSurfaceWindowMenu(SurfaceWrapper *wrapper);
    void updateLayerSurfaceContainer(SurfaceWrapper *surface);
    void registerSurfaceToForeignToplevel(SurfaceWrapper *wrapper);
    void handleDdeShellSurfaceAdded(WAYLIB_SERVER_NAMESPACE::WSurface *surface,
                                    SurfaceWrapper *wrapper);
    void setResourceManagerAtom(WAYLIB_SERVER_NAMESPACE::WXWayland *xwayland,
                                const QByteArray &value);
    void updateXWaylandDesktopProperties();
    void watchXWaylandDesktopOutput(Output *output);
    void watchXWaylandWorkspaceNames();
    // Prelaunch splash related: creates a prelaunch SurfaceWrapper when
    // PrelaunchSplash::splashRequested
    void handlePrelaunchSplashRequested(const QString &appId,
                                        const QString &instanceId,
                                        wlr_buffer *iconBuffer);
    void handlePrelaunchSplashClosed(const QString &appId, const QString &instanceId);
    void createPrelaunchSplash(const QString &appId,
                               const QString &instanceId,
                               wlr_buffer *iconBuffer,
                               const QSize &lastSize,
                               const QString &darkPalette,
                               const QString &lightPalette,
                               qlonglong splashThemeType);

    // --- helpers (internal) ---
    // Creates or matches a wrapper from prelaunch splash, then initializes it
    void ensureXdgWrapper(WAYLIB_SERVER_NAMESPACE::WXdgToplevelSurface *surface,
                          const QString &appId);
    // Creates or matches a wrapper from prelaunch splash, then initializes it
    void ensureXwaylandWrapper(WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface,
                               const QString &appId);
    // Unified parent/container update for Xdg & XWayland toplevel wrappers.
    void updateWrapperContainer(SurfaceWrapper *wrapper,
                                WAYLIB_SERVER_NAMESPACE::WSurface *parentSurface);
    // Async X11 property fetch for XWayland surfaces
    void fetchInitialProperties(WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface,
                                const QString &appId);
    void onInitialPropertiesReady(WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface,
                                  const QString &appId,
                                  const QMap<xcb_atom_t, QByteArray> &result);

    WAYLIB_SERVER_NAMESPACE::WXdgShell *m_xdgShell = nullptr;
    WAYLIB_SERVER_NAMESPACE::WLayerShell *m_layerShell = nullptr;
    TreelandWallpaperShellInterfaceV1 *m_wallpaperShell = nullptr;
    WAYLIB_SERVER_NAMESPACE::WInputMethodHelper *m_inputMethodHelper = nullptr;
    PrelaunchSplash *m_prelaunchSplash = nullptr;
    WineWindowStateManager *m_wineWindowStateManager = nullptr;
    WineWindowManager *m_wineWindowManager = nullptr;
    QList<WAYLIB_SERVER_NAMESPACE::WXWayland *> m_xwaylands;
    ForeignToplevelManagerInterfaceV1 *m_treelandForeignToplevel = nullptr;

    QPointer<RootSurfaceContainer> m_rootSurfaceContainer;
    LayerSurfaceContainer *m_backgroundContainer = nullptr;
    LayerSurfaceContainer *m_bottomContainer = nullptr;
    Workspace *m_workspace = nullptr;
    LayerSurfaceContainer *m_topContainer = nullptr;
    LayerSurfaceContainer *m_overlayContainer = nullptr;
    SurfaceContainer *m_popupContainer = nullptr;
    IMCandidatePanelManager *m_imCandidatePanelManager = nullptr;
    QObject *m_windowMenu = nullptr;
    // Prelaunch wrappers created before binding to a real shell surface
    QList<SurfaceWrapper *> m_prelaunchWrappers;
    // Prelaunch requests waiting for dconfig initialization
    QSet<QString> m_pendingPrelaunchAppIds;
    // AppIds of closed splash screens, used to close matching real windows
    QSet<QString> m_closedSplashAppIds;
    // Dock preview QML object
    QObject *m_dockPreview = nullptr;
    // Pending toplevel surfaces (XDG or XWayland) awaiting async AppId resolve; callbacks continue
    // only if the pointer remains in this list
    QList<WAYLIB_SERVER_NAMESPACE::WToplevelSurface *> m_pendingAppIdResolveToplevels;
    // New protocol based app id resolver (optional, may be null if module not loaded)
    AppIdResolverManager *m_appIdResolverManager = nullptr;
    WindowConfigStore *m_windowConfigStore = nullptr;
};
