#ifndef _diffusion_topic_views_h_
#define _diffusion_topic_views_h_ 1

/*
 * Copyright © 2019 - 2023 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 topic-views.h
 *
 * Allows a client session to manage topic views.
 *
 * A topic view maps one part of a server's topic tree to another. It
 * dynamically creates a set of <em>reference topics</em> from a set of
 * <em>source topics</em>, based on a declarative <em>topic view
 * specification</em>. The capabilities of topic views range from simple
 * mirroring of topics within the topic tree to advanced capabilities including
 * publication of partial values, expanding a single topic value into many
 * topics, changing topic values, inserting values from another topics, throttling
 * the rate of publication, and applying a fixed delay to the publication.
 *
 * A topic view can even map topics from another server (in a different
 * cluster). This capability is referred to as 'remote topic views'. The view
 * can specify the server that the <em>source topics</em> are hosted on in terms
 * of a <em>remote server</em> (see {@link remote-servers.h remote servers} for
 * details of how to create and maintain remote servers).
 *
 * Each reference topic has a single source topic and has the same topic type as
 * its source topic. Reference topics are read-only (they cannot be updated),
 * nor can they be created or removed directly. Otherwise, they behave just like
 * standard topics. A client session can subscribe to a reference topic, and can
 * fetch the reference topic's current value if it has one.
 *
 * The source topics of a topic view are defined by a topic selector. One or
 * more reference topics created for each source topic, according to the
 * topic view. If a source topic is removed, reference topics that are derived
 * from it will automatically be removed. If a topic is added that matches the
 * source topic selector of a topic view, a corresponding reference topic will be
 * created. Removing a topic view will remove all of its reference topics.
 *
 *
 * <h2>Topic view specifications</h2>
 *
 * Topic views are specified using a Domain Specific Language (DSL) which
 * provides many powerful features for manipulating topic data. For a full and
 * detailed description of the topic views DSL see the <a href=
 * "https://docs.diffusiondata.com/docs/latest/manual/html/developerguide/client/topics/topiccontrol/topic_views.html">user
 * manual</a>.
 *
 * The following is a simple topic view specification that mirrors all topics
 * below the path `a` to reference topics below the path `b`.
 *
 * `map ?a// to b/<path(1)>`
 *
 * A topic view with this specification will map a source topic at the path
 * `a/x/y/z` to a reference topic at the path `b/x/y/z`.
 * The specification is simple, so the reference topic will exactly mirror the
 * source topic. Other topic views features allow a single topic to be mapped to
 * many reference topics and have the data transformed in the process.
 *
 *
 * <h2>Topic view persistence and replication</h2>
 *
 * Reference topics are neither replicated nor persisted. They are created and
 * removed based on their source topics. However, topic views are replicated and
 * persisted. A server that restarts will restore topic views during recovery.
 * Each topic view will then create reference topics based on the source topics
 * that have been recovered.
 *
 * The server records all changes to topic views in a persistent store. Topic
 * views are restored if the server is started.
 *
 * If a server belongs to a cluster, topic views (and remote servers) will be
 * replicated to each server in the cluster. Topic views are evaluated locally
 * within a server. Replicated topic views that select non-replicated source
 * topics can create different reference topics on each server in the cluster.
 * When remote topic views are in use, each server in the cluster will make a
 * connection to the specified remote server and will separately manage their
 * remote topic views.
 *
 *
 * <h2>Access control</h2>
 *
 * The following access control restrictions are applied:
 *
 * <ul>
 *  <li>To list the topic views, a session needs the
 *      {@link GLOBAL_PERMISSION_READ_TOPIC_VIEWS READ_TOPIC_VIEWS} global permission.
 *  <li>To create, replace, or remove a topic view, a session needs the
 *      {@link GLOBAL_PERMISSION_MODIFY_TOPIC_VIEWS MODIFY_TOPIC_VIEWS} global permission
 *      and {@link PATH_PERMISSION_SELECT_TOPIC SELECT_TOPIC} permission
 *      for the source topic selector.
 *  <li>Each topic view records the principal and security roles of the
 *      session that created it as the <em>topic view security
 *      context</em>. When a topic view is evaluated, this security context
 *      is used to constrain the creation of reference topics. A reference
 *      topic will only be created if the security context has
 *      {@link PATH_PERMISSION_READ_TOPIC READ_TOPIC}
 *      permission for the source topic path, and
 *      {@link PATH_PERMISSION_MODIFY_TOPIC MODIFY_TOPIC} permission
 *      for the reference topic path. The topic view security context is
 *      copied from the creating session at the time the topic view is
 *      created or replaced, and is persisted with the topic view. The
 *      topic view security context is not updated if the roles associated
 *      with the session are changed.
 * </ul>
 */

#include "diffusion-api-error.h"
#include "session.h"

/**
 * Opaque topic view struct
 */
typedef struct DIFFUSION_TOPIC_VIEW_T DIFFUSION_TOPIC_VIEW_T;

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Return a memory allocated copy of the topic view's name. `free` should be called
 *        on this pointer when no longer needed.
 *
 * @param topic_view the topic view
 *
 * @return the topic view's name.
 */
char *diffusion_topic_view_get_name(const DIFFUSION_TOPIC_VIEW_T *topic_view);

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Return a memory allocated copy of the topic view's specification. `free` should be called
 *        on this pointer when no longer needed.
 *
 * @param topic_view the topic view
 *
 * @return the topic view's specification.
 */
char *diffusion_topic_view_get_specification(const DIFFUSION_TOPIC_VIEW_T *topic_view);

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Return a memory allocated `SET_T` of the topic view's roles. `set_free` should be called
 *        on this pointer when no longer needed.
 *
 * @param topic_view the topic view
 *
 * @return the topic view's roles used when evaluating permissions.
 */
SET_T *diffusion_topic_view_get_roles(const DIFFUSION_TOPIC_VIEW_T *topic_view);

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Returns a memory allocated copy of a `DIFFUSION_TOPIC_VIEW_T`. `diffusion_topic_view_free` should
 *        be called on this pointer when no longer needed.
 *
 * @param topic_view the topic_view to be copied.
 *
 * @return a copy of a `DIFFUSION_TOPIC_VIEW_T`. NULL, if `event` is NULL.
 */
DIFFUSION_TOPIC_VIEW_T *diffusion_topic_view_dup(const DIFFUSION_TOPIC_VIEW_T *topic_view);

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Free a memory allocated `DIFFUSION_TOPIC_VIEW_T`
 *
 * @param topic_view the `DIFFUSION_TOPIC_VIEW_T` to be freed.
 */
void diffusion_topic_view_free(DIFFUSION_TOPIC_VIEW_T *topic_view);

/**
 * @brief Callback when a topic view has successfully been created.
 *
 * @param topic_view The created topic view.
 * @param context    User supplied context.
 *
 * @return HANDLERS_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_topic_view_created_cb)(const DIFFUSION_TOPIC_VIEW_T *topic_view, void *context);

/**
 * @brief Callback when a response is received from the server regarding a topic view operation.
 *
 * @param context User supplied context.
 *
 * @return HANDLERS_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_topic_view_response_cb)(void *context);

/**
 * @brief Callback when a topic view has successfully been retrieved.
 *
 * @param topic_view The retrieved topic view.
 * @param context    User supplied context.
 *
 * @return HANDLERS_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_topic_view_retrieved_cb)(const DIFFUSION_TOPIC_VIEW_T *topic_view, void *context);

/**
 * @brief Callback when a response is received from the server regarding a
 *        `diffusion_topic_views_list_topic_views` operation.
 *
 * @param topic_views A `LIST_T` containing `DIFFUSION_TOPIC_VIEW_T`s
 * @param context     User supplied context.
 *
 * @return HANDLERS_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_topic_views_list_cb)(const LIST_T *topic_views, void *context);

/**
 * @brief Structure describing a request to create a topic view.
 * If the name is empty, the operation will fail.
 */
typedef struct diffusion_create_topic_view_params_s
{
    /// The name of the view to be created.
    const char *view;
    /// The specification of the view using the DSL.
    const char *specification;
    /// Callback when the topic view is created.
    on_topic_view_created_cb on_topic_view_created;
    /// 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 returned to callbacks.
    void *context;
} DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T;

/**
 * @brief Structure describing a request to list topic views
 */
typedef struct diffusion_topic_views_list_params_s
{
    /// Callback when a list of topic views is
    /// received.
    on_topic_views_list_cb on_topic_views_list;
    /// 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 returned to callbacks.
    void *context;
} DIFFUSION_TOPIC_VIEWS_LIST_PARAMS_T;

/**
 * @brief Structure describing a request to remove a topic view
 */
typedef struct diffusion_remove_topic_view_params_s
{
    /// The name of the view to be removed.
    const char *view;
    /// Callback when the topic view is removed.
    on_topic_view_response_cb on_topic_view_removed;
    /// 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 returned to callbacks.
    void *context;
} DIFFUSION_REMOVE_TOPIC_VIEW_PARAMS_T;

/**
 * @brief Structure describing a request to get a topic view
 */
typedef struct diffusion_get_topic_view_params_s
{
    /// The name of the view to be received.
    const char *name;
    /// Callback when the topic view is received.
    on_topic_view_retrieved_cb on_topic_view;
    /// 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 returned to callbacks.
    void *context;
} DIFFUSION_GET_TOPIC_VIEW_PARAMS_T;

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Create a new named topic view.
 *
 * If a view with the same name already exists the new view will replace
 * the existing view.
 *
 * @param session   The current session. If NULL, this function returns immediately.
 * @param params    Parameters defining the `diffusion_topic_views_create_topic_view` request
 *                  and callbacks.
 * @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_topic_views_create_topic_view(
    SESSION_T *session,
    const DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief List all the topic views that have been created.
 *
 * @param session   The current session. If NULL, this function returns immediately.
 * @param params    Parameters defining the `diffusion_topic_views_list_topic_views` request
 *                  and callbacks.
 * @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_topic_views_list_topic_views(
    SESSION_T *session,
    const DIFFUSION_TOPIC_VIEWS_LIST_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);

/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Remove a named topic view if it exists.
 *
 * If the named view does not exist the handler `on_topic_view_removed` will complete
 * successfully.
 *
 * @param session   The current session. If NULL, this function returns immediately.
 * @param params    Parameters defining the `diffusion_topic_views_remove_topic_view` request
 *                  and callbacks.
 * @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_topic_views_remove_topic_view(
    SESSION_T *session,
    const DIFFUSION_REMOVE_TOPIC_VIEW_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_TopicViews
 *
 * @brief Get a named Topic View.
 *
 * If the named view does not exist the handler `on_topic_view` will complete
 * successfully with a NULL `topic_view`.
 *
 * @param session   The current session. If NULL, this function returns immediately.
 * @param params    Parameters defining the `diffusion_topic_views_get_topic_view` request
 *                  and callbacks.
 * @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_topic_views_get_topic_view(
    SESSION_T *session,
    const DIFFUSION_GET_TOPIC_VIEW_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


#endif
