/**
 * @file sysrepo.h
 * @author Michal Vasko <mvasko@cesnet.cz>
 * @brief public API sysrepo header
 *
 * @copyright
 * Copyright 2018 Deutsche Telekom AG.
 * Copyright 2018 - 2019 CESNET, z.s.p.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef _SYSREPO_H
#define _SYSREPO_H

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#ifdef __APPLE__
    #include <sys/types.h>
#endif

#include <libyang/libyang.h>

#ifdef __cplusplus
extern "C" {
#endif

////////////////////////////////////////////////////////////////////////////////
// Logging API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup log_api Logging API
 * @{
 */

/**
 * @brief Sysrepo error codes.
 */
typedef enum sr_error_e {
    SR_ERR_OK = 0,             /**< No error. */
    SR_ERR_INVAL_ARG,          /**< Invalid argument. */
    SR_ERR_LY,                 /**< Error generated by libyang. */
    SR_ERR_SYS,                /**< System function call failed. */
    SR_ERR_NOMEM,              /**< Not enough memory. */
    SR_ERR_NOT_FOUND,          /**< Item not found. */
    SR_ERR_EXISTS,             /**< Item already exists. */
    SR_ERR_INTERNAL,           /**< Other internal error. */
    SR_ERR_UNSUPPORTED,        /**< Unsupported operation requested. */
    SR_ERR_VALIDATION_FAILED,  /**< Validation of the changes failed. */
    SR_ERR_OPERATION_FAILED,   /**< An operation failed. */
    SR_ERR_UNAUTHORIZED,       /**< Operation not authorized. */
    SR_ERR_LOCKED,             /**< Requested resource is already locked. */
    SR_ERR_TIME_OUT,           /**< Time out has expired. */
    SR_ERR_CALLBACK_FAILED,    /**< User callback failure caused the operation to fail. */
    SR_ERR_CALLBACK_SHELVE,    /**< User callback has not processed the event and will do so
                                    on some future event processing. */
} sr_error_t;

/**
 * @brief Returns the error message corresponding to the error code.
 *
 * @param[in] err_code Error code.
 * @return Error message (statically allocated, do not free).
 */
const char *sr_strerror(int err_code);

/**
 * @brief Log levels used to determine if message of certain severity should be printed.
 */
typedef enum {
    SR_LL_NONE = 0,  /**< Do not print any messages. */
    SR_LL_ERR,       /**< Print only error messages. */
    SR_LL_WRN,       /**< Print error and warning messages. */
    SR_LL_INF,       /**< Besides errors and warnings, print some other informational messages. */
    SR_LL_DBG,       /**< Print all messages including some development debug messages. */
} sr_log_level_t;

/**
 * @brief Enables / disables / changes log level (verbosity) of logging to
 * standard error output.
 *
 * By default, logging to stderr is disabled. Setting log level to any value
 * other than ::SR_LL_NONE enables the logging to stderr. Setting log level
 * back to ::SR_LL_NONE disables the logging to stderr.
 *
 * @note Please note that this will overwrite your libyang logging settings.
 * Also, only libyang errors are printed, if enabled.
 *
 * @param[in] log_level Requested log level (verbosity).
 */
void sr_log_stderr(sr_log_level_t log_level);

/**
 * @brief Learn current standard error output log level.
 *
 * @return stderr log level.
 */
sr_log_level_t sr_log_get_stderr(void);

/**
 * @brief Enables / disables / changes log level (verbosity) of logging to system log.
 *
 * By default, logging into syslog is disabled. Setting log level to any value
 * other than ::SR_LL_NONE enables the logging into syslog. Setting log level
 * back to ::SR_LL_NONE disables the logging into syslog.
 *
 * Library messages are logged with LOG_USER facility and plugin (syrepo-plugind) messages are
 * logged with LOG_DAEMON facility.
 *
 * @note Please note that enabling logging into syslog will overwrite your syslog
 * connection settings (calls openlog), if you are connected to syslog already and
 * also libyang logging settings.
 *
 * @param[in] app_name Name of the application. If not set, "sysrepo" will be used.
 * @param[in] log_level Requested log level (verbosity).
 */
void sr_log_syslog(const char *app_name, sr_log_level_t log_level);

/**
 * @brief Learn current system log log level.
 *
 * @return syslog log level.
 */
sr_log_level_t sr_log_get_syslog(void);

/**
 * @brief Sets callback that will be called when a log entry would be populated.
 *
 * @param[in] level Verbosity level of the log entry.
 * @param[in] message Message of the log entry.
 */
typedef void (*sr_log_cb)(sr_log_level_t level, const char *message);

/**
 * @brief Sets callback that will be called when a log entry would be populated.
 * Callback will be called for every message __regardless__ of any log level.
 *
 * @param[in] log_callback Callback to be called when a log entry would populated.
 */
void sr_log_set_cb(sr_log_cb log_callback);

/** @} logging */

////////////////////////////////////////////////////////////////////////////////
// Connection / Session Management
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup conn_sess_api Connection and Session API
 * @{
 */

/**
 * @brief Sysrepo connection.
 */
typedef struct sr_conn_ctx_s sr_conn_ctx_t;

/**
 * @brief Sysrepo session on a connection.
 */
typedef struct sr_session_ctx_s sr_session_ctx_t;

/**
 * @brief Flags used to override default connection handling by ::sr_connect call.
 */
typedef enum sr_conn_flag_e {
    SR_CONN_DEFAULT = 0,            /**< No special behaviour. */
    SR_CONN_CACHE_RUNNING = 1,      /**< Always cache running datastore data which makes mainly repeated retrieval of data
                                         much faster. Affects all sessions created on this connection. */
    SR_CONN_NO_SCHED_CHANGES = 2,   /**< Do not parse internal modules data and apply any scheduled changes. Makes
                                         creating the connection faster but, obviously, scheduled changes are not applied. */
    SR_CONN_ERR_ON_SCHED_FAIL = 4,  /**< If applying any of the scheduled changes fails, do not create a connection
                                         and return an error. */
} sr_conn_flag_t;

/**
 * @brief Options overriding default connection handling by ::sr_connect call,
 * it is supposed to be bitwise OR-ed value of any ::sr_conn_flag_t flags.
 */
typedef uint32_t sr_conn_options_t;

/**
 * @brief [Datastores](@ref datastores) that sysrepo supports. To change which datastore a session operates on,
 * use ::sr_session_switch_ds.
 */
typedef enum sr_datastore_e {
    SR_DS_STARTUP = 0,     /**< Contains configuration data that will be loaded when a device starts. */
    SR_DS_RUNNING = 1,     /**< Contains current configuration data. */
    SR_DS_CANDIDATE = 2,   /**< Contains prepared configuration data that do not affect actual configuration. */
    SR_DS_OPERATIONAL = 3, /**< Contains currently used configuration (see [operational datastore](@ref oper_ds)). */
} sr_datastore_t;

/**
 * @brief A single, detailed error message. Used in sr_error_info_s
 */
typedef struct sr_error_info_msg_s {
  char *message;   /**< Error message. */
  char *xpath;     /**< [XPath](@ref paths) (or rather path) to the node where the error has been discovered. */
} sr_error_info_msg_t;

/**
 * @brief Detailed sysrepo session error information.
 */
typedef struct sr_error_info_s {
    sr_error_t err_code; /**< Error code. */
    sr_error_info_msg_t *err; /**< Array of all generated errors. */
    size_t err_count;    /**< Error message count. */
} sr_error_info_t;

/**
 * @brief Connects to the sysrepo datastore. If possible (no other connections exist), also apply
 * any scheduled changes.
 *
 * @note Do not use `fork()` after creating a connection. Sysrepo internally stores PID of
 * every created connection and this way a mismatch of PID and connection is created.
 *
 * @param[in] opts Options overriding default connection handling by this call.
 * @param[out] conn Connection that can be used for subsequent API calls
 * (automatically allocated, it is supposed to be released by the caller using ::sr_disconnect).
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_connect(const sr_conn_options_t opts, sr_conn_ctx_t **conn);

/**
 * @brief Disconnect from the sysrepo datastore.
 *
 * Cleans up and frees connection context allocated by ::sr_connect. All sessions and subscriptions
 * started within the connection will be automatically stopped and cleaned up too.
 *
 * @note Connection and all its associated sessions and subscriptions can no longer be used even on error.
 *
 * @param[in] conn Connection acquired with ::sr_connect call.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_disconnect(sr_conn_ctx_t *conn);

/**
 * @brief Learn the current global number of (some possibly dead) connections.
 *
 * @param[out] conn_count Current number of connections.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_connection_count(uint32_t *conn_count);

/**
 * @brief Get the _libyang_ context used by a connection. Can be used in an application for working with data
 * and schemas. Do **NOT** change this context!
 *
 * @param[in] conn Connection to use.
 * @return Const libyang context.
 */
const struct ly_ctx *sr_get_context(sr_conn_ctx_t *conn);

/**
 * @brief Callback to be called before applying a diff. Set it using ::sr_set_diff_check_callback.
 *
 * @param[in] session Implicit session (do not stop) with information about the event originator session IDs.
 * @param[in] diff Diff to be applied.
 * @return Error code (::SR_ERR_OK on success).
 */
typedef int (*sr_diff_check_cb)(sr_session_ctx_t *session, const struct lyd_node *diff);

/**
 * @brief Set callback for checking every diff before it is applied on the datastore.
 * The diff is final (only CRUD operations) but without any implicit changes caused
 * by validation. This callback is primarily meant to allow full NACM
 * (NETCONF Access Control) to be performed by a NETCONF server.
 *
 * Required ROOT access.
 *
 * @param[in] conn Connection, whose all sessions diffs will be passed to this callback.
 * @param[in] callback Callback to call for every diff.
 */
void sr_set_diff_check_callback(sr_conn_ctx_t *conn, sr_diff_check_cb callback);

/**
 * @brief Start a new session.
 *
 * @param[in] conn Connection acquired with ::sr_connect call.
 * @param[in] datastore Datastore on which all sysrepo functions within this
 * session will operate. Later on, datastore can be later changed using
 * ::sr_session_switch_ds call. Functionality of some sysrepo calls does not depend on
 * datastore. If your session will contain just calls like these, you can pass
 * any valid value (e.g. ::SR_DS_RUNNING).
 * @param[out] session Session context that can be used for subsequent API
 * calls (automatically allocated, can be released by calling ::sr_session_stop).
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_session_start(sr_conn_ctx_t *conn, const sr_datastore_t datastore, sr_session_ctx_t **session);

/**
 * @brief Stop current session and releases resources tied to the session.
 *
 * Also releases any locks held and frees subscriptions created (only) by this session.
 *
 * @note Session can no longer be used even on error. Subscriptions, even
 * if they no longer handle any events are **never** freed and should be manually
 * freed using ::sr_unsubscribe.
 *
 * @param[in] session Session context acquired with ::sr_session_start call.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_session_stop(sr_session_ctx_t *session);

/**
 * @brief Use notification buffering for the session.
 *
 * When a notification is sent using this session for
 * a module that supports replay (notification should be stored),
 * the notification function does not wait until it is stored
 * but delegates this work to a special thread and returns.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) whose notifications will be buffered.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_session_notif_buffer(sr_session_ctx_t *session);

/**
 * @brief Change datastore which the session operates on. All subsequent
 * calls will be issued on the chosen datastore. Previous calls are not
 * affected.
 *
 * @param[in] session Session to modify.
 * @param[in] ds New datastore that will be operated on.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_session_switch_ds(sr_session_ctx_t *session, sr_datastore_t ds);

/**
 * @brief Learn the datastore a session operates on.
 *
 * @param[in] session Session to use.
 * @return Datastore of the session.
 */
sr_datastore_t sr_session_get_ds(sr_session_ctx_t *session);

/**
 * @brief Retrieve information about the error that has occurred
 * during the last operation executed within provided session.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[out] error_info Detailed error information. Be aware that
 * returned pointer may change by the next API call executed within the provided
 * session. Do not free or modify returned values.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_error(sr_session_ctx_t *session, const sr_error_info_t **error_info);

/**
 * @brief Set detailed error information into provided session. Used to notify
 * the client library about errors that occurred in the application code.
 * Does not print the message.
 *
 * @note Intended for change, RPC/action, or operational callbacks to be used
 * on the provided session.
 *
 * @param[in] session Implicit session provided in a callback.
 * @param[in] path Optional [path](@ref paths) of the node where the error has occurred.
 * @param[in] format Human-readable format of the error message.
 * @param[in] ... Format parameters.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_set_error(sr_session_ctx_t *session, const char *path, const char *format, ...);

/**
 * @brief Return the assigned session ID of the sysrepo session.
 *
 * @param [in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @return sysrepo SID or 0 in case of error.
 */
uint32_t sr_session_get_id(sr_session_ctx_t *session);

/**
 * @brief Set a NETCONF session ID for a sysrepo session. Any application
 * callbacks handling operations initiated by this session will be able to
 * read this ID from the session provided.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to change.
 * @param[in] nc_sid NETCONF session ID of a NETCONF session running on top of this session.
 */
void sr_session_set_nc_id(sr_session_ctx_t *session, uint32_t nc_sid);

/**
 * @brief Learn NETCONF session ID from a sysrepo session. Either reads back
 * the value set by ::sr_session_set_nc_id or of the initiating session when used
 * in an application callback.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @return Session NETCONF SID.
 */
uint32_t sr_session_get_nc_id(sr_session_ctx_t *session);

/**
 * @brief Set the effective user of a session to a different one that the process owner.
 *
 * Required ROOT access.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to change.
 * @param[in] user System user.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_session_set_user(sr_session_ctx_t *session, const char *user);

/**
 * @brief Get the effective user of a session.
 *
 * Required ROOT access.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @return Session user.
 */
const char *sr_session_get_user(sr_session_ctx_t *session);

/**
 * @brief Get the connection the session was created on.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @return Sysrepo connection.
 */
sr_conn_ctx_t *sr_session_get_connection(sr_session_ctx_t *session);

/** @} connsess */

////////////////////////////////////////////////////////////////////////////////
// Schema Manipulation API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup schema_api Schema API
 * @{
 */

/**
 * @brief Get the common path prefix for all sysrepo files.
 *
 * @note If a specific path was changed during compilation, it does not use this
 * path prefix.
 *
 * @return Sysrepo repository path.
 */
const char *sr_get_repo_path(void);

/**
 * @brief Install a new schema (module) into sysrepo. Deferred until there are no connections!
 *
 * @param[in] conn Connection to use.
 * @param[in] schema_path Path to the new schema. Can have either YANG or YIN extension/format.
 * @param[in] search_dirs Optional search directories for import schemas, supports the format `<dir>[:<dir>]*`.
 * @param[in] features Array of enabled features.
 * @param[in] feat_count Number of enabled features.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_install_module(sr_conn_ctx_t *conn, const char *schema_path, const char *search_dirs, const char **features,
        int feat_count);

/**
 * @brief Set newly installed module startup and running data. It is necessary in case empty data are not valid
 * for the particular schema (module).
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module to set startup data.
 * @param[in] data Data to set. Must be NULL if \p data_path is set.
 * @param[in] data_path Data file with the data to set. Must be NULL if \p data is set.
 * @param[in] format Format of the data/file.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_install_module_data(sr_conn_ctx_t *conn, const char *module_name, const char *data, const char *data_path,
        LYD_FORMAT format);

/**
 * @brief Remove an installed module from sysrepo. Deferred until there are no connections!
 *
 * Required WRITE access.
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module to remove.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_remove_module(sr_conn_ctx_t *conn, const char *module_name);

/**
 * @brief Update an installed schema (module) to a new revision. Deferred until there are no connections!
 *
 * Required WRITE access.
 *
 * @param[in] conn Connection to use.
 * @param[in] schema_path Path to the updated schema. Can have either YANG or YIN extension/format.
 * @param[in] search_dirs Optional search directories for import schemas, supports the format `<dir>[:<dir>]*`.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_update_module(sr_conn_ctx_t *conn, const char *schema_path, const char *search_dirs);

/**
 * @brief Cancel scheduled update of a module.
 *
 * Required WRITE access.
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module whose update to cancel.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_cancel_update_module(sr_conn_ctx_t *conn, const char *module_name);

/**
 * @brief Change module replay support.
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module to change. NULL to change all the modules.
 * @param[in] replay_support 0 to disabled, non-zero to enable.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_set_module_replay_support(sr_conn_ctx_t *conn, const char *module_name, int replay_support);

/**
 * @brief Change module filesystem permissions.
 *
 * Required WRITE access.
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module to change.
 * @param[in] owner If set, new owner of the module.
 * @param[in] group If set, new group of the module.
 * @param[in] perm If set, new permissions of the module.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_set_module_access(sr_conn_ctx_t *conn, const char *module_name, const char *owner, const char *group, mode_t perm);

/**
 * @brief Learn about module filesystem permissions.
 *
 * Required READ access.
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module to use.
 * @param[in,out] owner If set, read the owner of the module.
 * @param[in,out] group If set, read the group of the module.
 * @param[in,out] perm If set, read the permissions of the module.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_module_access(sr_conn_ctx_t *conn, const char *module_name, char **owner, char **group, mode_t *perm);

/**
 * @brief Enable a module feature. Deferred until there are no connections!
 *
 * Note that no recursive if-feature checks are performed meaning the feature may
 * still be effectively disabled in case some of its if-features are disabled.
 * This can be checked using `sysrepoctl -l`.
 *
 * Required WRITE access.
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module to change.
 * @param[in] feature_name Name of the feature to enable.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_enable_module_feature(sr_conn_ctx_t *conn, const char *module_name, const char *feature_name);

/**
 * @brief Disable a module feature. Deferred until there are no connections!
 *
 * Note that this may effectively also disable any dependant features.
 * This can be checked using `sysrepoctl -l`.
 *
 * Required WRITE access.
 *
 * @param[in] conn Connection to use.
 * @param[in] module_name Name of the module to change.
 * @param[in] feature_name Name of the feature to disable.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_disable_module_feature(sr_conn_ctx_t *conn, const char *module_name, const char *feature_name);

/**
 * @brief Get internal sysrepo data tree, which holds information about installed modules.
 * These data are from the _sysrepo_ module found in `modules/sysrepo.yang`.
 *
 * @param[in] conn Connection to use.
 * @param[out] sysrepo_data Sysrepo internal data tree.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_module_info(sr_conn_ctx_t *conn, struct lyd_node **sysrepo_data);

/** @} schema */

////////////////////////////////////////////////////////////////////////////////
// Data Retrieval API (get / get-config functionality)
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup get_data_api Getting Data API
 * @{
 */

/**
 * @brief Possible types of a data element stored in the sysrepo datastore.
 */
typedef enum sr_type_e {
    /* special types that does not contain any data */
    SR_UNKNOWN_T,              /**< Element unknown to sysrepo (unsupported element). */

    SR_LIST_T,                 /**< List instance. ([RFC 7950 sec 7.8](http://tools.ietf.org/html/rfc7950#section-7.8)) */
    SR_CONTAINER_T,            /**< Non-presence container. ([RFC 7950 sec 7.5](http://tools.ietf.org/html/rfc7950#section-7.5)) */
    SR_CONTAINER_PRESENCE_T,   /**< Presence container. ([RFC 7950 sec 7.5.1](http://tools.ietf.org/html/rfc7950#section-7.5.1)) */
    SR_LEAF_EMPTY_T,           /**< A leaf that does not hold any value ([RFC 7950 sec 9.11](http://tools.ietf.org/html/rfc7950#section-9.11)) */
    SR_NOTIFICATION_T,         /**< Notification instance ([RFC 7095 sec 7.16](https://tools.ietf.org/html/rfc7950#section-7.16)) */

    /* types containing some data */
    SR_BINARY_T,       /**< Base64-encoded binary data ([RFC 7950 sec 9.8](http://tools.ietf.org/html/rfc7950#section-9.8)) */
    SR_BITS_T,         /**< A set of bits or flags ([RFC 7950 sec 9.7](http://tools.ietf.org/html/rfc7950#section-9.7)) */
    SR_BOOL_T,         /**< A boolean value ([RFC 7950 sec 9.5](http://tools.ietf.org/html/rfc7950#section-9.5)) */
    SR_DECIMAL64_T,    /**< 64-bit signed decimal number ([RFC 7950 sec 9.3](http://tools.ietf.org/html/rfc7950#section-9.3)) */
    SR_ENUM_T,         /**< A string from enumerated strings list ([RFC 7950 sec 9.6](http://tools.ietf.org/html/rfc7950#section-9.6)) */
    SR_IDENTITYREF_T,  /**< A reference to an abstract identity ([RFC 7950 sec 9.10](http://tools.ietf.org/html/rfc7950#section-9.10)) */
    SR_INSTANCEID_T,   /**< References a data tree node ([RFC 7950 sec 9.13](http://tools.ietf.org/html/rfc7950#section-9.13)) */
    SR_INT8_T,         /**< 8-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_INT16_T,        /**< 16-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_INT32_T,        /**< 32-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_INT64_T,        /**< 64-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_STRING_T,       /**< Human-readable string ([RFC 7950 sec 9.4](http://tools.ietf.org/html/rfc7950#section-9.4)) */
    SR_UINT8_T,        /**< 8-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_UINT16_T,       /**< 16-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_UINT32_T,       /**< 32-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_UINT64_T,       /**< 64-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    SR_ANYXML_T,       /**< Unknown chunk of XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
    SR_ANYDATA_T,      /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
} sr_type_t;

/**
 * @brief Data of an element (if applicable), properly set according to the type.
 */
typedef union sr_data_u {
    char *binary_val;       /**< Base64-encoded binary data ([RFC 7950 sec 9.8](http://tools.ietf.org/html/rfc7950#section-9.8)) */
    char *bits_val;         /**< A set of bits or flags ([RFC 7950 sec 9.7](http://tools.ietf.org/html/rfc7950#section-9.7)) */
    bool bool_val;          /**< A boolean value ([RFC 7950 sec 9.5](http://tools.ietf.org/html/rfc7950#section-9.5)) */
    double decimal64_val;   /**< 64-bit signed decimal number ([RFC 7950 sec 9.3](http://tools.ietf.org/html/rfc7950#section-9.3))
                                 __Be careful with this value!__ It is not always possible and the value can change when converting
                                 between a double and YANG decimal64. Because of that you may see some unexpected behavior setting
                                 or reading this value. To avoid these problems, use `*_tree()` API variants instead. */
    char *enum_val;         /**< A string from enumerated strings list ([RFC 7950 sec 9.6](http://tools.ietf.org/html/rfc7950#section-9.6)) */
    char *identityref_val;  /**< A reference to an abstract identity ([RFC 7950 sec 9.10](http://tools.ietf.org/html/rfc7950#section-9.10)) */
    char *instanceid_val;   /**< References a data tree node ([RFC 7950 sec 9.13](http://tools.ietf.org/html/rfc7950#section-9.13)) */
    int8_t int8_val;        /**< 8-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    int16_t int16_val;      /**< 16-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    int32_t int32_val;      /**< 32-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    int64_t int64_val;      /**< 64-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    char *string_val;       /**< Human-readable string ([RFC 7950 sec 9.4](http://tools.ietf.org/html/rfc7950#section-9.4)) */
    uint8_t uint8_val;      /**< 8-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    uint16_t uint16_val;    /**< 16-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    uint32_t uint32_val;    /**< 32-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    uint64_t uint64_val;    /**< 64-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
    char *anyxml_val;       /**< Unknown chunk of XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
    char *anydata_val;      /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
} sr_data_t;

/**
 * @brief Structure that contains value of an data element stored in the sysrepo datastore.
 */
typedef struct sr_val_s {
    /** [XPath](@ref paths) (or rather path) identifier of the data element. */
    char *xpath;

    /** Type of an element. */
    sr_type_t type;

    /**
     * Flag for node with default value (applicable only for leaves).
     * It is set to TRUE only if the value was *implicitly* set by the datastore as per
     * module schema. Explicitly set/modified data element (through the sysrepo API) always
     * has this flag unset regardless of the entered value.
     */
    bool dflt;

    /** [Origin](@ref oper_ds) of the value. */
    char *origin;

    /** Data of an element (if applicable), properly set according to the type. */
    sr_data_t data;

} sr_val_t;

/**
 * @brief Flags used to override default data get behaviour on ::SR_DS_OPERATIONAL by ::sr_get_data call.
 */
typedef enum sr_get_oper_flag_e {
    SR_OPER_DEFAULT = 0,             /**< No special behaviour. */
    SR_OPER_NO_STATE = 1,            /**< Return only configuration data. */
    SR_OPER_NO_CONFIG = 2,           /**< Return only state data. If there are some state subtrees with configuration
                                          parents, these are also returned (with keys if lists). */
    SR_OPER_NO_SUBS = 4,             /**< Return only stored operational data (push), do not call subscriber callbacks (pull). */
    SR_OPER_NO_STORED = 8,           /**< Do not merge with stored operational data (push). */
    SR_OPER_WITH_ORIGIN = 16,        /**< Return data with their [origin attributes](@ref datastores). Nodes without
                                          one inherit the origin from parents. */
} sr_get_oper_flag_t;

/**
 * @brief Options overriding default get handling by ::sr_get_data call,
 * it is supposed to be bitwise OR-ed value of any ::sr_get_oper_flag_t flags.
 */
typedef uint32_t sr_get_oper_options_t;

/**
 * @brief Retrieve a single data element selected by the provided path.
 * Data are represented as ::sr_val_t structures.
 *
 * If the path identifies an empty leaf, a list or a container, the value
 * has no data filled in and its type is set properly
 * (::SR_LEAF_EMPTY_T / ::SR_LIST_T / ::SR_CONTAINER_T / ::SR_CONTAINER_PRESENCE_T).
 *
 * Required READ access, but if the access check fails, the module data are simply ignored without an error.
 *
 * @see Use ::sr_get_items for retrieving larger chunks
 * of data from the datastore. Since it retrieves the data from datastore in
 * larger chunks, it can work much more efficiently than multiple ::sr_get_item calls.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] path [Path](@ref paths) of the data element to be retrieved.
 * @param[in] timeout_ms Operational callback timeout in milliseconds. If 0, default is used.
 * @param[out] value Requested node, allocated dynamically (free using ::sr_free_val).
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_INVAL_ARG if multiple nodes match the path,
 * ::SR_ERR_NOT_FOUND if no nodes match the path).
 */
int sr_get_item(sr_session_ctx_t *session, const char *path, uint32_t timeout_ms, sr_val_t **value);

/**
 * @brief Retrieve an array of data elements selected by the provided XPath.
 * Data are represented as ::sr_val_t structures.
 *
 * All data elements are transferred within one message from the datastore,
 * which is more efficient that calling multiple ::sr_get_item calls.
 *
 * Required READ access, but if the access check fails, the module data are simply ignored without an error.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] xpath [XPath](@ref paths) of the data elements to be retrieved.
 * @param[in] timeout_ms Operational callback timeout in milliseconds. If 0, default is used.
 * @param[in] opts Options overriding default get behaviour.
 * @param[out] values Array of requested nodes, allocated dynamically (free using ::sr_free_values).
 * @param[out] value_cnt Number of returned elements in the values array.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_items(sr_session_ctx_t *session, const char *xpath, uint32_t timeout_ms, const sr_get_oper_options_t opts,
        sr_val_t **values, size_t *value_cnt);

/**
 * @brief Retrieve a single subtree whose root node is selected by the provided path.
 * Data are represented as _libyang_ subtrees.
 *
 * The functions returns values and all associated information stored under the root node and
 * all its descendants. While the same data can be obtained using ::sr_get_items in combination
 * with the expressive power of XPath addressing, the recursive nature of the output data type
 * also preserves the hierarchical relationships between data elements.
 *
 * Required READ access, but if the access check fails, the module data are simply ignored without an error.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] path [Path](@ref paths) selecting the root node of the subtree to be retrieved.
 * @param[in] timeout_ms Operational callback timeout in milliseconds. If 0, default is used.
 * @param[out] subtree Requested subtree, allocated dynamically.
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_INVAL_ARG if multiple nodes match the path).
 */
int sr_get_subtree(sr_session_ctx_t *session, const char *path, uint32_t timeout_ms, struct lyd_node **subtree);

/**
 * @brief Retrieve a tree whose root nodes match the provided XPath.
 * Data are represented as _libyang_ subtrees.
 *
 * Top-level trees are always returned so if an inner node is selected, all of its descendants
 * and its direct parents (lists also with keys) are returned.
 *
 * If the subtree selection process results in too many node overlaps, the cost of the operation
 * may be unnecessarily big. As an example, a common XPath expression `//.` is normally used
 * to select all nodes in a data tree, but for this operation it would result in an excessive duplication
 * of data nodes. Since all the descendants of each matched node are returned implicitly, `//` in the XPath
 * should never be used (i.e. `/\asterisk` is the correct XPath for all the nodes).
 *
 * Required READ access, but if the access check fails, the module data are simply ignored without an error.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] xpath [XPath](@ref paths) selecting root nodes of subtrees to be retrieved.
 * @param[in] max_depth Maximum depth of the selected subtrees. 0 is unlimited, 1 will not return any
 * descendant nodes. If a list should be returned, its keys are always returned as well.
 * @param[in] timeout_ms Operational callback timeout in milliseconds. If 0, default is used.
 * @param[in] opts Options overriding default get behaviour.
 * @param[out] data Connected top-level trees with all the requested data, allocated dynamically.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_data(sr_session_ctx_t *session, const char *xpath, uint32_t max_depth, uint32_t timeout_ms,
        const sr_get_oper_options_t opts, struct lyd_node **data);

/**
 * @brief Free ::sr_val_t structure and all memory allocated within it.
 *
 * @param[in] value Value to be freed.
 */
void sr_free_val(sr_val_t *value);

/**
 * @brief Free array of ::sr_val_t structures (and all memory allocated
 * within of each array element).
 *
 * @param[in] values Array of values to be freed.
 * @param[in] count Number of elements stored in the array.
 */
void sr_free_values(sr_val_t *values, size_t count);

/** @} getdata */

////////////////////////////////////////////////////////////////////////////////
// Data Manipulation API (edit-config functionality)
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup edit_data_api Editing Data API
 * @{
 */

/**
 * @brief Flags used to override default behavior of data manipulation calls.
 */
typedef enum sr_edit_flag_e {
    SR_EDIT_DEFAULT = 0,        /**< Default behavior - non-strict. */
    SR_EDIT_NON_RECURSIVE = 1,  /**< Non-recursive behavior:
                                     by ::sr_set_item, all preceding nodes (parents) of the identified element must exist. */
    SR_EDIT_STRICT = 2,         /**< Strict behavior:
                                     by ::sr_set_item the identified element must not exist (similar to NETCONF create operation),
                                     by ::sr_delete_item the identified element must exist (similar to NETCONF delete operation). */
    SR_EDIT_ISOLATE = 4,        /**< Create new operation separately, independent of all the previous operations. Since all the
                                     operations are concatenated into one edit tree, it may happen that 2 incompatible operations
                                     are set and an error is observed. This flag can in those cases be used. Also, if an error
                                     is returned the previous edit is always left untouched. */
} sr_edit_flag_t;

/**
 * @brief Options overriding default behavior of data manipulation calls,
 * it is supposed to be bitwise OR-ed value of any ::sr_edit_flag_t flags.
 */
typedef uint32_t sr_edit_options_t;

/**
 * @brief Options for specifying move direction of ::sr_move_item call.
 */
typedef enum sr_move_position_e {
    SR_MOVE_BEFORE = 0,    /**< Move the specified item before the selected sibling. */
    SR_MOVE_AFTER = 1,     /**< Move the specified item after the selected. */
    SR_MOVE_FIRST = 2,     /**< Move the specified item to the position of the first child. */
    SR_MOVE_LAST = 3,      /**< Move the specified item to the position of the last child. */
} sr_move_position_t;

/**
 * @brief Prepare to set (create) the value of a leaf, leaf-list, list, or presence container.
 * These changes are applied only after calling ::sr_apply_changes.
 * Data are represented as ::sr_val_t structures.
 *
 * With default options it recursively creates all missing nodes (containers and
 * lists including their key leaves) in the xpath to the specified node (can be
 * turned off with ::SR_EDIT_NON_RECURSIVE option). If ::SR_EDIT_STRICT flag is set,
 * the node must not exist (otherwise an error is returned).
 *
 * To create a list use xpath with key values included and pass NULL as value argument.
 *
 * Setting of a leaf-list value appends the value at the end of the leaf-list.
 * A value of leaf-list can be specified either by predicate in xpath or by value argument.
 * If both are present, value argument is ignored and xpath predicate is used.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] path [Path](@ref paths) identifier of the data element to be set.
 * @param[in] value Value to be set. `xpath` member of the ::sr_val_t structure can be NULL.
 * @param[in] opts Options overriding default behavior of this call.
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_OPERATION_FAILED if the whole edit was discarded).
 */
int sr_set_item(sr_session_ctx_t *session, const char *path, const sr_val_t *value, const sr_edit_options_t opts);

/**
 * @brief Prepare to set (create) the value of a leaf, leaf-list, list, or presence container.
 * These changes are applied only after calling ::sr_apply_changes.
 * Data are represented as pairs of a path and string value.
 *
 * Function provides the same functionality as ::sr_set_item.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] path [Path](@ref paths) identifier of the data element to be set.
 * @param[in] value String representation of the value to be set.
 * @param[in] origin Origin of the value, used only for ::SR_DS_OPERATIONAL edits.
 * @param[in] opts Options overriding default behavior of this call.
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_OPERATION_FAILED if the whole edit was discarded).
 */
int sr_set_item_str(sr_session_ctx_t *session, const char *path, const char *value, const char *origin,
        const sr_edit_options_t opts);

/**
 * @brief Prepare to selete the nodes matching the specified xpath. These changes are applied only
 * after calling ::sr_apply_changes. The accepted values are the same as for ::sr_set_item_str.
 *
 * If ::SR_EDIT_STRICT flag is set the specified node must must exist in the datastore.
 * If the @p path includes the list keys/leaf-list value, the specified instance is deleted.
 * If the @p path of list/leaf-list does not include keys/value, all instances are deleted.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] path [Path](@ref paths) identifier of the data element to be deleted.
 * @param[in] opts Options overriding default behavior of this call.
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_OPERATION_FAILED if the whole edit was discarded).
 */
int sr_delete_item(sr_session_ctx_t *session, const char *path, const sr_edit_options_t opts);

/**
 * @brief Prepare to move/create the instance of an user-ordered list or leaf-list to the specified position.
 * These changes are applied only after calling ::sr_apply_changes.
 *
 * Item can be moved to the first or last position or positioned relatively to its sibling.
 *
 * With default options it recursively creates all missing nodes (containers and
 * lists including their key leaves) in the xpath to the specified node (can be
 * turned off with ::SR_EDIT_NON_RECURSIVE option). If ::SR_EDIT_STRICT flag is set,
 * the node must not exist (otherwise an error is returned).
 *
 * @note To determine current order, you can issue a ::sr_get_items call
 * (without specifying keys of particular list).
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use
 * @param[in] path [Path](@ref paths) identifier of the data element to be moved.
 * @param[in] position Requested move direction.
 * @param[in] list_keys Predicate identifying the relative list instance (example input `[key1="val1"][key2="val2"]...`).
 * @param[in] leaflist_value Value of the relative leaf-list instance (example input `val1`) used
 * to determine relative position, needed only if position argument is ::SR_MOVE_BEFORE or ::SR_MOVE_AFTER.
 * @param[in] origin Origin of the value, used only for ::SR_DS_OPERATIONAL edits.
 * @param[in] opts Options overriding default behavior of this call.
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_OPERATION_FAILED if the whole edit was discarded).
 */
int sr_move_item(sr_session_ctx_t *session, const char *path, const sr_move_position_t position, const char *list_keys,
        const char *leaflist_value, const char *origin, const sr_edit_options_t opts);

/**
 * @brief Provide a prepared edit data tree to be applied.
 * These changes are applied only after calling ::sr_apply_changes.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] edit Edit content, similar semantics to
 * [NETCONF \<edit-config\>](https://tools.ietf.org/html/rfc6241#section-7.2) content.
 * @param[in] default_operation Default operation for nodes without operation on themselves or any parent.
 * Possible values are `merge`, `replace`, or `none` (see [NETCONF RFC](https://tools.ietf.org/html/rfc6241#page-39)).
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_edit_batch(sr_session_ctx_t *session, const struct lyd_node *edit, const char *default_operation);

/**
 * @brief Perform the validation a datastore and any changes made in the current session, but do not
 * apply nor discard them.
 *
 * Provides only YANG validation, apply-changes **subscribers will not be notified** in this case.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] module_name If specified, limits the validate operation only to this module and its dependencies.
 * @param[in] timeout_ms Operational callback timeout in milliseconds. If 0, default is used.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_validate(sr_session_ctx_t *session, const char *module_name, uint32_t timeout_ms);

/**
 * @brief Apply changes made in the current session.
 * In case the changes could not be applied successfully for any reason,
 * they remain intact in the session.
 *
 * @note Note that in case that you are changing the _running_ datastore, you also
 * need to copy the config to _startup_ to make the changes persistent.
 *
 * Required WRITE access.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to apply changes of.
 * @param[in] timeout_ms Configuration callback timeout in milliseconds. If 0, default is used. Note that this timeout
 * is measured separately for each callback meaning this whole function call can easily __take more time__ than this
 * timeout if there are changes applied for several subscribers.
 * @param[in] wait Whether to wait until all callbacks on all events are finished (even ::SR_EV_DONE or ::SR_EV_ABORT).
 * If not set, these events may not yet be processed after the function returns. Note that all ::SR_EV_CHANGE events
 * are always waited for.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_apply_changes(sr_session_ctx_t *session, uint32_t timeout_ms, int wait);

/**
 * @brief Learn whether there are any prepared non-applied changes in the session.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to check changes in.
 * @return non-zero if there are some changes, 0 if there are none.
 */
int sr_has_changes(sr_session_ctx_t *session);

/**
 * @brief Discard prepared changes made in the current session.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to discard changes from.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_discard_changes(sr_session_ctx_t *session);

/**
 * @brief Replace a datastore with the contents of a data tree. If the module is specified, limit
 * the operation only to the specified module. If it is not specified, the operation is performed on all modules.
 *
 * Required WRITE access.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific - target datastore) to use.
 * @param[in] module_name If specified, limits the replace operation only to this module.
 * @param[in] src_config Source data to replace the datastore. Is ALWAYS spent and cannot be further used by the application!
 * @param[in] timeout_ms Configuration callback timeout in milliseconds. If 0, default is used.
 * @param[in] wait Whether to wait until all callbacks on all events are finished (even ::SR_EV_DONE or ::SR_EV_ABORT).
 * If not set, these events may not yet be processed after the function returns. Note that all ::SR_EV_CHANGE events
 * are always waited for.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_replace_config(sr_session_ctx_t *session, const char *module_name, struct lyd_node *src_config,
        uint32_t timeout_ms, int wait);

/**
 * @brief Replaces a conventional datastore with the contents of
 * another conventional datastore. If the module is specified, limits
 * the operation only to the specified module. If it is not specified,
 * the operation is performed on all modules.
 *
 * @note Note that copying from _candidate_ to _running_ or vice versa causes
 * the _candidate_ datastore to revert to original behavior of mirroring _running_ datastore (@ref datastores).
 *
 * Required WRITE access.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific - target datastore) to use.
 * @param[in] module_name Optional module name that limits the copy operation only to this module.
 * @param[in] src_datastore Source datastore.
 * @param[in] timeout_ms Configuration callback timeout in milliseconds. If 0, default is used.
 * @param[in] wait Whether to wait until all callbacks on all events are finished (even ::SR_EV_DONE or ::SR_EV_ABORT).
 * If not set, these events may not yet be processed after the function returns. Note that all ::SR_EV_CHANGE events
 * are always waited for.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_copy_config(sr_session_ctx_t *session, const char *module_name, sr_datastore_t src_datastore, uint32_t timeout_ms,
        int wait);

/** @} editdata */

////////////////////////////////////////////////////////////////////////////////
// Locking API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup lock_api Locking API
 * @{
 */

/**
 * @brief Locks the data of the specified module or the whole datastore.
 *
 * @note Note that locking _candidate_ datastore after it has already
 * been modified is not allowed. Session needs to acquire this lock
 * before it or any other session performs any changes.
 *
 * @note This lock will be automatically released when the session is stopped.
 *
 * Required READ access.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] module_name Optional name of the module to be locked.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_lock(sr_session_ctx_t *session, const char *module_name);

/**
 * @brief Unlocks the data of the specified module or the whole datastore.
 *
 * Required READ access.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] module_name Optional name of the module to be unlocked.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_unlock(sr_session_ctx_t *session, const char *module_name);

/**
 * @brief Check whether the data of the specified module or the whole datastore are locked.
 *
 * Note that if whole datastore is checked, \p is_locked will be set only if all
 * the modules are locked by the same Sysrepo session. If a module is not locked
 * or locked by another Sysrepo session, \p is_locked will be false.
 *
 * @param[in] conn Connection to use.
 * @param[in] datastore Datastore of the lock.
 * @param[in] module_name Optional name of the module to check.
 * @param[out] is_locked True is the module or whole datastore is locked.
 * @param[out] id Optional Sysrepo SID of the session if the module/datastore is locked.
 * @param[out] nc_id Optional NETCONF SID of the session if the module/datatsore is locked.
 * @param[out] timestamp Optional timestamp of the lock.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_lock(sr_conn_ctx_t *conn, sr_datastore_t datastore, const char *module_name, int *is_locked, uint32_t *id,
        uint32_t *nc_id, time_t *timestamp);

/** @} lock */

////////////////////////////////////////////////////////////////////////////////
// Subscription API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup subs_api Subscription API
 * @{
 */

/**
 * @brief Flags used to override default handling of subscriptions.
 */
typedef enum sr_subscr_flag_e {
    /**
     * @brief Default behavior of the subscription. In case of ::sr_module_change_subscribe call it means that:
     *
     * - for every new subscription (flag ::SR_SUBSCR_CTX_REUSE not used) a thread is created that listens for
     *   new events (can be changed with ::SR_SUBSCR_NO_THREAD flag),
     * - the subscriber is the "owner" of the subscribed data tree and it will appear in the operational
     *   datastore while this subscription is alive (if not already, can be changed using ::SR_SUBSCR_PASSIVE flag),
     * - the callback will be called twice, once with ::SR_EV_CHANGE event and once with ::SR_EV_DONE / ::SR_EV_ABORT
     *   event passed in (can be changed with ::SR_SUBSCR_DONE_ONLY flag).
     */
    SR_SUBSCR_DEFAULT = 0,

    /**
     * @brief This option enables the application to re-use an already existing subscription context previously returned
     * from any sr_*_subscribe call instead of requesting the creation of a new one. In that case a single
     * ::sr_unsubscribe call unsubscribes from all subscriptions filed within the context.
     */
    SR_SUBSCR_CTX_REUSE = 1,

    /**
     * @brief There will be no thread created for handling this subscription meaning no event will be processed!
     * Use this flag when the application has its own event loop and it will listen for and process events manually
     * (see ::sr_get_event_pipe and ::sr_process_events).
     */
    SR_SUBSCR_NO_THREAD = 2,

    /**
     * @brief The subscriber is not the "owner" of the subscribed data tree, just a passive watcher for changes.
     * When this option is passed in to ::sr_module_change_subscribe, the subscription will have no effect on
     * the presence of the subtree in the operational datastore.
     */
    SR_SUBSCR_PASSIVE = 4,

    /**
     * @brief The subscriber does not support verification of the changes and wants to be notified only after
     * the changes has been applied in the datastore, without the possibility to deny them
     * (it will not receive ::SR_EV_CHANGE nor ::SR_EV_ABORT but only ::SR_EV_DONE events).
     */
    SR_SUBSCR_DONE_ONLY = 8,

    /**
     * @brief The subscriber wants to be notified about the current configuration at the moment of subscribing.
     * It will receive ::SR_EV_ENABLED event, whose applying can fail causing the whole subscription to fail.
     * On success this event will be followed by ::SR_EV_DONE. Be careful, ::SR_EV_ENABLED will be triggered
     * even if there are no data so there will not be any changes!
     */
    SR_SUBSCR_ENABLED = 16,

    /**
     * @brief The subscriber will be called before any other subscribers for the particular module
     * with an additional ::SR_EV_UPDATE event and is then allowed to modify the new module data. It can add new changes
     * by calling standard set functions (such as ::sr_set_item_str) on the implicit callback session and returning.
     * Note that you cannot subscribe more callbacks with this flags on one module with the same priority.
     */
    SR_SUBSCR_UPDATE = 32,

    /**
     * @brief The subscriber wants to modify other subscriptions in its callback. Normally, this would
     * cause deadlock but with this flag it is possible. But, there are some **limitations**. The callback
     * MUST not subscribe to the same RPC/module DS changes it is processing (would change subscription count
     * and cause invalid memory access) and MUST not subscribe on the same ::sr_subscription_ctx_t
     * `subscription` (would cause a deadlock). Accepted **only** for RPC/action and change subscriptions,
     * it makes no sense for others.
     */
    SR_SUBSCR_UNLOCKED = 64,

    /**
     * @brief Instead of removing any previous existing matching data before getting them from an operational
     * subscription callback, keep them. Then the returned data are merged into the existing data. Accepted
     * only for operational subscriptions.
     */
    SR_SUBSCR_OPER_MERGE = 128,

} sr_subscr_flag_t;

/**
 * @brief Sysrepo subscription context returned from sr_*_subscribe calls,
 * it is supposed to be released by the caller using ::sr_unsubscribe call.
 */
typedef struct sr_subscription_ctx_s sr_subscription_ctx_t;

/**
 * @brief Options overriding default behavior of subscriptions,
 * it is supposed to be a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
 */
typedef uint32_t sr_subscr_options_t;

/**
 * @brief Get the event pipe of a subscription. Do not call unless ::SR_SUBSCR_NO_THREAD flag was used
 * when subscribing! Event pipe can be used in `select()`, `poll()`, or similar functions to listen for new events.
 * It will then be ready for reading.
 *
 * @param[in] subscription Subscription without a listening thread.
 * @param[out] event_pipe Event pipe of the subscription, do not close! It will be closed
 * when the subscription is unsubscribed.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_event_pipe(sr_subscription_ctx_t *subscription, int *event_pipe);

/**
 * @brief Process any pending new events on a subscription. Should not be called unless ::SR_SUBSCR_NO_THREAD flag
 * was used when subscribing! Usually called after this subscription's event pipe is ready for reading but can
 * also be called periodically.
 *
 * @param[in] subscription Subscription without a listening thread with some new events.
 * @param[in] session Optional session for storing errors.
 * @param[out] stop_time_in Optional seconds until the nearest notification subscription stop time is elapsed
 * and this function should be called. If there are no subscriptions with stop time in future, it is set to 0.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_process_events(sr_subscription_ctx_t *subscription, sr_session_ctx_t *session, time_t *stop_time_in);

/**
 * @brief Unsubscribes from a subscription acquired by any of sr_*_subscribe
 * calls and releases all subscription-related data.
 *
 * In case that the same subscription context was used to subscribe for
 * multiple subscriptions, unsubscribes from all of them.
 *
 * @note Subscription will no longer work even on error.
 *
 * @param[in] subscription Subscription context acquired by any of sr_*_subscribe calls.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_unsubscribe(sr_subscription_ctx_t *subscription);

/** @} subs */

////////////////////////////////////////////////////////////////////////////////
// Change Subscriptions API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup change_subs_api Change Data Subscription API
 * @{
 */

/**
 * @brief Type of the event that has occurred (passed to application callbacks).
 *
 * @note Each change is normally announced twice: first as ::SR_EV_CHANGE event and then as ::SR_EV_DONE or ::SR_EV_ABORT
 * event. If the subscriber does not support verification, it can subscribe only to ::SR_EV_DONE event by providing
 * ::SR_SUBSCR_DONE_ONLY subscription flag. The general rule is that in case the operation fails, only if the subscriber
 * has __successfully__ processed the first event (::SR_EV_CHANGE/::SR_EV_RPC), it will get the second ::SR_EV_ABORT event.
 */
typedef enum sr_event_e {
    SR_EV_UPDATE,  /**< Occurs before any other events and the subscriber can update the apply-changes diff.
                        It is performed by calling `sr_*_item()` or ::sr_edit_batch() on the callback session
                        __without__ calling ::sr_apply_changes() or any other function. */
    SR_EV_CHANGE,  /**< Occurs just before the changes are committed to the datastore,
                        the subscriber is supposed to verify that the changes are valid and can be applied
                        and prepare all resources required for the changes. The subscriber can still deny the changes
                        in this phase by returning an error from the callback. */
    SR_EV_DONE,    /**< Occurs just after the changes have been successfully committed to the datastore,
                        the subscriber can apply the changes now, but it cannot deny the changes in this
                        phase anymore (any returned errors are just logged and ignored). */
    SR_EV_ABORT,   /**< Occurs in case that the commit transaction has failed because one of the verifiers
                        has denied the change (returned an error). The subscriber is supposed to return the managed
                        application to the state before the commit. Any returned errors are just logged and ignored.
                        This event is also generated for RPC subscriptions when a later callback has failed and
                        this one has already successfully processed ::SR_EV_RPC. The callback that failed will __never__
                        get this event! */
    SR_EV_ENABLED, /**< Occurs for subscriptions with the flag ::SR_SUBSCR_ENABLED and is normally followed by
                        ::SR_EV_DONE. It can fail and will also be triggered even when there is no startup configuration
                        (which is different from the ::SR_EV_CHANGE event). Also note that the callback on this event
                        __cannot__ return ::SR_ERR_CALLBACK_SHELVE. */
    SR_EV_RPC,     /**< Occurs for a standard RPC execution. If a later callback fails, ::SR_EV_ABORT is generated. */
} sr_event_t;

/**
 * @brief Type of the operation made on an item, used by changeset retrieval in ::sr_get_change_next.
 */
typedef enum sr_change_oper_e {
    SR_OP_CREATED,   /**< The item has been created by the change. */
    SR_OP_MODIFIED,  /**< The value of the item has been modified by the change. */
    SR_OP_DELETED,   /**< The item has been deleted by the change. */
    SR_OP_MOVED,     /**< The item has been moved in the subtree by the change (applicable for leaf-lists and user-ordered lists). */
} sr_change_oper_t;

/**
 * @brief Iterator used for retrieval of a changeset using ::sr_get_changes_iter call.
 */
typedef struct sr_change_iter_s sr_change_iter_t;

/**
 * @brief Callback to be called on the event of changing datastore content of the specified module.
 *
 * @note Callback is allowed to modify installed YANG modules but MUST not modify subscriptions on ::SR_EV_CHANGE event.
 * It would result in a deadlock and this callback timeout (unless ::SR_SUBSCR_UNLOCKED is used when subscribing).
 *
 * @param[in] session Implicit session (do not stop) with information about the changed data (retrieved by
 * ::sr_get_changes_iter) the event originator session IDs.
 * @param[in] module_name Name of the module where the change has occurred.
 * @param[in] xpath [XPath](@ref paths) used when subscribing, NULL if the whole module was subscribed to.
 * @param[in] event Type of the callback event that has occurred.
 * @param[in] request_id Request ID unique for the specific \p module_name. Connected events
 * for one request (::SR_EV_CHANGE and ::SR_EV_DONE, for example) have the same request ID.
 * @param[in] private_data Private context opaque to sysrepo, as passed to ::sr_module_change_subscribe call.
 * @return User error code (::SR_ERR_OK on success).
 */
typedef int (*sr_module_change_cb)(sr_session_ctx_t *session, const char *module_name, const char *xpath,
        sr_event_t event, uint32_t request_id, void *private_data);

/**
 * @brief Subscribe for changes made in the specified module. If there are changes made in several
 * modules, the module order is determined by the **order in the changes** (it is kept).
 *
 * Required WRITE access. If ::SR_SUBSCR_PASSIVE is set, required READ access.
 *
 * @param[in] session Session ([DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] module_name Name of the module of interest for change notifications.
 * @param[in] xpath Optional [XPath](@ref paths) further filtering the changes that will be handled by this subscription.
 * @param[in] callback Callback to be called when the change in the datastore occurs.
 * @param[in] private_data Private context passed to the callback function, opaque to sysrepo.
 * @param[in] priority Specifies the order in which the callbacks (**within module**) will be called.
 * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
 * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
 * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
 * @note An existing context may be passed in case that ::SR_SUBSCR_CTX_REUSE option is specified.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_module_change_subscribe(sr_session_ctx_t *session, const char *module_name, const char *xpath,
        sr_module_change_cb callback, void *private_data, uint32_t priority, sr_subscr_options_t opts,
        sr_subscription_ctx_t **subscription);

/**
 * @brief Create an iterator for retrieving the changes (list of newly added / removed / modified nodes)
 * in module-change callbacks. It __cannot__ be used outside the callback.
 *
 * @see ::sr_get_change_next for iterating over the changeset using this iterator.
 *
 * @param[in] session Implicit session provided in the callbacks (::sr_module_change_cb). Will not work with other sessions.
 * @param[in] xpath [XPath](@ref paths) selecting the changes. Note that you must select all the changes specifically,
 * not just subtrees (to get a full change subtree `//.` can be appended to the XPath)! Also note that if you use
 * an XPath that selects more changes than subscribed to, you may actually get them because all the changes of a module
 * are available in every callback!
 * @param[out] iter Iterator context that can be used to retrieve individual changes using
 * ::sr_get_change_next calls. Allocated by the function, should be freed with ::sr_free_change_iter.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_get_changes_iter(sr_session_ctx_t *session, const char *xpath, sr_change_iter_t **iter);

/**
 * @brief Create an iterator for retrieving the changes (list of newly added / removed / modified nodes)
 * in module-change callbacks. It __can__ be used even outside the callback.
 *
 * @see ::sr_get_change_next for iterating over the changeset using this iterator.
 *
 * @param[in] session Implicit session provided in the callbacks (::sr_module_change_cb). Will not work with other sessions.
 * @param[in] xpath [XPath](@ref paths) selecting the changes. Note that you must select all the changes specifically,
 * not just subtrees (to get a full change subtree `//.` can be appended to the XPath)! Also note that if you use
 * an XPath that selects more changes than subscribed to, you may actually get them because all the changes of a module
 * are available in every callback!
 * @param[out] iter Iterator context that can be used to retrieve individual changes using
 * ::sr_get_change_next calls. Allocated by the function, should be freed with ::sr_free_change_iter.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_dup_changes_iter(sr_session_ctx_t *session, const char *xpath, sr_change_iter_t **iter);

/**
 * @brief Return the next change from the provided iterator created
 * by ::sr_get_changes_iter call. Data are represented as ::sr_val_t structures.
 *
 * @note If the operation is ::SR_OP_MOVED the meaning of new_value and old value argument is
 * as follows - the value pointed by new_value was moved after the old_value. If the
 * old value is NULL it was moved to the first position. The same applies for operation ::SR_OP_CREATED
 * if the created instance was a user-ordered (leaf-)list.
 *
 * @param[in] session Implicit session provided in the callbacks (::sr_module_change_cb). Will not work with other sessions.
 * @param[in,out] iter Iterator acquired with ::sr_get_changes_iter call.
 * @param[out] operation Type of the operation made on the returned item.
 * @param[out] old_value Old value of the item (the value before the change).
 * NULL in case that the item has been just created (operation ::SR_OP_CREATED).
 * @param[out] new_value New (modified) value of the the item. NULL in case that
 * the item has been just deleted (operation ::SR_OP_DELETED).
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_NOT_FOUND on no more changes).
 */
int sr_get_change_next(sr_session_ctx_t *session, sr_change_iter_t *iter, sr_change_oper_t *operation,
        sr_val_t **old_value, sr_val_t **new_value);

/**
 * @brief Returns the next change from the provided iterator created
 * by ::sr_get_changes_iter call. Data are represented as _libyang_ subtrees.
 *
 * @note Meaning of output parameters varies based on the operation:
 * ::SR_OP_CREATED - \p node is the created node, for user-ordered lists either \p prev_value or \p prev_list is
 * always set with meaning similar to ::SR_OP_MOVED.
 * ::SR_OP_MODIFIED - \p node is the modified node, \p prev_value is set to the previous value of the leaf,
 * \p prev_dflt is set if the previous leaf value was the default.
 * ::SR_OP_DELETED - \p node is the deleted node.
 * ::SR_OP_MOVED - \p node is the moved (leaf-)list instance, for user-ordered lists either \p prev_value (leaf-list) or
 * \p prev_list (list) is set to the preceding instance unless the node is the first, when they are set to "" (empty string).
 *
 * @param[in] session Implicit session provided in the callbacks (::sr_module_change_cb). Will not work with other sessions.
 * @param[in,out] iter Iterator acquired with ::sr_get_changes_iter call.
 * @param[out] operation Type of the operation made on the returned item.
 * @param[out] node Affected data node always with all parents, depends on the operation.
 * @param[out] prev_value Previous value, depends on the operation.
 * @param[out] prev_list Previous list keys predicate (`[key1="val1"][key2="val2"]...`), depends on the operation.
 * @param[out] prev_dflt Previous value default flag, depends on the operation.
 * @return Error code (::SR_ERR_OK on success, ::SR_ERR_NOT_FOUND on no more changes).
 */
int sr_get_change_tree_next(sr_session_ctx_t *session, sr_change_iter_t *iter, sr_change_oper_t *operation,
        const struct lyd_node **node, const char **prev_value, const char **prev_list, bool *prev_dflt);

/**
 * @brief Frees ::sr_change_iter_t iterator and all memory allocated within it.
 *
 * @param[in] iter Iterator to be freed.
 */
void sr_free_change_iter(sr_change_iter_t *iter);

/** @} datasubs */

////////////////////////////////////////////////////////////////////////////////
// RPC (Remote Procedure Calls) and Action API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup rpc_subs_api RPC/Action Subscription API
 * @{
 */

/**
 * @brief Callback to be called for the delivery of an RPC/action. Data are represented as ::sr_val_t structures.
 *
 * @note Callback is allowed to modify installed YANG modules but MUST not modify subscriptions. It would result in
 * a deadlock and this callback timeout (unless ::SR_SUBSCR_UNLOCKED is used when subscribing).
 *
 * @param[in] session Implicit session (do not stop) with information about event originator session IDs.
 * @param[in] xpath Full operation [xpath](@ref paths) identifying the exact RPC/action executed.
 * @param[in] input Array of input parameters.
 * @param[in] input_cnt Number of input parameters.
 * @param[in] event Type of the callback event that has occurred.
 * @param[in] request_id Request ID unique for the specific \p op_path.
 * @param[out] output Array of output parameters. Should be allocated on heap,
 * will be freed by sysrepo after sending of the RPC response.
 * @param[out] output_cnt Number of output parameters.
 * @param[in] private_data Private context opaque to sysrepo, as passed to ::sr_rpc_subscribe call.
 * @return User error code (::SR_ERR_OK on success).
 */
typedef int (*sr_rpc_cb)(sr_session_ctx_t *session, const char *xpath, const sr_val_t *input, const size_t input_cnt,
        sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data);

/**
 * @brief Callback to be called for the delivery of an RPC/action. Data are represented as _libyang_ subtrees.
 *
 * @note Callback is allowed to modify installed YANG modules but MUST not modify subscriptions. It would result in
 * a deadlock and this callback timeout (unless ::SR_SUBSCR_UNLOCKED is used when subscribing).
 *
 * @param[in] session Implicit session (do not stop) with information about the event originator session IDs.
 * @param[in] op_path Simple operation [path](@ref paths) identifying the RPC/action.
 * @param[in] input Data tree of input parameters. Always points to the __RPC/action__ itself, even for nested operations.
 * @param[in] event Type of the callback event that has occurred.
 * @param[in] request_id Request ID unique for the specific \p op_path.
 * @param[out] output Data tree of output parameters. Should be allocated on heap,
 * will be freed by sysrepo after sending of the RPC response.
 * @param[in] private_data Private context opaque to sysrepo, as passed to ::sr_rpc_subscribe_tree call.
 * @return User error code (::SR_ERR_OK on success).
 */
typedef int (*sr_rpc_tree_cb)(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input,
        sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data);

/**
 * @brief Subscribe for the delivery of an RPC/action. Data are represented as ::sr_val_t structures.
 *
 * Required WRITE access.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] xpath [XPath](@ref paths) identifying the RPC/action. Any predicates are allowed.
 * @param[in] callback Callback to be called.
 * @param[in] private_data Private context passed to the callback function, opaque to sysrepo.
 * @param[in] priority Specifies the order in which the callbacks (**within RPC/action**) will be called.
 * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
 * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
 * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
 * @note An existing context may be passed in case that ::SR_SUBSCR_CTX_REUSE option is specified.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_rpc_subscribe(sr_session_ctx_t *session, const char *xpath, sr_rpc_cb callback, void *private_data,
        uint32_t priority, sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);

/**
 * @brief Subscribe for the delivery of an RPC/action. Data are represented as _libyang_ subtrees.
 *
 * Required WRITE access.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] xpath [XPath](@ref paths) identifying the RPC/action. Any predicates are allowed.
 * @param[in] callback Callback to be called.
 * @param[in] private_data Private context passed to the callback function, opaque to sysrepo.
 * @param[in] priority Specifies the order in which the callbacks (**within RPC/action**) will be called.
 * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
 * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
 * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
 * @note An existing context may be passed in case that ::SR_SUBSCR_CTX_REUSE option is specified.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_rpc_subscribe_tree(sr_session_ctx_t *session, const char *xpath, sr_rpc_tree_cb callback,
        void *private_data, uint32_t priority, sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);

/**
 * @brief Send an RPC/action and wait for the result. Data are represented as ::sr_val_t structures.
 *
 * Required READ access.
 *
 * @note RPC/action must be valid in (is validated against) the [operational datastore](@ref oper_ds) context.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] path [Path](@ref paths) identifying the RPC/action.
 * @param[in] input Array of input parameters (array of all nodes that hold some
 * data in RPC/action input subtree - same as ::sr_get_items would return).
 * @param[in] input_cnt Number of input parameters.
 * @param[in] timeout_ms RPC/action callback timeout in milliseconds. If 0, default is used.
 * @param[out] output Array of output parameters (all nodes that hold some data
 * in RPC/action output subtree). Will be allocated by sysrepo and should be freed by
 * caller using ::sr_free_values.
 * @param[out] output_cnt Number of output parameters.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_rpc_send(sr_session_ctx_t *session, const char *path, const sr_val_t *input, const size_t input_cnt,
        uint32_t timeout_ms, sr_val_t **output, size_t *output_cnt);

/**
 * @brief Send an RPC/action and wait for the result. Data are represented as _libyang_ subtrees.
 *
 * Required READ access.
 *
 * @note RPC/action must be valid in (is validated against) the [operational datastore](@ref oper_ds) context.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] input Input data tree.
 * @param[in] timeout_ms RPC/action callback timeout in milliseconds. If 0, default is used.
 * @param[out] output Output data tree. Will be allocated by sysrepo and should be freed by the caller.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_rpc_send_tree(sr_session_ctx_t *session, struct lyd_node *input, uint32_t timeout_ms, struct lyd_node **output);

/** @} rpcsubs */

////////////////////////////////////////////////////////////////////////////////
// Notifications API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup notif_subs_api Notification Subscription API
 * @{
 */

/**
 * @brief Type of the notification passed to the ::sr_event_notif_cb and ::sr_event_notif_tree_cb callbacks.
 */
typedef enum sr_ev_notif_type_e {
    SR_EV_NOTIF_REALTIME,         /**< Real-time notification. */
    SR_EV_NOTIF_REPLAY,           /**< Replayed notification. */
    SR_EV_NOTIF_REPLAY_COMPLETE,  /**< Not a real notification, just a signal that the notification replay has completed
                                       (all the stored notifications from the given time interval have been delivered). */
    SR_EV_NOTIF_STOP,             /**< Not a real notification, just a signal that replay stop time has been reached
                                       (delivered only if stop_time was specified when subscribing). */
} sr_ev_notif_type_t;

/**
 * @brief Callback to be called for the delivery of a notification. Data are represented as ::sr_val_t structures.
 *
 * @note Callback is allowed to modify installed YANG modules and subscriptions.
 *
 * @param[in] session Implicit session (do not stop) with information about the event originator session IDs.
 * @param[in] notif_type Type of the notification.
 * @param[in] xpath Full operation [xpath](@ref paths) identifying the exact notification executed.
 * @param[in] values Array of all nodes that hold some data in event notification subtree.
 * @param[in] values_cnt Number of items inside the values array.
 * @param[in] timestamp Time when the notification was generated
 * @param[in] private_data Private context opaque to sysrepo,
 * as passed to ::sr_event_notif_subscribe call.
 */
typedef void (*sr_event_notif_cb)(sr_session_ctx_t *session, const sr_ev_notif_type_t notif_type, const char *xpath,
        const sr_val_t *values, const size_t values_cnt, time_t timestamp, void *private_data);

/**
 * @brief Callback to be called for the delivery of a notification. Data are represented as _libyang_ subtrees.
 *
 * @note Callback is allowed to modify installed YANG modules and subscriptions.
 *
 * @param[in] session Implicit session (do not stop) with information about the event originator session IDs.
 * @param[in] notif_type Type of the notification.
 * @param[in] notif Notification data tree. Always points to the __notification__ itself, even for nested ones.
 * @param[in] timestamp Time when the notification was generated
 * @param[in] private_data Private context opaque to sysrepo, as passed to ::sr_event_notif_subscribe_tree call.
 */
typedef void (*sr_event_notif_tree_cb)(sr_session_ctx_t *session, const sr_ev_notif_type_t notif_type,
        const struct lyd_node *notif, time_t timestamp, void *private_data);

/**
 * @brief Subscribe for the delivery of a notification(s). Data are represented as ::sr_val_t structures.
 *
 * Required WRITE access.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] module_name Name of the module whose notifications to subscribe to.
 * @param[in] xpath Optional [XPath](@ref paths) further filtering received notifications.
 * @param[in] start_time Optional start time of the subscription. Used for replaying stored notifications.
 * @param[in] stop_time Optional stop time ending the notification subscription.
 * @param[in] callback Callback to be called when the event notification is delivered.
 * @param[in] private_data Private context passed to the callback function, opaque to sysrepo.
 * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
 * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
 * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
 * @note An existing context may be passed in case that ::SR_SUBSCR_CTX_REUSE option is specified.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_event_notif_subscribe(sr_session_ctx_t *session, const char *module_name, const char *xpath, time_t start_time,
        time_t stop_time, sr_event_notif_cb callback, void *private_data, sr_subscr_options_t opts,
        sr_subscription_ctx_t **subscription);

/**
 * @brief Subscribes for the delivery of a notification(s). Data are represented as _libyang_ subtrees.
 *
 * Required WRITE access.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] module_name Name of the module whose notifications to subscribe to.
 * @param[in] xpath Optional [XPath](@ref paths) further filtering received notifications.
 * @param[in] start_time Optional start time of the subscription. Used for replaying stored notifications.
 * @param[in] stop_time Optional stop time ending the notification subscription.
 * @param[in] callback Callback to be called when the event notification is delivered.
 * @param[in] private_data Private context passed to the callback function, opaque to sysrepo.
 * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
 * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
 * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
 * @note An existing context may be passed in case that ::SR_SUBSCR_CTX_REUSE option is specified.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_event_notif_subscribe_tree(sr_session_ctx_t *session, const char *module_name, const char *xpath,
        time_t start_time, time_t stop_time, sr_event_notif_tree_cb callback, void *private_data,
        sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);

/**
 * @brief Send a notification. Data are represented as ::sr_val_t structures. In case there are
 * particularly many notifications send on a session (100 notif/s or more) and all of them
 * are stored for replay, consider using ::sr_session_notif_buffer().
 *
 * Required WRITE access. If the module does not support replay, required READ access.
 *
 * @note Notification must be valid in (is validated against) the [operational datastore](@ref oper_ds) context.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] path [Path](@ref paths) identifying the notification.
 * @param[in] values Array of all nodes that hold some data in event notification subtree
 * (same as ::sr_get_items would return).
 * @param[in] values_cnt Number of items inside the values array.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_event_notif_send(sr_session_ctx_t *session, const char *path, const sr_val_t *values, const size_t values_cnt);

/**
 * @brief Send a notification. Data are represented as _libyang_ subtrees. In case there are
 * particularly many notifications send on a session (100 notif/s or more) and all of them
 * are stored for replay, consider using ::sr_session_notif_buffer().
 *
 * Required WRITE access. If the module does not support replay, required READ access.
 *
 * @note Notification must be valid in (is validated against) the [operational datastore](@ref oper_ds) context.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] notif Notification data tree to send.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_event_notif_send_tree(sr_session_ctx_t *session, struct lyd_node *notif);

/** @} notifsubs */

////////////////////////////////////////////////////////////////////////////////
// Operational Data API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup oper_subs_api Operational Data Subscription API
 * @{
 */

/**
 * @brief Callback to be called when operational data at the selected xpath are requested.
 * Data are represented as _libyang_ subtrees.
 *
 * When the callback is called, the data parent is provided. Any parent children (selected by @p path)
 * are removed and should be provided by the callback instead, if they exist. Callback handler can
 * provide any data matching the @p path but in case there are other nested subscriptions,
 * they will be called after this one (and when they are called, their parent children will again be removed
 * which can result in nodes provided by the original callback being lost).
 *
 * @note Callback is allowed to modify installed YANG modules but MUST not modify subscriptions. It would result in
 * a deadlock and this callback timeout.
 *
 * @param[in] session Implicit session (do not stop) with information about the event originator session IDs.
 * @param[in] module_name Name of the affected module.
 * @param[in] path [Path](@ref paths) identifying the subtree that is supposed to be provided, same as the one used
 * for the subscription.
 * @param[in] request_xpath [XPath](@ref paths) as requested by a client. Can be NULL.
 * @param[in] request_id Request ID unique for the specific \p module_name.
 * @param[in,out] parent Pointer to an existing parent of the requested nodes. Is NULL for top-level nodes.
 * Caller is supposed to append the requested nodes to this data subtree and return either the original parent
 * or a top-level node.
 * @param[in] private_data Private context opaque to sysrepo, as passed to ::sr_oper_get_items_subscribe call.
 * @return User error code (::SR_ERR_OK on success).
 */
typedef int (*sr_oper_get_items_cb)(sr_session_ctx_t *session, const char *module_name, const char *path,
        const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data);

/**
 * @brief Register for providing operational data at the given xpath.
 *
 * Required WRITE access.
 *
 * @param[in] session Session (not [DS](@ref sr_datastore_t)-specific) to use.
 * @param[in] module_name Name of the affected module.
 * @param[in] path [Path](@ref paths) identifying the subtree which the provider is able to provide. Predicates can be
 * used to provide only specific instances of nodes. Before calling this callback, any existing data matching this
 * path __are deleted__.
 * @param[in] callback Callback to be called when the operational data for the given xpath are requested.
 * @param[in] private_data Private context passed to the callback function, opaque to sysrepo.
 * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
 * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
 * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
 * @return Error code (::SR_ERR_OK on success).
 */
int sr_oper_get_items_subscribe(sr_session_ctx_t *session, const char *module_name, const char *path,
        sr_oper_get_items_cb callback, void *private_data, sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);

/** @} oper_subs */

////////////////////////////////////////////////////////////////////////////////
// Plugin API
////////////////////////////////////////////////////////////////////////////////

/**
 * @defgroup plugin_api Plugin API
 * @{
 */

/**
 * @brief Sysrepo plugin initialization callback name that must exist in every plugin.
 */
#define SRP_INIT_CB     "sr_plugin_init_cb"

/**
 * @brief Sysrepo plugin cleanup callback name that must exist in every plugin.
 */
#define SRP_CLEANUP_CB  "sr_plugin_cleanup_cb"

/**
 * @brief Sysrepo plugin initialization callback.
 *
 * @param[in] session Sysrepo session that can be used for any API calls needed
 * for plugin initialization (mainly for reading of startup configuration
 * and subscribing for notifications).
 * @param[out] private_data Private context (opaque to sysrepo) that will be
 * passed to ::srp_cleanup_cb_t when plugin cleanup is requested.
 *
 * @return Error code (::SR_ERR_OK on success).
 */
typedef int (*srp_init_cb_t)(sr_session_ctx_t *session, void **private_data);

/**
 * @brief Sysrepo plugin cleanup callback.
 *
 * @param[in] session Sysrepo session that can be used for any API calls
 * needed for plugin cleanup (mainly for unsubscribing of subscriptions
 * initialized in ::srp_init_cb_t).
 * @param[in] private_data Private context as passed in ::srp_init_cb_t.
 */
typedef void (*srp_cleanup_cb_t)(sr_session_ctx_t *session, void *private_data);

/**
 * @brief Log a plugin error message with format arguments.
 *
 * @param[in] format Message format.
 * @param[in] ... Format arguments.
 */
#define SRP_LOG_ERR(format, ...) srp_log(SR_LL_ERR, format, __VA_ARGS__)

/**
 * @brief Log a plugin warning message with format arguments.
 *
 * @param[in] format Message format.
 * @param[in] ... Format arguments.
 */
#define SRP_LOG_WRN(format, ...) srp_log(SR_LL_WRN, format, __VA_ARGS__)

/**
 * @brief Log a plugin info message with format arguments.
 *
 * @param[in] format Message format.
 * @param[in] ... Format arguments.
 */
#define SRP_LOG_INF(format, ...) srp_log(SR_LL_INF, format, __VA_ARGS__)

/**
 * @brief Log a plugin debug message with format arguments.
 *
 * @param[in] format Message format.
 * @param[in] ... Format arguments.
 */
#define SRP_LOG_DBG(format, ...) srp_log(SR_LL_DBG, format, __VA_ARGS__)

/**
 * @brief Log a simple plugin error message.
 *
 * @param[in] msg Message to log.
 */
#define SRP_LOG_ERRMSG(msg) srp_log(SR_LL_ERR, msg)

/**
 * @brief Log a simple plugin warning message.
 *
 * @param[in] msg Message to log.
 */
#define SRP_LOG_WRNMSG(msg) srp_log(SR_LL_WRN, msg)

/**
 * @brief Log a simple plugin info message.
 *
 * @param[in] msg Message to log.
 */
#define SRP_LOG_INFMSG(msg) srp_log(SR_LL_INF, msg)

/**
 * @brief Log a simple plugin debug message.
 *
 * @param[in] msg Message to log.
 */
#define SRP_LOG_DBGMSG(msg) srp_log(SR_LL_DBG, msg)

/**@} plugin */

/**
 * @internal
 * @brief Log a plugin message with variable arguments.
 *
 * @param[in] ll Log level (severity).
 * @param[in] format Message format.
 * @param[in] ... Format arguments.
 */
void srp_log(sr_log_level_t ll, const char *format, ...);

#ifdef __cplusplus
}
#endif

#endif /* _SYSREPO_H */
