#ifndef ACL_HTABLE_INCLUDE_H
#define ACL_HTABLE_INCLUDE_H

#ifdef  __cplusplus
extern "C" {
#endif

#include "acl_define.h"
#include "thread/acl_thread.h"
#include "acl_hash.h"			/* just for ACL_HASH_FN */
#include "acl_slice.h"
#include "acl_iterator.h"

/*--------------------------------------------------------------------------*/
typedef struct ACL_HTABLE	ACL_HTABLE;
typedef struct ACL_HTABLE_INFO 	ACL_HTABLE_INFO;

/**
 * 哈希表对象结构句柄
 */
struct ACL_HTABLE {
	int     size;                   /* length of entries array */
	int     init_size;              /* length of initial entryies array */
	int     used;                   /* number of entries in table */
	ACL_HTABLE_INFO **data;         /* entries array, auto-resized */
	unsigned int flag;              /* properties flag */
	int     status;                 /* the operator's status on the htable */

	ACL_HASH_FN  hash_fn;           /* hash function */
	ACL_SLICE_POOL *slice;
	acl_pthread_mutex_t *rwlock;

	/* for acl_iterator */

	/* 取迭代器头函数 */
	void *(*iter_head)(ACL_ITER*, struct ACL_HTABLE*);
	/* 取迭代器下一个函数 */
	void *(*iter_next)(ACL_ITER*, struct ACL_HTABLE*);
	/* 取迭代器尾函数 */
	void *(*iter_tail)(ACL_ITER*, struct ACL_HTABLE*);
	/* 取迭代器上一个函数 */
	void *(*iter_prev)(ACL_ITER*, struct ACL_HTABLE*);
	/* 取迭代器关联的当前容器成员结构对象 */
	ACL_HTABLE_INFO *(*iter_info)(ACL_ITER*, struct ACL_HTABLE*);
};

/**
 * 哈希表中每一个哈希项的存储信息类型
 */
struct ACL_HTABLE_INFO {
	/**
	 * 哈希键, 只所以如此声明，是因为当创建哈希表的标志位为
	 * ACL_BINHASH_FLAG_KEY_REUSE 时需要复用输入的键空间
	 */
	union {
		char *key;
		const char *c_key;
	} key;
	void   *value;			/**< associated value */
	unsigned hash;			/**< store the key's hash value */
	struct ACL_HTABLE_INFO *next;	/**< colliding entry */
	struct ACL_HTABLE_INFO *prev;	/**< colliding entry */
};

/**
 * ACL_HTABLE 遍历用类型
 */
typedef struct ACL_HTABLE_ITER {
	/* public */
	ACL_HTABLE_INFO *ptr;

	/* private */
	int  i;
	int  size;
	ACL_HTABLE_INFO **h;
} ACL_HTABLE_ITER;

/**
 * 建立哈希表
 * @param size 哈希表长度
 * @param flag {unsigned int} 哈希表属性标志位, ACL_BINHASH_FLAG_xxx
 * @return 所建哈希表的头指针或为空(这时表示出了严重的错误, 主要是内存分配问题)
 */
ACL_API ACL_HTABLE *acl_htable_create(int size, unsigned int flag);
/* 添加新的对象时是否直接复用键地址 */
#define	ACL_HTABLE_FLAG_KEY_REUSE	(1 << 0)

/* 是否针对启用多线程互斥方式 */
#define	ACL_HTABLE_FLAG_USE_LOCK	(1 << 1)

/* 每次查询时是否将查询结果对象放在链头 */
#define	ACL_HTABLE_FLAG_MSLOOK		(1 << 2)

/* 统一将键转换为小写，从而实现键查询不区分大小写的功能 */
#define	ACL_HTABLE_FLAG_KEY_LOWER	(1 << 3)

ACL_API ACL_HTABLE *acl_htable_create3(int size, unsigned int flag,
		ACL_SLICE_POOL *slice);

/**
 * 设置哈希表的控制参数
 * @param table 哈希表对象句柄
 * @param name 控制参数的变参初始值, name 及以后的控制参数如下定义
 *  ACL_HTABLE_CTL_END: 变参表结束标志
 *  ACL_HTABLE_CTL_RWLOCK: 是否启用读写锁机制
 *  ACL_HTABLE_CTL_HASH_FN: 用户自定义的哈希值计算函数
 */
ACL_API void acl_htable_ctl(ACL_HTABLE *table, int name, ...);
#define	ACL_HTABLE_CTL_END      0  /**< 控制结束标志 */
#define	ACL_HTABLE_CTL_RWLOCK   1  /**< 是否加锁 */
#define	ACL_HTABLE_CTL_HASH_FN  2  /**< 设置私有哈希函数 */

/**
 * 检查上一次哈希表操作后哈希表的状态
 * @param table 哈希表指针
 * @return {int} 操作哈希表后的状态, 参见如下的 ACL_HTABLE_STAT_XXX
 */
ACL_API int acl_htable_errno(ACL_HTABLE *table);
#define	ACL_HTABLE_STAT_OK          0  /**< 状态正常 */
#define	ACL_HTABLE_STAT_INVAL       1  /**< 无效参数 */
#define	ACL_HTABLE_STAT_DUPLEX_KEY  2  /**< 重复键 */

/**
 * 设置哈希表的当前状态, error 取值 ACL_HTABLE_STAT_XXX
 * @param table 哈希表指针
 * @param error 设置哈希表的错误状态
 */
ACL_API void acl_htable_set_errno(ACL_HTABLE *table, int error);

/**
 * 往哈希表里添加新的项
 * @param table 哈希表指针
 * @param key 键, 在函数内部会复制此 key 键
 * @param value 用户自己的特定数据项(可以由类型硬转化而来, 但是此数据项必须
 *  不能堆栈变量)
 * @return 所分配的哈希表项的指针, == NULL: 表示内部分配内存出错, 为严重的错误
 *  注：如果在添加时该哈希争键存在，则返回已经存在的哈希项，使用者应该通过调用
 *  acl_htable_last_errno() 来查看是否重复添加同一个键值(ACL_HTABLE_STAT_DUPLEX_KEY)
 */
ACL_API ACL_HTABLE_INFO *acl_htable_enter(ACL_HTABLE *table,
		const char *key, void *value);

/**
 * 往哈希表里添加新的项，当多个线程同时进行此操作时，函数内部会自动保证互斥操作
 * @param table 哈希表指针
 * @param key 键, 在函数内部会复制此 key 键
 * @param value 用户自己的特定数据项(可以由类型硬转化而来, 但是此数据项必须
 *  不能堆栈变量)
 * @param callback 如果该函数指针不为空，则当添加成功后便调用该函数
 * @param arg callback 的参数之一
 * @return {int} 0 表示 添加成功，-1 表示添加失败
 *  注：如果在添加时该哈希争键存在，则返回已经存在的哈希项，使用者应该通过调用
 *  acl_htable_last_errno() 来查看是否重复添加同一个键值(ACL_HTABLE_STAT_DUPLEX_KEY)
 */
ACL_API int acl_htable_enter_r(ACL_HTABLE *table, const char *key, void *value,
		void (*callback)(ACL_HTABLE_INFO *ht, void *arg), void *arg);

/**
 * 由所给的 key 键查寻某一特定哈希项
 * @param table 哈希表指针
 * @param key 键
 * @return 不为空指针: 表示查到了对应于 key 键的哈希项
 *         为空: 表示未查到对应于 key 键的哈希项
 */
ACL_API ACL_HTABLE_INFO *acl_htable_locate(ACL_HTABLE *table, const char *key);

/**
 * 由所给的 key 键查寻某一特定哈希项，当多个线程同时进行此操作时，
 * 函数内部会自动保证互斥操作
 * @param table 哈希表指针
 * @param key 键
 * @param callback 查到所要求的键值后如果该指针非空则调用之
 * @param arg callback 参数之一
 * @return 不为空指针: 表示查到了对应于 key 键的哈希项
 *         为空: 表示未查到对应于 key 键的哈希项
 */
ACL_API int acl_htable_locate_r(ACL_HTABLE *table, const char *key,
		void (*callback)(ACL_HTABLE_INFO *ht, void *arg), void *arg);

/**
 * 由所给的 key 键查寻用户的数据项
 * @param table 哈希表指针
 * @param key 键
 * @return 不为空: 表示查到了对应于 key 键的数据项, 用户可以根据用户自己的
 *  数据类型进行转换; 为空: 表示未查到对应于 key 键的数据项
 */
ACL_API void *acl_htable_find(ACL_HTABLE *table, const char *key);

/**
 * 由所给的 key 键查寻用户的数据项, 当多个线程同时进行此操作时，
 * 函数内部会自动保证互斥操作
 * @param table 哈希表指针
 * @param key 键
 * @param callback 当查到所要求的键值后，如果该函数指针不为空则调用之
 * @param arg callback 的参数之一
 * @return 不为空: 表示查到了对应于 key 键的数据项, 用户可以根据用户自己的
 *  数据类型进行转换; 为空: 表示未查到对应于 key 键的数据项
 */
ACL_API int  acl_htable_find_r(ACL_HTABLE *table, const char *key,
		void (*callback)(void *value, void *arg), void *arg);

/**
 * 根据所给的 key 键删除某一哈希项
 * @param table 哈希表指针
 * @param key 键
 * @param free_fn 如果该函数指针不为空并且找到了对应于 key 键的数据项, 则先
 *  调用用户所提供的析构函数做一些清尾工作, 然后再释放该哈希项
 * @return 0: 成功;  -1: 未找到该 key 键
 */
ACL_API int acl_htable_delete(ACL_HTABLE *table, const char *key,
		void (*free_fn) (void *));
#define	acl_htable_delete_r	acl_htable_delete

/**
 * 直接根据 acl_htable_locate 返回的非空对象从哈希表中删除该对象
 * @param table 哈希表指针
 * @param ht {ACL_HTABLE_INFO*} 存储于哈希表中的内部结构对象
 * @param free_fn 如果该函数指针不为空并且找到了对应于 key 键的数据项, 则先
 *  调用用户所提供的析构函数做一些清尾工作, 然后再释放该哈希项
 */
ACL_API void acl_htable_delete_entry(ACL_HTABLE *table, ACL_HTABLE_INFO *ht,
		void (*free_fn) (void *));

/**
 * 释放整个哈希表
 * @param table 哈希表指针
 * @param free_fn 如果该指针不为空则对哈希表中的每一项哈希项先用该函数做
 *  清尾工作, 然后再释放
 */
ACL_API void acl_htable_free(ACL_HTABLE *table, void (*free_fn) (void *));

/**
 * 重置哈希表, 该函数会释放哈希表中的所有内容项, 并重新初始化
 * @param table 哈希表指针
 * @param free_fn 如果该指针不为空则对哈希表中的每一项哈希项先用该函数做
 *  清尾工作, 然后再释放
 * @return 是否重置成功. 0: OK; < 0: error.
 */
ACL_API int acl_htable_reset(ACL_HTABLE *table, void (*free_fn) (void *));
#define	acl_htable_reset_r	acl_htable_reset

/**
 * 对哈希表中的每一项哈希项进行处理
 * @param table 哈希表指针
 * @param walk_fn 处理每一项哈希项的函数指针, 不能为空
 * @param arg 用户自己类型的数据
 */
ACL_API void acl_htable_walk(ACL_HTABLE *table,
		void (*walk_fn) (ACL_HTABLE_INFO *, void *), void *arg);
#define	acl_htable_walk_r	acl_htable_walk

/**
 * 返回哈希表当前的容器空间大小
 * @param table 哈希表指针
 * @return 哈希表的容器空间大小
 */
ACL_API int acl_htable_size(const ACL_HTABLE *table);

/**
 * 返回哈希表当前的窗口中所含元素个数
 * @param table 哈希表指针
 * @return 哈希表中元素个数
 */
ACL_API int acl_htable_used(const ACL_HTABLE *table);

/**
 * 将哈希表里的所有项组合成一个链表
 * @param table 哈希表
 * @return 不为空: 链表指针; 为空: 表示该哈希表里没有哈希项
 */
ACL_API ACL_HTABLE_INFO **acl_htable_list(const ACL_HTABLE *table);

/**
 * 显示哈希表中 key 键的分布状态
 * @param table 哈希表指针
 */
ACL_API void acl_htable_stat(const ACL_HTABLE *table);
#define	acl_htable_stat_r	acl_htable_stat

ACL_API ACL_HTABLE_INFO **acl_htable_data(ACL_HTABLE *table);
ACL_API const ACL_HTABLE_INFO *acl_htable_iter_head(
		ACL_HTABLE *table, ACL_HTABLE_ITER *iter);
ACL_API const ACL_HTABLE_INFO *acl_htable_iter_next(ACL_HTABLE_ITER *iter);
ACL_API const ACL_HTABLE_INFO *acl_htable_iter_tail(
		ACL_HTABLE *table, ACL_HTABLE_ITER *iter);
ACL_API const ACL_HTABLE_INFO *acl_htable_iter_prev(ACL_HTABLE_ITER *iter);

/*--------------------  一些方便快捷的宏操作 --------------------------------*/

#define	ACL_HTABLE_ITER_KEY(iter)	((iter).ptr->key.c_key)
#define	acl_htable_iter_key		ACL_HTABLE_ITER_KEY

#define	ACL_HTABLE_ITER_VALUE(iter)	((iter).ptr->value)
#define	acl_htable_iter_value		ACL_HTABLE_ITER_VALUE

/**
 * 遍历 ACL_HTABLE
 * @param iter {ACL_HTABLE_ITER}
 * @param table_ptr {ACL_HTABLE *}
 * @example:
	void test()
	{
		ACL_HTABLE *table = acl_htable_create(10, 0);
		ACL_HTABLE_ITER iter;
		char *value, key[32];
		int   i;

		for (i = 0; i < 100; i++) {
			value = (char*) acl_mystrdup("value");
			snprintf(key, sizeof(key), "key:%d", i);
			(void) acl_htable_enter(table, key, value);
		}

		acl_htable_foreach(iter, table) {
			printf("%s=%s\n", acl_htable_iter_key(iter),
				acl_htable_iter_value(iter));
			if (i == 50)
				break;
		}
		acl_htable_free(table, acl_myfree_fn);
	}
 */

#if 0
#define	ACL_HTABLE_FOREACH(iter, table_ptr)  \
    if (table_ptr)  \
        for((iter).size = acl_htable_size((table_ptr)), (iter).i = 0,  \
          (iter).h = acl_htable_data((table_ptr)); (iter).i < (iter).size; (iter).i++)  \
            for ((iter).ptr = *(iter).h++; (iter).ptr; (iter).ptr = (iter).ptr->next)
#define	ACL_HTABLE_FOREACH_REVERSE(iter, table_ptr)  \
    if (table_ptr)  \
        for((iter).size = acl_htable_size((table_ptr)), (iter).i = (iter).size - 1,  \
          (iter).h = acl_htable_data((table_ptr)) + (iter).i; (iter).i >= 0; (iter).i--)  \
            for ((iter).ptr = *(iter).h--; (iter).ptr; (iter).ptr = (iter).ptr->next)
#else
#define	ACL_HTABLE_FOREACH(iter, table_ptr)  \
    if (table_ptr)  \
        for((void) acl_htable_iter_head((table_ptr), &iter);  \
            (iter).ptr;  \
            (void) acl_htable_iter_next(&iter))
#define	ACL_HTABLE_FOREACH_REVERSE(iter, table_ptr)  \
    if (table_ptr)  \
        for((void) acl_htable_iter_tail((table_ptr), &iter);  \
            (iter).ptr;  \
            (void) acl_htable_iter_prev(&iter))
#endif

#define	acl_htable_foreach		ACL_HTABLE_FOREACH
#define	acl_htable_foreach_reverse	ACL_HTABLE_FOREACH_REVERSE

#ifdef  __cplusplus
}
#endif

#endif

