#ifndef _diffusion_subscription_control_h_
#define _diffusion_subscription_control_h_ 1

/*
 * Copyright © 2014 - 2025 DiffusionData Ltd., All Rights Reserved.
 *
 * Use is subject to licence terms.
 *
 * NOTICE: All information contained herein is, and remains the
 * property of DiffusionData. The intellectual and technical
 * concepts contained herein are proprietary to DiffusionData and
 * may be covered by U.S. and Foreign Patents, patents in process, and
 * are protected by trade secret or copyright law.
 */

/**
 * @file subscription-control.h
 *
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief This feature allows a session to subscribe or unsubscribe other sessions to topics, on a
 * single server or across a cluster.
 *
 * Requests to subscribe sessions to topics can be submitted at any time even if the topics do not
 * exist at the server. Topic selectors are used on subscription to match against topics at the
 * server. The session will become subscribed to any topics that exist at the server that match the
 * selector (unless they are already subscribed, or the session has insufficient permission). The
 * subscription request is also retained at the server so that if any newly created topics match the
 * selector, the session will then become subscribed to it (unless a subsequent unsubscription
 * cancels it).
 *
 * Specific sessions (anywhere in a cluster) may be subscribed/unsubscribed if the `session_id` is
 * known.
 *
 * Subscriptions may also be requested using 'session filters' (see {@link session.h} for a full
 * description of session filters), where all sessions (anywhere in a cluster) that satisfy a
 * particular filter expression will be subscribed/unsubscribed. The filter is only evaluated once
 * against the current sessions that exist at the time - it is not retained and applied to any
 * sessions that are created later. In order to be notified of new sessions as they are created
 * session properties listeners can be used and those sessions subscribed as required based upon
 * their session properties.
 *
 * This feature also provides the ability to query the topic selections of another session.
 *
 * <H3>Access control</H3>
 * To subscribe other sessions to topics, a session must have `MODIFY_SESSION` permission, and
 * `SELECT_TOPIC` permission for the topic selector used for subscription. The
 * subscribed sessions will only be subscribed to matching topics for which they have `READ_TOPIC`
 * permission.
 *
 * To unsubscribe other sessions, a session must have `MODIFY_SESSION` permission, and
 * `SELECT_TOPIC` permission for the topic selector used for unsubscription.
 *
 * Operations that identify sessions using a session filter require the `VIEW_SESSION` permission.
 *
 * Getting the topic selections for a specified session requires `VIEW_SESSION` permission.
 *
 */

#include "diffusion-api-error.h"
#include "session.h"
#include "topic-selection.h"
#include "topics.h"


/**
 * @brief Callback for subscribe_client() and unsubscribe_client().
 *
 * @param session       The current active session.
 * @param context       User-supplied context from the originating call.
 *
 * @return HANDLER_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_subscription_control_cb)(SESSION_T *session, void *context);


/**
 * @brief Callback for `diffusion_subscribe_by_filter` and `diffusion_unsubscribe_by_filter`.
 *
 * @param number_selected       Number of sessions matching the filter.
 * @param context               User-supplied context from the originating call.
 *
 * @return HANDLER_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_subscription_control_by_filter_cb)(const int number_selected, void *context);


/**
 * @brief Callback handlers that may be registered so the caller may
 * receive notification that a subscription or unsubscription request
 * has completed.
 */
#define SUBSCRIPTION_CONTROL_HANDLERS                                                             \
    /** A callback to indicate that the subscription or  unsubscription request has completed. */ \
    on_subscription_control_cb on_complete;                                                       \
    /** Standard service error callback */                                                        \
    ERROR_HANDLER_T on_error;                                                                     \
    /** Standard service discard callback */                                                      \
    DISCARD_HANDLER_T on_discard;

typedef struct subscription_control_params_s
{
    /// Callback handlers.
    SUBSCRIPTION_CONTROL_HANDLERS
    /// ID of the session to be subscribed to or unsubscribed from
    /// the topic selector.
    SESSION_ID_T session_id;
    /// Topic selector.
    char *topic_selector;
    /// User-supplied context returned to callbacks.
    void *context;
} SUBSCRIPTION_CONTROL_PARAMS_T;

/**
 * Structure supplied when issuing a `diffusion_subscribe_by_filter` call.
 */
typedef struct diffusion_subscribe_by_filter_s
{
    /// The session filter expression.
    const char *filter;
    /// The topics to subscribe to.
    const char *topic_selector;
    /// Callback
    on_subscription_control_by_filter_cb on_subscribe_by_filter;
    /// Callback to handle errors. Can be NULL.
    ERROR_HANDLER_T on_error;
    /// Callback to handle discards. Can be NULL.
    DISCARD_HANDLER_T on_discard;
    /// User supplied context. Can be NULL.
    void *context;
} DIFFUSION_SUBSCRIBE_BY_FILTER_PARAMS_T;

/**
 * Structure supplied when issuing a `diffusion_unsubscribe_by_filter` call.
 */
typedef struct diffusion_unsubscribe_by_filter_s
{
    /// The session filter expression.
    const char *filter;
    /// The topics to unsubscribe from.
    const char *topic_selector;
    /// Callback
    on_subscription_control_by_filter_cb on_unsubscribe_by_filter;
    /// Callback to handle errors. Can be NULL.
    ERROR_HANDLER_T on_error;
    /// Callback to handle discards. Can be NULL.
    DISCARD_HANDLER_T on_discard;
    /// User supplied context. Can be NULL.
    void *context;
} DIFFUSION_UNSUBSCRIBE_BY_FILTER_PARAMS_T;

/**
 * @brief Callback for diffusion_get_topic_selections().
 *
 * @param session               The current active session.
 * @param topic_selections      The collections of topic selections.
 * @param context               User-supplied context from the originating call.
 *
 * @return HANDLER_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_get_topic_selections_cb)(SESSION_T *session, HASH_T *topic_selections, void *context);

#define DIFFUSION_GET_TOPIC_SELECTIONS_HANDLERS                   \
    /** A callback to indicate that the request has completed. */ \
    on_get_topic_selections_cb on_complete;                       \
    /** Standard service error callback */                        \
    ERROR_HANDLER_T on_error;                                     \
    /** Standard service discard callback */                      \
    DISCARD_HANDLER_T on_discard;

/**
 * Structure supplied when issuing a `diffusion_get_topic_selections` call.
 */
typedef struct diffusion_get_topic_selections_s
{
    /// Callback handlers.
    DIFFUSION_GET_TOPIC_SELECTIONS_HANDLERS
    /// ID of the session to be subscribed to or unsubscribed from
    /// the topic selector.
    SESSION_ID_T *session_id;
    /// User-supplied context returned to callbacks.
    void *context;

} DIFFUSION_GET_TOPIC_SELECTIONS_PARAMS_T;

/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Subscribe another session to topics.
 *
 * This is equivalent to calling `diffusion_subscribe_client_with_scope` specifying the
 * `DEFAULT_SELECTION_SCOPE` as the selection scope.
 *
 * @param session       The session handle. If NULL, this function returns immediately.
 *
 * @param params        Parameters describing the subscription control
 *                      request and callback handlers which may be invoked in response.
 */
void subscribe_client(SESSION_T *session, const SUBSCRIPTION_CONTROL_PARAMS_T params);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Subscribe another session to topics.
 *
 * New subscriptions will be established for existing topics that match the
 * provided topic selector and for which the subscribed session has
 * `READ_TOPIC` permission. The topic selector will be added to the
 * topic selections of the subscribed session, and re-evaluated when new
 * topics are added or the session's security roles change.
 *
 * A session that does not have `SELECT_TOPIC` permission for a topic
 * cannot subscribe directly, but can be subscribed indirectly using this
 * method.
 *
 * @param session           The session handle. If NULL, this function returns immediately.
 *
 * @param selection_scope   Specifies the scope of the selection. If NULL, this function returns immediately.
 *
 * @param params            Parameters describing the subscription control
 *                          request and callback handlers which may be invoked in response.
 *
 * @param api_error         Populated on API error. Can be NULL.
 *
 * @return True if the operation was successful. False, otherwise.
 *         In this case, if a non-NULL `api_error` pointer has been provided,
 *         this will be populated with the error information and should be freed
 *         with `diffusion_api_error_free`.
 *
 * @since 6.12
 */
bool diffusion_subscribe_client_with_scope(
    SESSION_T *session,
    const char *selection_scope,
    const SUBSCRIPTION_CONTROL_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Unsubscribe another session from topics.
 *
 * This is equivalent to calling `diffusion_unsubscribe_client_with_scope` specifying the
 * `DEFAULT_SELECTION_SCOPE` as the selection scope.
 *
 * @param session       The session handle. If NULL, this function returns immediately.
 *
 * @param params        Parameters describing the subscription control
 *                      request and callback handlers which may be invoked
 *                      in response.
 */
void unsubscribe_client(SESSION_T *session, const SUBSCRIPTION_CONTROL_PARAMS_T params);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Unsubscribe another session from topics.
 *
 * This function requests that a client (anywhere in a cluster) is unsubscribed from the
 * topics specified by a topic selector.
 *
 * @param session           The session handle. If NULL, this function returns immediately.
 *
 * @param selection_scope   Specifies the scope of the selection. If NULL, this function returns immediately.
 *
 * @param params            Parameters describing the subscription control
 *                          request and callback handlers which may be invoked in response.
 *
 * @param api_error         Populated on API error. Can be NULL.
 *
 *  @return True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 *
 * @since 6.12
 */
bool diffusion_unsubscribe_client_with_scope(
    SESSION_T *session,
    const char *selection_scope,
    const SUBSCRIPTION_CONTROL_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Subscribe sessions that satisfy a given session filter to topics. In a
 * clustered environment the filter will be applied to all servers in the
 * cluster.
 *
 * This is equivalent to calling `diffusion_subscribe_client_by_filter_with_scope` specifying the
 * `DEFAULT_SELECTION_SCOPE` as the selection scope.
 *
 * @param session       The session handle. If NULL, this function returns immediately.
 *
 * @param params        Parameters describing the `diffusion_subscribe_by_filter`
 *                      request and callback handlers which may be invoked in response.
 *
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return  True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 */
bool diffusion_subscribe_by_filter(SESSION_T *session, const DIFFUSION_SUBSCRIBE_BY_FILTER_PARAMS_T params, DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Subscribe sessions that satisfy a given session filter to topics. In a
 * clustered environment the filter will be applied to all servers in the
 * cluster.
 *
 * For each session (anywhere in a cluster) that matches the filter, new subscriptions will be
 * established for existing topics that match the provided topic selector
 * and for which the sessions has `READ_TOPIC` permission. The topic
 * selector will be added to the topic selections of the subscribed session,
 * and re-evaluated when new topics are added or the session's security
 * roles change.
 *
 * A session that does not have `SELECT_TOPIC` permission for a topic
 * cannot subscribe directly, but can be subscribed indirectly using this
 * method.
 *
 * @param session           The session handle. If NULL, this function returns immediately.
 *
 * @param selection_scope   Specifies the scope of the selection. If NULL, this function returns immediately.
 *
 * @param params            Parameters describing the `diffusion_subscribe_client_by_filter_with_scope`
 *                          request and callback handlers which may be invoked in response.
 *
 * @param api_error         Populated on API error. Can be NULL.
 *
 * @return  True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 *
 * @since 6.12
 */
bool diffusion_subscribe_client_by_filter_with_scope(
    SESSION_T *session,
    const char *selection_scope,
    const DIFFUSION_SUBSCRIBE_BY_FILTER_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Unsubscribe sessions (anywhere in a cluster) that satisfy a given session filter from
 * topics.
 *
 * This is equivalent to calling `diffusion_unsubscribe_client_by_filter_with_scope` specifying the
 * `DEFAULT_SELECTION_SCOPE` as the selection scope.
 *
 * @param session       The session handle. If NULL, this function returns immediately.
 *
 * @param params        Parameters describing the `diffusion_unsubscribe_by_filter`
 *                      request and callback handlers which may be invoked in response.
 *
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return  True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 */
bool diffusion_unsubscribe_by_filter(
    SESSION_T *session,
    const DIFFUSION_UNSUBSCRIBE_BY_FILTER_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Unsubscribe sessions (anywhere in a cluster) that satisfy a given session filter from
 * topics.
 *
 * @param session           The session handle. If NULL, this function returns immediately.
 *
 * @param selection_scope   Specifies the scope of the selection. If NULL, this function returns immediately.
 *
 * @param params            Parameters describing the `diffusion_unsubscribe_client_by_filter_with_scope`
 *                          request and callback handlers which may be invoked in response.
 *
 * @param api_error         Populated on API error. Can be NULL.
 *
 * @return  True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 *
 * @since 6.12
 */
bool diffusion_unsubscribe_client_by_filter_with_scope(
    SESSION_T *session,
    const char *selection_scope,
    const DIFFUSION_UNSUBSCRIBE_BY_FILTER_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Unsubscribe topics from all topic selection scopes.
 *
 * This can be used at any time whilst connected to reduce the set of topics
 * to which the session is subscribed or negate earlier subscription
 * requests and will apply to all scopes in use.
 *
 * @param session       The session handle. If NULL, this function returns immediately.
 *
 * @param params        Parameters describing the `diffusion_unsubscribe_client_all_scopes`
 *                      request and callback handlers which may be invoked in response.
 *
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return  True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 *
 * @since 6.12
 */
bool diffusion_unsubscribe_client_all_scopes(
    SESSION_T *session,
    const SUBSCRIPTION_CONTROL_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Unsubscribe topics from all topic selection scopes.
 *
 * Unsubscribe sessions that satisfy a given session filter from topics for
 * all scopes. In a clustered environment the filter will be applied to all
 * servers in the cluster.
 *
 * @param session       The session handle. If NULL, this function returns immediately.
 *
 * @param params        Parameters describing the `diffusion_unsubscribe_client_all_scopes`
 *                      request and callback handlers which may be invoked in response.
 *
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return  True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 *
 * @since 6.12
 */
bool diffusion_unsubscribe_client_by_filter_all_scopes(
    SESSION_T *session,
    const DIFFUSION_UNSUBSCRIBE_BY_FILTER_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);

/**
 * @ingroup PublicAPI_SubscriptionControl
 *
 * @brief Returns a map of the current topic selection state for a specified
 * session, keyed on topic selection scope.
 *
 * Each scope will have an ordered list of selections and/or deselections.
 *
 * The server conflates selections, so if there has been a selection that is
 * later rendered redundant by a deselection it will not be present. A scope
 * that has been used but fully deselected will therefore not be present in
 * the map and therefore no entry will have an empty list.
 *
 * If the session has no current selections the map will be empty.
 *
 * @param session       The session handle. If NULL, this function returns immediately.
 *
 * @param params        Parameters describing the `diffusion_get_topic_selections`
 *                      request and callback handlers which may be invoked in response.
 *
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return  True if the operation was successful. False, otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided,
 *          this will be populated with the error information and should be freed
 *          with `diffusion_api_error_free`.
 *
 * @since 6.12
 */
bool diffusion_get_topic_selections(
    SESSION_T *session,
    const DIFFUSION_GET_TOPIC_SELECTIONS_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);

#endif
