//
// Copyright 2022 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

// WaylandWindow.h: Definition of the implementation of OSWindow for Wayland

#ifndef UTIL_WAYLAND_WINDOW_H
#define UTIL_WAYLAND_WINDOW_H

#include <poll.h>
#include <wayland-client.h>
#include <wayland-egl-core.h>

#include "xdg-decoration-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"

#include "util/OSWindow.h"
#include "util/util_export.h"

ANGLE_UTIL_EXPORT bool IsWaylandWindowAvailable();

class ANGLE_UTIL_EXPORT WaylandWindow : public OSWindow
{
  public:
    WaylandWindow(void *nativeDisplay);
    ~WaylandWindow() override;

    void disableErrorMessageDialog() override;
    void destroy() override;

    void resetNativeWindow() override;
    EGLNativeWindowType getNativeWindow() const override;

    void setNativeDisplay(EGLNativeDisplayType display) override;
    EGLNativeDisplayType getNativeDisplay() const override;
    EGLenum getNativeDisplayPlatformType() const override;

    void messageLoop() override;

    void setMousePosition(int x, int y) override;
    bool setOrientation(int width, int height) override;
    bool setPosition(int x, int y) override;
    bool resize(int width, int height) override;
    void setVisible(bool isVisible) override;

    void signalTestEvent() override;

  private:
    struct wl_compositor *mCompositor;
    struct wl_surface *mSurface;
    struct wl_egl_window *mWindow;

    struct xdg_wm_base *mXdgWmBase    = nullptr;
    struct xdg_surface *mXdgSurface   = nullptr;
    struct xdg_toplevel *mXdgToplevel = nullptr;

    // Server-side decoration request (optional -- only used if the compositor
    // advertises zxdg_decoration_manager_v1). Compositors that grant SSD draw
    // the title bar + close/min/max buttons around the surface. Compositors
    // that refuse or don't advertise the protocol leave the window
    // decorationless (we don't draw client-side chrome).
    struct zxdg_decoration_manager_v1 *mDecorationManager   = nullptr;
    struct zxdg_toplevel_decoration_v1 *mToplevelDecoration = nullptr;

    // wl_shm placeholder maps the window before EGL claims the surface.
    // Retired in getNativeWindow() because direct wl_surface_attach once
    // EGL owns the surface is illegal per wayland-egl -- Mesa is lenient,
    // Nvidia's EGLStream backend crashes. WindowTest is the only non-EGL
    // OSWindow caller and never invokes getNativeWindow, so it keeps the
    // full placeholder lifecycle.
    struct wl_shm *mShm                  = nullptr;
    mutable struct wl_buffer *mShmBuffer = nullptr;
    mutable int32_t mShmBufferWidth      = 0;
    mutable int32_t mShmBufferHeight     = 0;
    mutable bool mEglHandoffComplete     = false;
    bool mVisible                        = false;

    bool ensureShmBuffer();
    void releaseShmPlaceholder() const;

    // Input -- bound on demand from wl_seat capabilities. May be null if the
    // compositor's seat doesn't advertise the corresponding capability (e.g.
    // headless setups with no keyboard or pointer).
    struct wl_seat *mSeat         = nullptr;
    struct wl_keyboard *mKeyboard = nullptr;
    struct wl_pointer *mPointer   = nullptr;

    // Tracked modifier state, applied to every key event we push.
    bool mShiftDown   = false;
    bool mControlDown = false;
    bool mAltDown     = false;
    bool mSuperDown   = false;

    // Last pointer position. The compositor delivers wl_fixed_t surface
    // coords; we round to int and reuse the value for the (X, Y) fields of
    // button events (which don't carry their own coordinates).
    int mPointerX = 0;
    int mPointerY = 0;

    struct
    {
        int32_t width;
        int32_t height;
    } mGeometry{};

    struct
    {
        int32_t width;
        int32_t height;
    } mWindowSize{};

    bool mWaitForConfigure = false;

    bool mFullscreen = false;
    bool mMaximized  = false;
    bool mResize     = false;
    bool mActivated  = false;

    // fds[0] = wl_display fd (compositor events)
    // fds[1] = key-repeat timerfd (lazily created in initializeImpl)
    struct pollfd fds[2];

    // Key auto-repeat. Wayland makes this the client's responsibility -- the
    // compositor only sends one press + one release per physical key event,
    // and a separate `repeat_info` hint with the user's preferred rate/delay.
    // We mirror the X11/Win32 contract by re-pushing EVENT_KEY_PRESSED via a
    // timerfd, so downstream samples behave the same across backends.
    int mRepeatTimerFd   = -1;
    uint32_t mRepeatKey  = 0;    // Linux keycode currently being repeated; 0 = none
    int32_t mRepeatRate  = 25;   // chars/sec (libinput default if compositor doesn't tell us)
    int32_t mRepeatDelay = 600;  // ms before first repeat

    static void RegistryHandleGlobal(void *data,
                                     struct wl_registry *registry,
                                     uint32_t name,
                                     const char *interface,
                                     uint32_t version);
    static void RegistryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);

    bool initializeImpl(const std::string &name, int width, int height) override;

    static const struct wl_registry_listener registryListener;

    static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial);

    static const struct xdg_wm_base_listener xdg_wm_base_listener;

    static void handle_xdg_surface_configure(void *data,
                                             struct xdg_surface *xdg_surface,
                                             uint32_t serial);

    static const struct xdg_surface_listener xdg_surface_listener;

    static void handle_toplevel_configure(void *data,
                                          struct xdg_toplevel *toplevel,
                                          int32_t width,
                                          int32_t height,
                                          struct wl_array *states);

    static void handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel);

    static const struct xdg_toplevel_listener xdg_toplevel_listener;

    // zxdg_toplevel_decoration_v1: tells us what mode the compositor actually
    // granted in response to set_mode(SERVER_SIDE). We don't change behavior
    // either way -- the listener exists to satisfy the protocol contract and
    // to make a granted-CLIENT_SIDE response observable in logs.
    static void decoration_configure(void *data,
                                     struct zxdg_toplevel_decoration_v1 *decoration,
                                     uint32_t mode);
    static const struct zxdg_toplevel_decoration_v1_listener decoration_listener;

    // wl_seat: routes capability changes to keyboard / pointer setup.
    static void seat_capabilities(void *data, struct wl_seat *seat, uint32_t caps);
    static void seat_name(void *data, struct wl_seat *seat, const char *name);
    static const struct wl_seat_listener seat_listener;

    // wl_keyboard: keymap is ignored (we map raw Linux codes directly).
    // key/modifiers drive key events; leave disarms any in-flight key repeat.
    // Focus events come from xdg_toplevel.state.activated, not from
    // wl_keyboard enter/leave (see handle_toplevel_configure).
    static void keyboard_keymap(void *data,
                                struct wl_keyboard *keyboard,
                                uint32_t format,
                                int32_t fd,
                                uint32_t size);
    static void keyboard_enter(void *data,
                               struct wl_keyboard *keyboard,
                               uint32_t serial,
                               struct wl_surface *surface,
                               struct wl_array *keys);
    static void keyboard_leave(void *data,
                               struct wl_keyboard *keyboard,
                               uint32_t serial,
                               struct wl_surface *surface);
    static void keyboard_key(void *data,
                             struct wl_keyboard *keyboard,
                             uint32_t serial,
                             uint32_t time,
                             uint32_t key,
                             uint32_t state);
    static void keyboard_modifiers(void *data,
                                   struct wl_keyboard *keyboard,
                                   uint32_t serial,
                                   uint32_t mods_depressed,
                                   uint32_t mods_latched,
                                   uint32_t mods_locked,
                                   uint32_t group);
    static void keyboard_repeat_info(void *data,
                                     struct wl_keyboard *keyboard,
                                     int32_t rate,
                                     int32_t delay);
    static const struct wl_keyboard_listener keyboard_listener;

    // wl_pointer: enter/leave drive mouse enter/leave; motion drives mouse
    // moved; button drives press/release; axis drives wheel.
    static void pointer_enter(void *data,
                              struct wl_pointer *pointer,
                              uint32_t serial,
                              struct wl_surface *surface,
                              wl_fixed_t x,
                              wl_fixed_t y);
    static void pointer_leave(void *data,
                              struct wl_pointer *pointer,
                              uint32_t serial,
                              struct wl_surface *surface);
    static void pointer_motion(void *data,
                               struct wl_pointer *pointer,
                               uint32_t time,
                               wl_fixed_t x,
                               wl_fixed_t y);
    static void pointer_button(void *data,
                               struct wl_pointer *pointer,
                               uint32_t serial,
                               uint32_t time,
                               uint32_t button,
                               uint32_t state);
    static void pointer_axis(void *data,
                             struct wl_pointer *pointer,
                             uint32_t time,
                             uint32_t axis,
                             wl_fixed_t value);
    static void pointer_frame(void *data, struct wl_pointer *pointer);
    static void pointer_axis_source(void *data, struct wl_pointer *pointer, uint32_t source);
    static void pointer_axis_stop(void *data,
                                  struct wl_pointer *pointer,
                                  uint32_t time,
                                  uint32_t axis);
    static void pointer_axis_discrete(void *data,
                                      struct wl_pointer *pointer,
                                      uint32_t axis,
                                      int32_t discrete);
    static const struct wl_pointer_listener pointer_listener;
};

ANGLE_UTIL_EXPORT OSWindow *CreateWaylandWindow(void *nativeDisplay);

#endif  // UTIL_WAYLAND_WINDOW_H
