#ifndef LACTIVATIONTOKENMANAGER_H
#define LACTIVATIONTOKENMANAGER_H

#include <LFactoryObject.h>
#include <LActivationToken.h>

/**
 * @brief Activation Token Manager
 *
 * @anchor lactivationtokenmanager_detailed
 *
 * The [XDG Activation](https://wayland.app/protocols/xdg-activation-v1) protocol allows clients to pass focus to another client's surface.
 *
 * The protocol functions as follows:
 *
 * 1. The "activator" client requests a new token from the compositor via the createTokenRequest() method.
 *    During the request, the client can provide additional (optional) information, such as the application ID of the "target" client
 *    (LActivationToken::toActivateAppId()), the event that triggered the request (LActivationToken::triggeringEvent()),
 *    and the surface that received the triggering event (LActivationToken::origin()).
 *
 * 2. If the request is accepted, the compositor generates an activation token (a unique and random string)
 *    and sends it to the client. The token is stored in the tokens() map.
 *
 * 3. The "activator" client then passes the token to the "target" client via its own communication channel (e.g., D-Bus, the **XDG_ACTIVATION_TOKEN** environment variable).
 *
 * 4. The "target" client can use the token to activate one of its surfaces by invoking the activateSurfaceRequest() method.
 *
 * The default implementations of createTokenRequest() and activateSurfaceRequest() only accept requests that provide a valid triggering event and
 * destroy the token after it is used, as well as those that haven't been used in the last 10 seconds.
 * This is for security reasons, tokens can remain valid indefinitely if desired.
 *
 * There is a unique instance of this class, created within LCompositor::createObjectRequest(),
 * which can be accessed globally via Louvre::activationTokenManager().
 *
 * To disable this protocol, remove its global from LCompositor::createGlobalsRequest()
 *
 * @see LActivationToken
 */
class Louvre::LActivationTokenManager : public LFactoryObject
{
public:
    static constexpr LFactoryObject::Type FactoryObjectType = LFactoryObject::Type::LActivationTokenManager;

    /**
     * @brief LActivationTokenManager class constructor.
     *
     * There is only one instance of this class, which can be accessed globally via activationTokenManager().
     *
     * @param params Internal parameters provided in LCompositor::createObjectRequest().
     */
    LActivationTokenManager(const void *params) noexcept;

    /**
     * @brief Destructor of the LActivationTokenManager class.
     *
     * Invoked after LCompositor::onAnticipatedObjectDestruction().
     */
    ~LActivationTokenManager() noexcept;

    LCLASS_NO_COPY(LActivationTokenManager)

    /**
     * @brief Retrieve available tokens.
     *
     * Provides all currently available tokens, which clients can use to activate surfaces.
     *
     * To expire tokens, use LActivationToken::destroy() or the destroyTokensOlderThanMs() method.
     */
    const std::unordered_map<std::string, LActivationToken*> &tokens() const noexcept
    {
        return m_tokens;
    }

    /**
     * @brief Retrieve the current token.
     *
     * This method provides access to the token used during createTokenRequest() or activateSurfaceRequest() requests.
     * If neither request is in progress, this method returns `nullptr`.
     *
     * The token is not provided as an argument to those requests because the default implementation of activateSurfaceRequest()
     * triggers LToplevelRole::activateRequest() if the surface has a toplevel role. This way, the token can be accessed
     * from LToplevelRole::activateRequest() if needed.
     */
    LActivationToken *token() const noexcept
    {
        return m_token.get();
    }

    /**
     * @brief Destroy tokens older than the specified number of milliseconds.
     *
     * The default implementation of activateSurfaceRequest() uses this method to destroy all tokens older than 10 seconds
     * to prevent unnecessary tokens from remaining active.
     *
     * @see LActivationToken::destroy() and LActivationToken::created().
     */
    void destroyTokensOlderThanMs(UInt32 ms);

    /// @name Virtual Methods
    /// @{

    /**
     * @brief Request to create an activation token.
     *
     * This request is triggered each time a client wants to create a new activation token.
     * The activation token data can be accessed via token(). If the token is denied, `token()->destroy()` must be called,
     * otherwise, the token will be retained in the tokens() map, and clients will be allowed to use it for activateSurfaceRequest().
     *
     * The default implementation only accepts tokens generated by the currently focused client.
     *
     * #### Default implementation
     * @snippet LActivationTokenManagerDefault.cpp createTokenRequest
     */
    virtual void createTokenRequest();

    /**
     * @brief Request to activate a surface.
     *
     * This request indicates that a client wants to activate one of its surfaces and is only triggered if the client
     * provides one of the tokens(), which can be accessed during the request with token().
     *
     * The default implementation verifies if the token is recent and destroys it immediately after use,
     * along with any tokens older than 10 seconds.
     *
     * If the token is not destroyed, LActivationToken::timesUsed() is incremented by 1.
     *
     * @param surface The surface to activate.
     *
     * #### Default implementation
     * @snippet LActivationTokenManagerDefault.cpp activateSurfaceRequest
     */
    virtual void activateSurfaceRequest(LSurface *surface);

    /// @}

private:
    friend class Protocols::XdgActivation::GXdgActivation;
    friend class Protocols::XdgActivation::RXdgActivationToken;
    friend class LActivationToken;
    std::unordered_map<std::string, LActivationToken*> m_tokens;
    LWeak<LActivationToken> m_token;
};

#endif // LACTIVATIONTOKENMANAGER_H
