/*
 * Copyright (C) 2010-2020 Red Hat, Inc.
 *
 * Author: Angus Salkeld <asalkeld@redhat.com>
 *
 * This file is part of libqb.
 *
 * libqb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * libqb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef QB_MAP_H_DEFINED
#define QB_MAP_H_DEFINED

#include <stdint.h>
#ifndef S_SPLINT_S
#include <unistd.h>
#endif /* S_SPLINT_S */

/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */

/**
 * @file qbmap.h
 * This provides a map interface to a Patricia trie, hashtable or skiplist.
 *
 * @par Ordering
 * The hashtable is NOT ordered, but ptrie and skiplist are.
 *
 * @par Iterating
 * Below is a simple example of how to iterate over a map.
 * @code
 * const char *p;
 * void *data;
 * qb_map_iter_t *it = qb_map_iter_create(m);
 * for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) {
 *     printf("%s > %s\n", p, (char*) data);
 * }
 * qb_map_iter_free(it);
 * @endcode
 *
 * Deletion of items within the iterator is supported. But note do not
 * free the item memory in the iterator. If you need to free the data
 * items then register for a notifier and free the memory there. This
 * is required as the items are reference counted.
 * @code
 * qb_map_notify_add(m, NULL, my_map_free_handler,
 *		     QB_MAP_NOTIFY_FREE, NULL);
 * @endcode
 *
 * @par Notifications
 * These allow you to get callbacks when values are inserted/removed or
 * replaced.
 * @note hashtable only supports deletion and replacement notificatins.
 * There is also a special global callback for freeing deleted and replaced
 * values (QB_MAP_NOTIFY_FREE).
 * @see qb_map_notify_add() qb_map_notify_del_2()
 *
 * @par Prefix matching
 * The ptrie supports prefixes in the iterator:
 *
 * @code
 * it = qb_map_pref_iter_create(m, "aa");
 * while ((p = qb_map_iter_next(it, &data)) != NULL) {
 *     printf("%s > %s\n", p, (char*)data);
 * }
 * qb_map_iter_free(it);
 * @endcode
 *
 * The ptrie also supports prefixes in notifications:
 * (remember to pass QB_MAP_NOTIFY_RECURSIVE into the notify_add.
 * @code
 * qb_map_notify_add(m, "root", my_map_notification,
 *		    (QB_MAP_NOTIFY_INSERTED|
 *		     QB_MAP_NOTIFY_DELETED|
 *		     QB_MAP_NOTIFY_REPLACED|
 *		     QB_MAP_NOTIFY_RECURSIVE),
 *		    NULL);
 *
 * @endcode
 */

/**
 * This is an opaque data type representing an instance of a map.
 */
typedef struct qb_map qb_map_t;

/**
 * This is an opaque data type representing an iterator instance.
 */
typedef struct qb_map_iter qb_map_iter_t;

#define	QB_MAP_NOTIFY_DELETED   1
#define	QB_MAP_NOTIFY_REPLACED  2
#define QB_MAP_NOTIFY_INSERTED  4
#define	QB_MAP_NOTIFY_RECURSIVE 8
#define	QB_MAP_NOTIFY_FREE      16

typedef void (*qb_map_notify_fn)(uint32_t event,
				 char* key,
				 void* old_value,
				 void* value,
				 void* user_data);

typedef int32_t (*qb_map_transverse_fn)(const char* key,
					void* value,
					void* user_data);

/**
 * Create an unsorted map based on a hashtable.
 *
 * @param max_size maximum size of the hashtable
 *
 * @return the map instance
 */
qb_map_t* qb_hashtable_create(size_t max_size);

/**
 * Create a sorted map using a skiplist.
 *
 * @return the map instance
 */
qb_map_t* qb_skiplist_create(void);

/**
 * Create a sorted map using a Patricia trie or "Radix tree".
 *
 * @htmlonly
 * See the wikipedia <a href="http://en.wikipedia.org/wiki/Radix_Tree">Radix_tree</a>
 * and <a href="http://en.wikipedia.org/wiki/Trie">Trie</a> pages.
 * @endhtmlonly
 */
qb_map_t* qb_trie_create(void);

/**
 * print out the nodes in the trie
 *
 * (for debug purposes)
 */
void
qb_trie_dump(qb_map_t* m);

/**
 * Add a notifier to the map.
 *
 * @param m the map instance
 * @param key the key (or prefix) to attach the notification to.
 * @param fn the callback
 * @param events the type of events to register for.
 * @param user_data a pointer to be passed into the callback
 *
 * @note QB_MAP_NOTIFY_INSERTED is only valid on tries.
 * @note you can use key prefixes with trie maps.
 *
 * @retval 0 success
 * @retval -errno failure
 */
int32_t qb_map_notify_add(qb_map_t* m, const char* key,
			  qb_map_notify_fn fn, int32_t events,
			  void *user_data);

/**
 * Delete a notifier from the map.
 *
 * @note the key,fn and events must match those you added.
 *
 * @param m the map instance
 * @param key the key (or prefix) to attach the notification to.
 * @param fn the callback
 * @param events the type of events to register for.
 *
 * @retval 0 success
 * @retval -errno failure
 */
int32_t qb_map_notify_del(qb_map_t* m, const char* key,
			  qb_map_notify_fn fn, int32_t events);

/**
 * Delete a notifier from the map (including the userdata).
 *
 * @note the key, fn, events and userdata must match those you added.
 *
 * @param m the map instance
 * @param key the key (or prefix) to attach the notification to.
 * @param fn the callback
 * @param events the type of events to register for.
 * @param user_data a pointer to be passed into the callback
 *
 * @retval 0 success
 * @retval -errno failure
 */
int32_t qb_map_notify_del_2(qb_map_t* m, const char* key,
			    qb_map_notify_fn fn, int32_t events,
			    void *user_data);

/**
 * Inserts a new key and value into a qb_map_t.
 *
 * If the key already exists in the qb_map_t, it gets replaced by the new key.
 */
void qb_map_put(qb_map_t *map, const char* key, const void* value);

/**
 * Gets the value corresponding to the given key.
 *
 * @retval NULL (if the key does not exist)
 * @retval a pointer to the value
 */
void* qb_map_get(qb_map_t *map, const char* key);

/**
 * Removes a key/value pair from a map.
 */
int32_t qb_map_rm(qb_map_t *map, const char* key);

/**
 * Get the number of items in the map.
 */
size_t qb_map_count_get(qb_map_t *map);

/**
 * Calls the given function for each of the key/value pairs in the map.
 *
 * The function is passed the key and value of each pair, and the given data
 * parameter. The map is traversed in sorted order.
 */
void qb_map_foreach(qb_map_t *map, qb_map_transverse_fn func, void* user_data);

/**
 * Create an iterator
 */
qb_map_iter_t* qb_map_iter_create(qb_map_t *map);

/**
 * Create a prefix iterator.
 *
 * This will iterate over all items with the given
 * prefix.
 * @note this is only supported by the trie.
 */
qb_map_iter_t* qb_map_pref_iter_create(qb_map_t *map, const char* prefix);

/**
 * Get the next item
 *
 * @param i the iterator
 * @param value (out) the next item's value
 *
 * @retval the next key
 * @retval NULL - the end of the iteration
 */
const char* qb_map_iter_next(qb_map_iter_t* i, void** value);

/**
 * free the iterator
 *
 * @param i the iterator
 */
void qb_map_iter_free(qb_map_iter_t* i);

/**
 * Destroy the map, removes all the items from the map.
 */
void qb_map_destroy(qb_map_t *map);

/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */

#endif /* QB_MAP_H_DEFINED */
