#ifndef ACL_TOKEN_TREE_INCLUDE_H
#define ACL_TOKEN_TREE_INCLUDE_H

#ifdef __cplusplus
extern "C" {
#endif
#include "acl_define.h"
#include "acl_vstring.h"
#include "acl_iterator.h"

#define ACL_PRINT_CHAR(x)  \
	((((x) >= 'a' && (x) <='z')  \
	|| ((x) >= 'A' && (x) <= 'Z')  \
	|| ((x) >= '0' && (x) <= '9')  \
	|| (x) == ';' || (x) == '!'  \
	|| (x) == ':' || (x) == ','  \
	|| (x) == '.' || (x) == '@'  \
	|| (x) == '#' || (x) == '$'  \
	|| (x) == '%' || (x) == '^'  \
	|| (x) == '&' || (x) == '*'  \
	|| (x) == '(' || (x) == ')'  \
	|| (x) == '-' || (x) == '='  \
	|| (x) == '|' || (x) == '\\'  \
	|| (x) == '[' || (x) == ']'  \
	|| (x) == '{' || (x) == '}'  \
	|| (x) == '\'' || (x) == '"')  \
	? (x) : '-')

typedef struct ACL_TOKEN ACL_TOKEN;

struct ACL_TOKEN {
	unsigned char     ch;
	unsigned int      flag;
#define ACL_TOKEN_F_NONE	0
#define ACL_TOKEN_F_STOP	(1 << 0)
#define ACL_TOKEN_F_PASS	(1 << 1)
#define ACL_TOKEN_F_DENY	(1 << 2)
#define ACL_TOKEN_F_UTF8	(1 << 3)

#define ACL_TOKEN_WIDTH		256
	struct ACL_TOKEN *tokens[ACL_TOKEN_WIDTH];
	struct ACL_TOKEN *parent;
	void             *ctx;

	ACL_TOKEN *(*iter_head)(ACL_ITER*, ACL_TOKEN*);
	ACL_TOKEN *(*iter_next)(ACL_ITER*, ACL_TOKEN*);
};

#define ACL_TOKEN_TREE_WORD_MATCH(acl_token_tree_in, word_in, acl_token_out) \
{ \
    const unsigned char *_ptr = (const unsigned char*) word_in; \
    ACL_TOKEN *_token_iter = acl_token_tree_in, *_token = NULL; \
    while (*_ptr) { \
        _token = _token_iter->tokens[*_ptr]; \
        if (_token == NULL) \
            break; \
        _token_iter = _token; \
        _ptr++; \
    } \
    if (_token && (_token->flag & ACL_TOKEN_F_STOP)) \
        acl_token_out = _token; \
    else \
        acl_token_out = NULL; \
}

#define ACL_TOKEN_TREE_MATCH(acl_token_tree_in, s_in, delim_in, delim_tab_in, acl_token_out) do  \
{  \
    ACL_TOKEN *acl_token_iter = (acl_token_tree_in), *acl_token_tmp; \
    (acl_token_out) = NULL;  \
    if (((const char*) delim_in)) {  \
        int   _i;  \
        while (*(s_in)) {  \
            for (_i = 0; ((const char*) delim_in)[_i]; _i++) {  \
                if (*(s_in) == ((const char*) delim_in)[_i])  \
                    goto _END;  \
            }  \
            acl_token_tmp = acl_token_iter->tokens[*((const unsigned char*)(s_in))];  \
            if (acl_token_tmp == NULL) {  \
                if ((acl_token_out))  \
                    break;  \
                acl_token_iter = (acl_token_tree_in);  \
                acl_token_tmp = acl_token_iter->tokens[*((const unsigned char*)(s_in))];  \
                if (acl_token_tmp == NULL) {  \
                    (s_in)++;  \
                    continue;  \
                }  \
            }  \
            if ((acl_token_tmp->flag & ACL_TOKEN_F_STOP))  \
                (acl_token_out) = acl_token_tmp;  \
            acl_token_iter = acl_token_tmp;  \
            (s_in)++;  \
        }  \
_END:  \
        break;  \
    } else if (((char*) delim_tab_in)) {  \
        while (*(s_in)) {  \
            if (((char*) delim_tab_in)[*((const unsigned char*)(s_in))])  \
                break;  \
            acl_token_tmp = acl_token_iter->tokens[*((const unsigned char*)(s_in))];  \
            if (acl_token_tmp == NULL) {  \
                if ((acl_token_out))  \
                    break;  \
                acl_token_iter = (acl_token_tree_in);  \
                acl_token_tmp = acl_token_iter->tokens[*((const unsigned char*)(s_in))];  \
                if (acl_token_tmp == NULL) {  \
                    (s_in)++;  \
                    continue;  \
                }  \
            }  \
            if ((acl_token_tmp->flag & ACL_TOKEN_F_STOP))  \
                (acl_token_out) = acl_token_tmp;  \
            acl_token_iter = acl_token_tmp;  \
            (s_in)++;  \
        }  \
    } else {  \
        while (*(s_in)) {  \
            acl_token_tmp = acl_token_iter->tokens[*((const unsigned char*)(s_in))];  \
            if (acl_token_tmp == NULL) {  \
                if ((acl_token_out))  \
                    break;  \
                acl_token_iter = (acl_token_tree_in);  \
                acl_token_tmp = acl_token_iter->tokens[*((const unsigned char*)(s_in))];  \
                if (acl_token_tmp == NULL) {  \
                    (s_in)++;  \
                    continue;  \
                }  \
            }  \
            if ((acl_token_tmp->flag & ACL_TOKEN_F_STOP))  \
                (acl_token_out) = acl_token_tmp;  \
            acl_token_iter = acl_token_tmp;  \
            (s_in)++;  \
        }  \
    }  \
} while (0)

ACL_API char *acl_token_delim_tab_new(const char *delim);
ACL_API void acl_token_delim_tab_free(char *delim_tab);
ACL_API ACL_TOKEN *acl_token_new(void);
ACL_API void acl_token_free(ACL_TOKEN *token);
ACL_API void acl_token_name(ACL_TOKEN *token, ACL_VSTRING *buf);
ACL_API const char *acl_token_name1(ACL_TOKEN *token);
ACL_API ACL_TOKEN *acl_token_tree_add(ACL_TOKEN *tree,
	const char *word, unsigned int flag, const void *ctx);
ACL_API ACL_TOKEN *acl_token_tree_add_word_map(ACL_TOKEN *tree,
	const char *word, const char *word_map, unsigned int flag);
ACL_API ACL_TOKEN *acl_token_tree_word_match(ACL_TOKEN *tree,
	const char *word);
ACL_API void *acl_token_tree_word_remove(ACL_TOKEN *tree, const char *word);
ACL_API ACL_TOKEN *acl_token_tree_match(ACL_TOKEN *tree,
	const char **ptr, const char *delim, const char *delim_tab);
ACL_API void acl_token_tree_walk(ACL_TOKEN *tree,
	void (*walk_fn)(ACL_TOKEN*, void*), void *arg);
ACL_API void acl_token_tree_print(ACL_TOKEN *tree);
ACL_API ACL_TOKEN *acl_token_tree_create(const char *s);
ACL_API ACL_TOKEN *acl_token_tree_create2(const char *s, const char *sep);
ACL_API void acl_token_tree_destroy(ACL_TOKEN *tree);
ACL_API void acl_token_tree_load_deny(const char *filepath, ACL_TOKEN *tree);
ACL_API void acl_token_tree_load_pass(const char *filepath, ACL_TOKEN *tree);

#ifdef __cplusplus
}
#endif

#endif
