/*
    SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>

    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KWAYLAND_SERVER_XDGSHELL_INTERFACE_H
#define KWAYLAND_SERVER_XDGSHELL_INTERFACE_H

#include "global.h"
#include "resource.h"

#include <QSize>

#include <KWayland/Server/kwaylandserver_export.h>

namespace KWayland
{
namespace Server
{
class OutputInterface;
class SeatInterface;
class SurfaceInterface;
class XdgShellPopupInterface;
class XdgShellSurfaceInterface;
template<typename T>
class GenericShellSurface;

/**
 * Enum describing the different InterfaceVersion encapsulated in this implementation.
 *
 * @since 5.25
 **/
enum class XdgShellInterfaceVersion {
    /**
     * xdg_shell (unstable v5)
     **/
    UnstableV5,
    /**
     * zxdg_shell_v6 (unstable v6)
     * @since 5.39
     **/
    UnstableV6,
    /**
      xdg_wm_base (stable)
      @since 5.48
      */
    Stable,
};

/**
 * Flags describing how a popup should be reposition if constrained
 * @since 5.39
 */
enum class PositionerConstraint {
    /**
     * Slide the popup on the X axis until there is room
     */
    SlideX = 1 << 0,
    /**
     * Slide the popup on the Y axis until there is room
     */
    SlideY = 1 << 1,
    /**
     * Invert the anchor and gravity on the X axis
     */
    FlipX = 1 << 2,
    /**
     * Invert the anchor and gravity on the Y axis
     */
    FlipY = 1 << 3,
    /**
     * Resize the popup in the X axis
     */
    ResizeX = 1 << 4,
    /**
     * Resize the popup in the Y axis
     */
    ResizeY = 1 << 5,
};

Q_DECLARE_FLAGS(PositionerConstraints, PositionerConstraint)

/**
 *
 * @since 5.25
 **/
class KWAYLANDSERVER_EXPORT XdgShellInterface : public Global
{
    Q_OBJECT
public:
    virtual ~XdgShellInterface();

    /**
     * @returns The interface version used by this XdgShellInterface
     **/
    XdgShellInterfaceVersion interfaceVersion() const;

    /**
     * @returns The XdgShellSurfaceInterface for the @p native resource.
     **/
    // TODO KF6 make virtual
    XdgShellSurfaceInterface *getSurface(wl_resource *native);

    /**
     * Confirm the client is still alive and responding
     *
     * Will result in pong being emitted
     *
     * @returns unique identifier for this request
     * @since 5.39
     */
    quint32 ping(XdgShellSurfaceInterface *surface);

Q_SIGNALS:
    void surfaceCreated(KWayland::Server::XdgShellSurfaceInterface *surface);

    /**
     * Emitted whenever a new popup got created.
     *
     * A popup only gets created in response to an action on the @p seat.
     *
     *
     * @param surface The popup xdg shell surface which got created
     * @param seat The seat on which an action triggered the popup
     * @param serial The serial of the action on the seat
     *
     * XDGV5 only
     * Use both xdgPopupCreated and XdgShellPopupInterface::grabbed to cover both XDGV5 and XDGV6
     **/

    void popupCreated(KWayland::Server::XdgShellPopupInterface *surface, KWayland::Server::SeatInterface *seat, quint32 serial);

    /*
     * Emitted whenever a new popup gets created.
     *
     * @param surface The popup xdg shell surface which got created
     * @since 5.39
     */
    void xdgPopupCreated(KWayland::Server::XdgShellPopupInterface *surface);

    /*
     * Emitted in response to a ping request
     *
     * @param serial unique identifier for the request
     * @since 5.39
     */
    void pongReceived(quint32 serial);

    /*
     * Emitted when the application takes more than expected
     * to answer to a ping, this will always be emitted before
     * eventuallt pingTimeout gets emitted
     *
     * @param serial unique identifier for the request
     * @since 5.39
     */
    void pingDelayed(quint32 serial);

    /*
     * Emitted when the application doesn't answer to a ping
     * and the serve gave up on it
     *
     * @param serial unique identifier for the request
     * @since 5.39
     */
    void pingTimeout(quint32 serial);

protected:
    class Private;
    explicit XdgShellInterface(Private *d, QObject *parent = nullptr);

private:
    Private *d_func() const;
};

/**
 *
 * @since 5.25
 **/
class KWAYLANDSERVER_EXPORT XdgShellSurfaceInterface : public Resource
{
    Q_OBJECT
public:
    virtual ~XdgShellSurfaceInterface();

    /**
     * @returns The interface version used by this XdgShellSurfaceInterface
     **/
    XdgShellInterfaceVersion interfaceVersion() const;

    /**
     * States the Surface can be in
     **/
    enum class State {
        /**
         * The Surface is maximized.
         **/
        Maximized = 1 << 0,
        /**
         * The Surface is fullscreen.
         **/
        Fullscreen = 1 << 1,
        /**
         * The Surface is currently being resized by the Compositor.
         **/
        Resizing = 1 << 2,
        /**
         * The Surface is considered active. Does not imply keyboard focus.
         **/
        Activated = 1 << 3,
    };
    Q_DECLARE_FLAGS(States, State)

    /**
     * Sends a configure event to the Surface.
     * This tells the Surface the current @p states it is in and the @p size it should have.
     * If @p size has width and height at @c 0, the Surface can choose the size.
     *
     * The Surface acknowledges the configure event with {@link configureAcknowledged}.
     *
     * @param states The states the surface is in
     * @param size The requested size
     * @returns The serial of the configure event
     * @see configureAcknowledged
     * @see isConfigurePending
     **/
    quint32 configure(States states, const QSize &size = QSize(0, 0));

    /**
     * @returns @c true if there is a not yet acknowledged configure event.
     * @see configure
     * @see configureAcknowledged
     **/
    bool isConfigurePending() const;

    /**
     * @return The SurfaceInterface this XdgSurfaceV5Interface got created for.
     **/
    SurfaceInterface *surface() const;

    /**
     * @returns The title of this surface.
     * @see titleChanged
     **/
    QString title() const;
    QByteArray windowClass() const;

    /**
     * @returns Whether this Surface is a transient for another Surface, that is it has a parent.
     * @see transientFor
     **/
    bool isTransient() const;
    /**
     * @returns the parent surface if the surface is a transient for another surface
     * @see isTransient
     **/
    QPointer<XdgShellSurfaceInterface> transientFor() const;

    /**
     * Request the client to close the window.
     **/
    void close();

    /**
     * @brief windowGeometry
     * The geometry of the window within the buffer
     *
     * If invalid, the geometry of the buffer should be used instead
     * @since 5.59
     */
    QRect windowGeometry() const;

    /**
     * @returns The minimum size for the window specified by the client.
     * @since 5.65
     */
    QSize minimumSize() const;

    /**
     * @returns The maximum size for the window specified by the client.
     * @since 5.65
     */
    QSize maximumSize() const;

Q_SIGNALS:
    /**
     * Emitted whenever the title changes.
     *
     * @see title
     **/
    void titleChanged(const QString &);
    /**
     * Emitted whenever the window class changes.
     *
     * @see windowClass
     **/
    void windowClassChanged(const QByteArray &);
    /**
     * The surface requested a window move.
     *
     * @param seat The SeatInterface on which the surface requested the move
     * @param serial The serial of the implicit mouse grab which triggered the move
     **/
    void moveRequested(KWayland::Server::SeatInterface *seat, quint32 serial);
    /**
     * The surface requested a window resize.
     *
     * @param seat The SeatInterface on which the surface requested the resize
     * @param serial The serial of the implicit mouse grab which triggered the resize
     * @param edges A hint which edges are involved in the resize
     **/
    void resizeRequested(KWayland::Server::SeatInterface *seat, quint32 serial, Qt::Edges edges);
    void windowMenuRequested(KWayland::Server::SeatInterface *seat, quint32 serial, const QPoint &surfacePos);
    /**
     * The surface requested a change of maximized state.
     * @param maximized Whether the window wants to be maximized
     **/
    void maximizedChanged(bool maximized);
    /**
     * The surface requested a change of fullscreen state
     * @param fullscreen Whether the window wants to be fullscreen
     * @param output An optional output hint on which the window wants to be fullscreen
     **/
    void fullscreenChanged(bool fullscreen, KWayland::Server::OutputInterface *output);
    /**
     * The surface requested to be minimized.
     **/
    void minimizeRequested();
    /**
     * A configure event with @p serial got acknowledged.
     * @see configure
     **/
    void configureAcknowledged(quint32 serial);
    /**
     * Emitted whenever the parent surface changes.
     * @see isTransient
     * @see transientFor
     **/
    void transientForChanged();

    /**
     * Emitted whenever the maximum size hint changes
     * @since 5.39
     */
    void maxSizeChanged(const QSize &size);

    /**
     * Emitted whenever the minimum size hint changes
     * @since 5.39
     */
    void minSizeChanged(const QSize &size);

    /**
     * @brief windowGeometryChanged
     * @param windowGeometry the newly changed windowGeometry
     * @since 5.59
     */
    void windowGeometryChanged(const QRect &windowGeometry);

protected:
    class Private;
    explicit XdgShellSurfaceInterface(Private *p);

private:
    Private *d_func() const;
    friend class GenericShellSurface<XdgShellSurfaceInterface>;
};

/**
 *
 * @since 5.25
 **/
class KWAYLANDSERVER_EXPORT XdgShellPopupInterface : public Resource
{
    Q_OBJECT
public:
    virtual ~XdgShellPopupInterface();

    /**
     * @return The SurfaceInterface this XdgShellPopupInterface got created for.
     **/
    SurfaceInterface *surface() const;

    /*
     * Ask the popup surface to configure itself for the given configuration.
     *
     * @arg rect. The position of the surface relative to the transient parent
     * @since 5.39
     */
    quint32 configure(const QRect &rect);

    /**
     * @returns the parent surface.
     * @see transientOffset
     **/
    QPointer<SurfaceInterface> transientFor() const;

    /**
     * The offset of the Surface in the coordinate system of the SurfaceInterface this surface is a transient for.
     *
     * For XDG V6 this returns the point on the anchorRect defined by the anchor edge.
     *
     * @returns offset in parent coordinate system.
     * @see transientFor
     **/
    QPoint transientOffset() const;

    /**
     * The size of the surface that is to be positioned.
     *
     * @since 5.39
     */
    QSize initialSize() const;

    /**
     * The area this popup should be positioned around
     * @since 5.39
     */
    QRect anchorRect() const;

    /**
     * Which edge of the anchor should the popup be positioned around
     * @since 5.39
     */
    Qt::Edges anchorEdge() const;

    /**
     * An additional offset that should be applied to the popup from the anchor rect
     *
     * @since 5.39
     */
    QPoint anchorOffset() const;

    /**
     * Specifies in what direction the popup should be positioned around the anchor
     * i.e if the gravity is "bottom", then then the top of top of the popup will be at the anchor edge
     * if the gravity is top, then the bottom of the popup will be at the anchor edge
     *
     * @since 5.39
     */

    // DAVE left + right is illegal, so this is possible a useless return value? Maybe an enum with 9 entries left, topleft, top, ..
    Qt::Edges gravity() const;

    /**
     * Specifies how the compositor should position the popup if it does not fit in the requested position
     * @since 5.39
     */
    PositionerConstraints constraintAdjustments() const;

    /**
     * Dismiss this popup. This indicates to the client that it should destroy this popup.
     * The Compositor can invoke this method when e.g. the user clicked outside the popup
     * to dismiss it.
     **/
    void popupDone();

    /**
     * @brief windowGeometryChanged
     * @param windowGeometry the newly changed geometry of the window contents within the buffer
     * @since 5.59
     */
    QRect windowGeometry() const;

Q_SIGNALS:
    /**
     * A configure event with @p serial got acknowledged.
     * Note: XdgV6 only
     * @see configure
     * @since 5.39
     **/
    void configureAcknowledged(quint32 serial);

    /**
     * The client requested that this popup takes an explicit grab
     *
     * @param seat The seat on which an action triggered the popup
     * @param serial The serial of the action on the seat
     * @since 5.39
     */
    void grabRequested(KWayland::Server::SeatInterface *seat, quint32 serial);

    /**
     * @brief windowGeometryChanged
     * @param windowGeometry the newly changed windowGeometry
     * @since 5.59
     */
    void windowGeometryChanged(const QRect &windowGeometry);

protected:
    class Private;
    explicit XdgShellPopupInterface(Private *p);

private:
    friend class GenericShellSurface<XdgShellPopupInterface>;

    Private *d_func() const;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(PositionerConstraints)

}
}

Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface *)
Q_DECLARE_METATYPE(KWayland::Server::XdgShellPopupInterface *)
Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface::State)
Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface::States)

#endif
