/*-
 * Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
 * All rights reserved
 *
 * Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <errno.h>
#include <limits.h>
#include <unistd.h>

#include "vsb.h"

/*
 * A quadbit patricia trie comprises a struct qp_y, representing the root
 * node of the true, and a table of strings, both of which are owned by a
 * VMOD object. Lookups return the index of a string in the table, and
 * prefix searches fill a struct match_data.
 */
struct qp_y;

/*
 * struct match_data is initialized with an array at indices of length
 * limit. After a call to QP_Prefixes(), the indices array is filled with
 * n indices of elemnts of the strings table that are matching prefixes.
 * Elements of indices are ordered from shortest to longest prefix.  If
 * there is an exact match, its index is in field exact, otherwise exact
 * is 0. min is the lowest matching index, max is the highest.
 */
struct match_data {
	unsigned	magic;
#define MATCH_DATA_MAGIC 0x0d9a845e
	unsigned	*indices;
	unsigned	limit;
	unsigned	n;
	unsigned	exact;
	unsigned	min;
	unsigned	max;
};

struct qp_stats {
	unsigned	magic;
#define QP_STATS_MAGIC 0x06d2b30c
	uint64_t	nodes;
	uint64_t	leaves;
	uint64_t	terms;
	uint64_t	nodesz;
	uint64_t	dmin;
	uint64_t	dmax;
	double		davg;
	uint64_t	fmin;
	uint64_t	fmax;
	double		favg;
};

/*
 * Insert the string at strings[idx] into the trie whose root is
 * *root. All of root, strings, and strings[idx] MAY NOT be NULL.
 * If *root is NULL, then the trie is empty, and *root is set to
 * the new root. All new nodes are allocated by the function.
 *
 * QP_Insert() SHALL be invoked with successively longer strings at
 * strings[idx}.  It suffices to insert strings in the order that results
 * from strcmp(3).
 *
 * Returns 0 on success. On error, errno is set. errno == EINVAL if the
 * same string is inserted more than once. errno == EPERM if
 * allow_overlaps is 0, but a string with the same prefix was already
 * inserted. errno may be set to other values (most likely ENOMEM for
 * malloc failures).
 */
int QP_Insert(struct qp_y * * restrict root, unsigned idx,
	      char * const restrict * const restrict strings,
	      unsigned allow_overlaps);

/*
 * Return the index of subject in the table strings.
 *
 * root MUST be the root of the trie generated previously by use of
 * QP_Insert(). strings and subject MAY NOT be NULL.
 *
 * Returns the index of subject in strings, or UINT_MAX if subject is not
 * in strings or if root is NULL.
 */
unsigned QP_Lookup(const struct qp_y * const restrict root,
		   char * const restrict * const restrict strings,
		   const char * const restrict subject);

/*
 * Fill *match with information about elements of strings that are
 * prefixes of subject, as described above.
 *
 * All of strings, subject, match, and match->indices MAY NOT be NULL, and
 * match->limit MAY NOT be 0. match MUST have the correct magic number.
 *
 * Returns 0 iff there is sufficient space at match->indices for the
 * prefix indices. Note that match->n is 0 if no prefixes were found.
 */
int QP_Prefixes(const struct qp_y * const restrict root,
		char * const restrict * const restrict strings,
		const char * const restrict subject,
		struct match_data * const restrict match);

/*
 * Fill *stats with statistics for trie at root. stats MAY NOT be NULL,
 * and MUST have a valid magic number. If root is non-NULL, then it MUST
 * be the root of trie formed previously with QP_Insert() and strings.
 */
void QP_Stats(const struct qp_y * const restrict root,
	      char * const restrict * const restrict strings,
	      struct qp_stats * const restrict stats);

/* Free y. Silently does nothing if y is NULL. */
void QP_Free(struct qp_y *y);

/*
 * Return a string dump of root as generated for strings.
 *
 * Returns an empty buffer if root is NULL. If root is non-NULL, strings
 * MAY NOT be NULL.
 */
struct vsb * QP_Dump(struct qp_y *root, char **strings);
