#ifndef _LSUP_NAMESPACE_H
#define _LSUP_NAMESPACE_H

#include "hashmap.h"

#include "core.h"

/** @brief Namespace prefix length, including terminator.
 */
#define PFX_LEN 8


/** @brief Namespace map structure.
 *
 * It contains a double hash map of pfx->ns and ns->pfx for fast 2-way lookup.
 *
 * Prefixes are fixed PFX_LEN-size strings, namespaces are arbitrary sized
 * strings.
 */
typedef struct hashmap LSUP_NSMap;

/** @brief Namespace prefix type.
 */
typedef char ns_pfx[PFX_LEN];


/** @brief Create a new namespace map.
 *
 * @return A pointer to an empty map. It must be freed with #LSUP_nsmap_free().
 */
LSUP_NSMap *
LSUP_nsmap_new (void);


/** @brief Free a namespace map and its internal structures.
 *
 * @param[in] map The map to free.
 */
void
LSUP_nsmap_free (LSUP_NSMap *map);


/** @brief Add a prefix -> namespace pair to the map or update it.
 *
 * If the prefix already exists, it is quietly updated with the new value.
 *
 * @param[in] map The map to add to.
 *
 * @param[in] pfx The namespace prefix.
 *
 * @param[in] nsstr Fully qualified namespace.
 *
 * @return LSUP_OK if the record was added or replaced; LSUP_MEM_ERR if an
 * allocation error occurred.
 */
LSUP_rc
LSUP_nsmap_add (LSUP_NSMap *map, const char *pfx, const char *nsstr);


/** @brief Remove a prefix -> namespace pair from a map.
 *
 * @param[in] map The map to remove from.
 *
 * @param[in] pfx The namespace prefix to remove.
 *
 * @return LSUP_OK on successful delete; LSUP_NOACTION if no record was found.
 */
LSUP_rc
LSUP_nsmap_remove (LSUP_NSMap *map, const char *pfx);


/** @brief Get the namespace for a prefix.
 *
 * @param[in] map The map to look up the namespace in.
 *
 * @param[in] pfx The prefix to look up.
 *
 * @return A pointer to the namespace string. Note that this is not a copy and
 *  should not be modified directly.
 */
const char *
LSUP_nsmap_get_ns (const LSUP_NSMap *map, const char *pfx);


/** @brief Get the prefix for a namespace.
 *
 * @param[in] map The map to look up the prefix in.
 *
 * @param[in] pfx The namespace to look up.
 *
 * @return Found prefix, or NULL if the namespace is not mapped.
 */
const char *
LSUP_nsmap_get_pfx (const LSUP_NSMap *map, const char *ns);


/** @brief Convert a namespace-prefixed string to a FQ URI sring if mapped.
 *
 * @param[in] map Namespace map to look up.
 *
 * @param[in] uri URI string to denormalize.
 *
 * @param[out] fq_uri String pointer to be filled with the FQ URI. If the
 *  namespace is not in the map or an error occurred, this will be NULL.
 *  The caller is in charge of freeing the memory.
 *
 * @return LSUP_OK on success, LSUP_NORESULT if no entry was found in the map,
 *  LSUP_MEM_ERR if a memory allocation error ocurred.
 */
LSUP_rc
LSUP_nsmap_normalize_uri (
        const LSUP_NSMap *map, const char *pfx_uri, char **fq_uri);


/** @brief Convert a FQ URI string to a prefixed string if the prefix is found.
 *
 * TODO Note that this function does not attempt to find the longest or
 * shortest namespace prefix in case of conflicts (e.g. when both
 * `http://example.edu/` and `http://example.edu/data/` are mapped and
 * `http://example.edu/data/51937642` is being denormalized). In such
 * case, the first prefix that is found is assigned.
 *
 * @param[in] map Namespace map to look up.
 *
 * @param[in] uri URI string to normalize.
 *
 * @param[out] String pointer to be filled with the prefixed URI. If the
 *  namespace is not in the map or an error occurred, this will be NULL.
 *  The caller is in charge of freeing the memory.
 *
 * @return LSUP_OK on success, LSUP_NORESULT if no entry was found in the map,
 *  LSUP_MEM_ERR if a memory allocation error ocurred.
 */
LSUP_rc
LSUP_nsmap_denormalize_uri (
        const LSUP_NSMap *map, const char *fq_uri, char **pfx_uri);


/** @brief Dump all entries of a namespace map.
 *
 * @param[in] map Map to dump.
 *
 * @return 2-dimensional array of strings, with as many rows as namespace
 *  entries, and two columns: the first for the namespace prefix, the second
 *  for the namespace.
 */
const char ***
LSUP_nsmap_dump (const LSUP_NSMap *map);

#endif