/**
 * @file xml.h
 * @author Radek Krejci <rkrejci@cesnet.cz>
 * @brief Public API of libyang XML parser
 *
 * Copyright (c) 2015 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

#ifndef LY_XML_H_
#define LY_XML_H_

#include <sys/types.h>
#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup xmlparser XML Parser
 *
 * Simplified libyang XML parser for XML data modeled by YANG.
 *
 * @{
 */

/*
 * Structures
 */

/*
 * structure definition from context.h
 */
struct ly_ctx;

/**
 * @brief enumeration of attribute types
 */
typedef enum lyxml_attr_type {
    LYXML_ATTR_STD = 1,              /**< standard XML attribute */
    LYXML_ATTR_NS = 2                /**< XML namespace definition */
} LYXML_ATTR_TYPE;

/**
 * @brief Namespace definition.
 *
 * The structure is actually casted lyxml_attr structure which covers all
 * attributes defined in an element. The namespace definition is in this case
 * also covered by lyxml_attr structure.
 */
struct lyxml_ns {
    LYXML_ATTR_TYPE type;            /**< type of the attribute = LYXML_ATTR_NS */
    struct lyxml_ns *next;           /**< next sibling attribute */
    struct lyxml_elem *parent;       /**< parent node of the attribute */
    const char *prefix;              /**< the namespace prefix if defined, NULL for default namespace */
    const char *value;               /**< the namespace value */
};

/**
 * @brief Element's attribute definition
 *
 * The structure actually covers both the attributes as well as namespace
 * definitions.
 *
 * Attributes are being connected only into a singly linked list (compare it
 * with the elements).
 */
struct lyxml_attr {
    LYXML_ATTR_TYPE type;            /**< type of the attribute */
    struct lyxml_attr *next;         /**< next sibling attribute */
    const struct lyxml_ns *ns;       /**< pointer to the namespace of the attribute if any */
    const char *name;                /**< name of the attribute (the LocalPart of the qualified name) */
    const char *value;               /**< data stored in the attribute */
};

/**
 * @brief Structure describing an element in an XML tree.
 *
 * If the name item is NULL, then the content is part of the mixed content.
 *
 * Children elements are connected in a half ring doubly linked list:
 * - first's prev pointer points to the last children
 * - last's next pointer is NULL
 */
struct lyxml_elem {
    char flags;                      /**< special flags */
#define LYXML_ELEM_MIXED 0x01 /* element contains mixed content */
/* 0x80 is reserved and cannot be set! */

    struct lyxml_elem *parent;       /**< parent node */
    struct lyxml_attr *attr;         /**< first attribute declared in the element */
    struct lyxml_elem *child;        /**< first children element */
    struct lyxml_elem *next;         /**< next sibling node */
    struct lyxml_elem *prev;         /**< previous sibling node */

    const char *name;                /**< name of the element */
    const struct lyxml_ns *ns;       /**< namespace of the element */
    const char *content;             /**< text content of the node if any */
};

/*
 * Functions
 * Parser
 */

/**
 * @defgroup xmlreadoptions XML parser options
 * @ingroup xmlparser
 *
 * Various options to change behavior of XML read functions (lyxml_parse_*()).
 *
 * @{
 */
#define LYXML_PARSE_MULTIROOT 0x01 /**< By default, XML is supposed to be well-formed so the input file or memory chunk
                                        contains only a single XML tree. This option make parser to read multiple XML
                                        trees from a single source (regular file terminated by EOF or memory chunk
                                        terminated by NULL byte). In such a case, the returned XML element has other
                                        siblings representing the other XML trees from the source. */
#define LYXML_PARSE_NOMIXEDCONTENT 0x02 /**< By default, the parser allows elements with mixed content (text content
                                        mixed with standard XML children). This option cases to handle such elements
                                        as invalid input (#LYVE_XML_INVAL). */

/**
 * @}
 */

/**
 * @brief Parse XML from in-memory string
 *
 * @param[in] ctx libyang context to use
 * @param[in] data Pointer to a NULL-terminated string containing XML data to
 * parse.
 * @param[in] options Parser options, see @ref xmlreadoptions.
 * @return Pointer to the root of the parsed XML document tree or NULL in case of empty \p data. To free the
 *         returned data, use lyxml_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case
 *         of error, #ly_errno contains appropriate error code (see #LY_ERR).
 */
struct lyxml_elem *lyxml_parse_mem(struct ly_ctx *ctx, const char *data, int options);

/**
 * @brief Parse XML from filesystem
 *
 * @param[in] ctx libyang context to use
 * @param[in] filename Path to the file where read data to parse
 * @param[in] options Parser options, see @ref xmlreadoptions.
 * @return Pointer to the root of the parsed XML document tree or NULL in case of empty file. To free the
 *         returned data, use lyxml_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case
 *         of error, #ly_errno contains appropriate error code (see #LY_ERR).
 */
struct lyxml_elem *lyxml_parse_path(struct ly_ctx *ctx, const char *filename, int options);

/**
 * @defgroup xmldumpoptions XML printer options
 * @ingroup xmlparser
 *
 * Various options to change behavior of XML dump functions (lyxml_print_*()).
 *
 * When no option is specified (value 0), dumper prints all the content at once.
 *
 * @{
 */
#define LYXML_PRINT_OPEN   0x01  /**< print only the open part of the XML element.
                                   If used in combination with #LYXML_PRINT_CLOSE, it prints the element without
                                   its children: \<element/\>. If none of these two options is used, the element
                                   is printed including all its children. */
#define LYXML_PRINT_FORMAT 0x02  /**< format the output.
                                   If option is not used, the element and its children are printed without indentantion.
                                   If used in combination with #LYXML_PRINT_CLOSE or LYXML_PRINT_ATTRS or LYXML_PRINT_OPEN,
                                   it has no effect.*/
#define LYXML_PRINT_CLOSE  0x04  /**< print only the closing part of the XML element.
                                   If used in combination with #LYXML_PRINT_OPEN, it prints the element without
                                   its children: \<element/\>. If none of these two options is used, the element
                                   is printed including all its children. */
#define LYXML_PRINT_ATTRS  0x08  /**< dump only attributes and namespace declarations of the element (element name
                                   is not printed). This option cannot be used in combination with
                                   #LYXML_PRINT_OPEN and/or #LYXML_PRINT_CLOSE */
#define LYXML_PRINT_SIBLINGS 0x10/**< dump all top-level siblings. By default, the given XML element is supposed to be
                                   the only root element (and document is supposed to be well-formed XML). With this
                                   option the printer consider that the given XML element can has some sibling
                                   elements and print them all (so the given element is not necessarily printed as
                                   the first one). */
#define LYXML_PRINT_NO_LAST_NEWLINE 0x20 /**< makes sense only combined with LYXML_PRINT_FORMAT and causes the very
                                           last newline not to be printed - necessary for correct anyxml XML structure
                                           print. */

/**
 * @}
 */

/**
 * @brief Dump XML tree to a IO stream
 *
 * To write data into a file descriptor instead of file stream, use lyxml_print_fd().
 *
 * @param[in] stream IO stream to print out the tree.
 * @param[in] elem Root element of the XML tree to print
 * @param[in] options Dump options, see @ref xmldumpoptions.
 * @return number of printed characters.
 */
int lyxml_print_file(FILE * stream, const struct lyxml_elem *elem, int options);

/**
 * @brief Dump XML tree to a IO stream
 *
 * Same as lyxml_dump(), but it writes data into the given file descriptor.
 *
 * @param[in] fd File descriptor to print out the tree.
 * @param[in] elem Root element of the XML tree to print
 * @param[in] options Dump options, see @ref xmldumpoptions.
 * @return number of printed characters.
 */
int lyxml_print_fd(int fd, const struct lyxml_elem *elem, int options);

/**
 * @brief Dump XML tree to a IO stream
 *
 * Same as lyxml_dump(), but it allocates memory and store the data into it.
 * It is up to caller to free the returned string by free().
 *
 * @param[out] strp Pointer to store the resulting dump.
 * @param[in] elem Root element of the XML tree to print
 * @param[in] options Dump options, see @ref xmldumpoptions.
 * @return number of printed characters.
 */
int lyxml_print_mem(char **strp, const struct lyxml_elem *elem, int options);

/**
 * @brief Dump XML tree to a IO stream
 *
 * Same as lyxml_dump(), but it writes data via the provided callback.
 *
 * @param[in] writeclb Callback function to write the data (see write(1)).
 * @param[in] arg Optional caller-specific argument to be passed to the \p writeclb callback.
 * @param[in] elem Root element of the XML tree to print
 * @param[in] options Dump options, see @ref xmldumpoptions.
 * @return number of printed characters.
 */
int lyxml_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lyxml_elem *elem, int options);

/**
 * @brief Duplicate the XML tree into the different content.
 *
 * Date parser requires to have the input XML tree in the same context as the resulting data tree. Therefore,
 * if you need to parse a single XML tree into a different contexts, you have to duplicate the source XML
 * tree into the required context first.
 *
 * @param[in] ctx Target context for the result.
 * @param[in] root Root node of the XML tree to duplicate. If an internal node is provided,
 *            the parents are not duplicated and only the specified subtree is duplicated.
 * @result Pointer to the duplicated tree or NULL on error.
 */
struct lyxml_elem *lyxml_dup(struct ly_ctx *ctx, struct lyxml_elem *root);

/**
 * @brief Free (and unlink from the XML tree) the specified element with all
 * its attributes and namespace definitions.
 *
 * @param[in] ctx libyang context to use
 * @param[in] elem Pointer to the element to free.
 */
void lyxml_free(struct ly_ctx *ctx, struct lyxml_elem *elem);

/**
 * @brief Free (and unlink from the XML tree) the specified (sub)tree with all
 * its attributes and namespace definitions. In contrast to lyxml_free(), free also
 * all the element's siblings (preceding as well as following).
 *
 * @param[in] ctx libyang context to use
 * @param[in] elem Pointer to the element to free.
 */
void lyxml_free_withsiblings(struct ly_ctx *ctx, struct lyxml_elem *elem);

/**
 * @brief Unlink the element from its parent. In contrast to lyxml_free(),
 * after return the caller can still manipulate with the elem. Any namespaces
 * are corrected and copied, if needed.
 *
 * @param[in] ctx libyang context to use.
 * @param[in] elem Element to unlink from its parent (if any).
 */
void lyxml_unlink(struct ly_ctx *ctx, struct lyxml_elem *elem);

/**
 * @brief Get value of the attribute in the specified element.
 */
const char *lyxml_get_attr(const struct lyxml_elem *elem, const char *name, const char *ns);

/**
 * @brief Get namespace definition of the given prefix in context of the specified element.
 *
 * @param[in] elem Element where start namespace searching
 * @param[in] prefix Prefix of the namespace to search for
 * @return Namespace defintion or NULL if no such namespace exists
 */
const struct lyxml_ns *lyxml_get_ns(const struct lyxml_elem *elem, const char *prefix);

/**@}*/

#ifdef __cplusplus
}
#endif

#endif /* LY_XML_H_ */
