/*
 * $Id$
 *
    Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.

    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 "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 THE AUTHOR 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.
 */

#ifndef _QSE_HTL_T_
#define _QSE_HTL_T_

/** \file
 * This file provides the hash table for fixed-size data.
 */

#include <qse/types.h>
#include <qse/macros.h>

/**
 * The #qse_htl_walk_t type defines walking directions.
 */
enum qse_htl_walk_t
{
	QSE_HTL_WALK_STOP     = 0,
	QSE_HTL_WALK_FORWARD  = 1,
};
typedef enum qse_htl_walk_t qse_htl_walk_t;

typedef struct qse_htl_t qse_htl_t;
typedef struct qse_htl_node_t  qse_htl_node_t;

/**
 * The #qse_htl_hasher_t type defines a data hasher function.
 */
typedef qse_uint32_t (*qse_htl_hasher_t) (
	qse_htl_t*  htl,
	const void* data
);

/**
 * The #qse_htl_comper_t type defines a key comparator that is called when
 * the list needs to compare data.  The comparator must return 0 if the data
 * are the same and a non-zero integer otherwise.
 */
typedef int (*qse_htl_comper_t) (
	qse_htl_t*  htl,   /**< hash table */
	const void* data1, /**< data pointer */
	const void* data2  /**< data pointer */
);

/**
 * The #qse_htl_feeeer_t type defines a data deallocation function.
 */
typedef void (*qse_htl_freeer_t) (
	qse_htl_t*  htl,
	void*       data
);

/**
 * The #qse_htl_copier_t type defines a data copier function.
 */
typedef void* (*qse_htl_copier_t) (
	qse_htl_t*  htl,
	void*       data
);

/**
 * The #qse_htl_walker_t function defines a callback function 
 * for qse_htl_walk().
 */
typedef qse_htl_walk_t (*qse_htl_walker_t) (
	qse_htl_t* htl,
	void*      data,
	void*      ctx
);

struct qse_htl_node_t
{
	struct qse_htl_node_t* next;
	qse_uint32_t           reversed;
	qse_uint32_t           key;
 	void*                  data;
};

struct qse_htl_t 
{
	qse_mmgr_t* mmgr;

	int keysize; /* default key size */

	int num_elements;
	int num_buckets; /* power of 2 */
	int next_grow;
	int mask;

	qse_htl_hasher_t hasher;
	qse_htl_comper_t comper;
	qse_htl_freeer_t freeer;
	qse_htl_copier_t copier;

	qse_htl_node_t   null;
	qse_htl_node_t** buckets;
};

#if defined(__cplusplus)
extern "C" {
#endif

/**
 * The qse_htl_open() function creates an hash table.
 */
QSE_EXPORT qse_htl_t* qse_htl_open (
	qse_mmgr_t* mmgr,
	qse_size_t  xtnsize,
	int         scale
);

/**
 * The qse_htl_close() function destroys an hash table.
 */
QSE_EXPORT void qse_htl_close (
	qse_htl_t* htl  /**< hash table */
);

/**
 * The qse_htl_open() function initializes an hash table.
 */
QSE_EXPORT int qse_htl_init (
	qse_htl_t*  htl,
	qse_mmgr_t* mmgr,
	int         scale
);

/**
 * The qse_htl_close() function finalizes an hash table.
 */
QSE_EXPORT void qse_htl_fini (
	qse_htl_t* htl  /**< hash table */
);


#if defined(QSE_HAVE_INLINE)
static QSE_INLINE qse_mmgr_t* qse_htl_getmmgr (qse_htl_t* htl) { return (htl)->mmgr; }
#else
#define qse_htl_getmmgr(htl) ((htl)->mmgr))
#endif

#if defined(QSE_HAVE_INLINE)
static QSE_INLINE void* qse_htl_getxtn (qse_htl_t* htl) { return QSE_XTN(htl); }
#else
#define qse_htl_getxtn(htl) (QSE_XTN(htl))
#endif

#if defined(QSE_HAVE_INLINE)
static QSE_INLINE int qse_htl_getsize (qse_htl_t* htl) { return htl->num_elements; }
#else
#define qse_htl_getsize(htl) ((htl)->num_elements)
#endif

#if defined(QSE_HAVE_INLINE)
static QSE_INLINE qse_htl_hasher_t qse_htl_gethasher (qse_htl_t* htl) { return htl->hasher; }
static QSE_INLINE qse_htl_comper_t qse_htl_getcomper (qse_htl_t* htl) { return htl->comper; }
static QSE_INLINE qse_htl_freeer_t qse_htl_getfreeer (qse_htl_t* htl) { return htl->freeer; }

static QSE_INLINE void qse_htl_sethasher (qse_htl_t* htl, qse_htl_hasher_t _hasher) { htl->hasher = _hasher; }
static QSE_INLINE void qse_htl_setcomper (qse_htl_t* htl, qse_htl_comper_t _comper) { htl->comper = _comper; }
static QSE_INLINE void qse_htl_setfreeer (qse_htl_t* htl, qse_htl_freeer_t _freeer) { htl->freeer = _freeer; }
static QSE_INLINE void qse_htl_setcopier (qse_htl_t* htl, qse_htl_copier_t _copier) { htl->copier = _copier; }
#else
#define qse_htl_gethasher(htl) ((htl)->hasher)
#define qse_htl_getcomper(htl) ((htl)->comper)
#define qse_htl_getfreeer(htl) ((htl)->freeer)

#define qse_htl_sethasher(htl,_hasher) ((htl)->hasher = (_hasher))
#define qse_htl_setcomper(htl,_comper) ((htl)->comper = (_comper))
#define qse_htl_setfreeer(htl,_freeer) ((htl)->freeer = (_freeer))
#define qse_htl_setcopier(htl,_copier) ((htl)->copier = (_copier))
#endif

/**
 * The qse_htl_search() function searches a hash table to find a 
 * matching item. It returns the index to the slot of an internal array
 * where the matching item is found. 
 * \return slot index if a match if found,
 *         #QSE_NULL if no match is found.
 */
QSE_EXPORT qse_htl_node_t* qse_htl_search (
	qse_htl_t*       htl,    /**< hash table */
	const void*      data    /**< data pointer */
);

QSE_EXPORT qse_htl_node_t* qse_htl_heterosearch (
	qse_htl_t*       htl,     /**< hash table */
	const void*      data,    /**< data pointer */
	qse_htl_hasher_t hasher,
	qse_htl_comper_t comper
);

/**
 * The qse_htl_insert() function inserts a new item. It fails if it finds
 * an existing item.
 * \return a node pointer on success, #QSE_NULL on failure.
 */
QSE_EXPORT qse_htl_node_t* qse_htl_insert (
	qse_htl_t*  htl,    /**< hash table */
	void*       data    /**< data pointer */
);

/**
 * The qse_htl_upsert() function inserts a new item if it finds no matching
 * item or updates an exsting item if finds a matching item.
 * \return node pointer on success, #QSE_NULL on failure.
 */
QSE_EXPORT qse_htl_node_t* qse_htl_upsert (
	qse_htl_t*       htl,    /**< hash table */
	void*            data    /**< data pointer */
);

/**
 * The qse_htl_update() function updates an existing item. It fails if it finds
 * no existing item.
 * \return a node pointer on success, #QSE_NULL on failure.
 */
QSE_EXPORT qse_htl_node_t* qse_htl_update (
	qse_htl_t*       htl,    /**< hash table */
	void*            data    /**< data pointer */
);

/**
 * The qse_htl_upyank() function updates an existing dataum without disposing
 * the old data. 
 */
QSE_EXPORT qse_htl_node_t* qse_htl_upyank (
	qse_htl_t*       htl,    /**< hash table */
	void*            data,   /**< data pointer */
	void**           oldata  /**< old data pointer */
);

/**
 * The qse_htl_ensert() function inserts a new item if one is not found.
 * It returns an existing item otherwise.
 */
QSE_EXPORT qse_htl_node_t* qse_htl_ensert (
	qse_htl_t*       htl,    /**< hash table */
	void*            data    /**< data pointer */
);

/**
 * The qse_htl_delete() function deletes an existing item. It fails if it finds
 * no existing item.
 * \return 0 on success, -1 on failure
 */
QSE_EXPORT int qse_htl_delete (
	qse_htl_t*       htl,    /**< hash table */
	void*            data    /**< data pointer */
);

QSE_EXPORT void* qse_htl_yank (
	qse_htl_t*       ht,
	void*            data
);

QSE_EXPORT qse_htl_node_t* qse_htl_yanknode (
	qse_htl_t*       ht,
	void*            data
);

/**
 * The qse_htl_clear() functions deletes all data items.
 */
QSE_EXPORT void qse_htl_clear (
	qse_htl_t*       htl     /**< hash table */
);

/**
 * The qse_htl_walk() function executes the callback function @a walker for
 * each valid data item.
 */
QSE_EXPORT void qse_htl_walk (
	qse_htl_t*       htl,    /**< hash table */
	qse_htl_walker_t walker, /**< callback function */
	void*            ctx     /**< context */
);

/* ------------------------------------------------------------------------- */

QSE_EXPORT qse_uint32_t qse_genhash32_update (const void* data, qse_size_t size, qse_uint32_t hash);
QSE_EXPORT qse_uint32_t qse_genhash32 (const void *data, qse_size_t size);
/*qse_uint32_t qse_foldhash32 (qse_uint32_t hash, int bits);*/

QSE_EXPORT qse_uint32_t qse_mbshash32 (const qse_mchar_t* p);
QSE_EXPORT qse_uint32_t qse_wcshash32 (const qse_wchar_t* p);
QSE_EXPORT qse_uint32_t qse_mbscasehash32 (const qse_mchar_t* p);
QSE_EXPORT qse_uint32_t qse_wcscasehash32 (const qse_wchar_t* p);

#if defined(QSE_CHAR_IS_WCHAR)
#	define qse_strhash32(x)     qse_wcshash32(x)
#	define qse_strcasehash32(x) qse_wcscasehash32(x)
#else
#	define qse_strhash32(x)     qse_mbshash32(x)
#	define qse_strcasehash32(x) qse_mbscasehash32(x)
#endif

#if defined(__cplusplus)
}
#endif

#endif
