#ifndef C99TREE_TREE_H
#define C99TREE_TREE_H
typedef struct node_s node_t;

#include "location.h"

/* tree node types */
typedef enum node_type_e {
	T_CALL,          /* function call instruction */
	T_STATEMENT,
	T_TYPE,          /* type container for qualifiers, specifier, typenames, attribs */
	T_DECL,
	T_FUNCDEF,
	T_ARGLIST,       /* function arguments */
	T_ARG,           /* a specific argument */
	T_BLOCK,         /* compound statement */
	T_CLASS,
	T_TYPEOF,
	T_ATTRLIST,
	T_ATTR,
	T_PTR,
	T_INDEX,
	T_QUALIFIER,
	T_ID,
	T_ELLIPSIS,      /* ... */
	T_ENUM,
	T_INIT,
	T_STRUCT,
	T_BITS,          /* :X part of a bitfield */
	T_ASM,
	T_LABEL,         /* jump and switch labels */
	T_CONDITION,     /* condition section of while(), for() and if() */
	T_THEN,
	T_ELSE,
	T_OP,            /* operator */
	T_ICONST,        /* integer constant */
	T_FCONST,        /* floating point constant */
	T_SCONST,        /* string constant */
	T_SCONSTLIST,    /* appended string constants */
	T_DEBUG,         /* dummy node for debugging - shouldn't show up in any of the production output */
	T_UNION,
	T_PRAGMA,
	T_TYPECUSTOM,    /* typedef'd types */
	T_TYPESPEC,      /* long, short, signed, unsigned, _Complex and _Imaginary */
	T_TYPENAME,      /* name of the type (like int, float, etc.) */
	T_TYPEDEF,
	T_EXPRLIST,      /* comma separated list of expressions (1,2,3) */
	T_EXPRBRACKET,   /* a bracketed expression, i.e. (expr) */
	T_COMMENT,       /* goes in the token tree */
	T_INITLIST,      /* initializer list (int a[] = {1, 2}; ) */
	T_GRAMMAR_TREE,  /* the root of the grammar subtree */
	T_TOKEN_TREE,    /* the root of the token subtree */
	T_TOKEN_STR,     /* a non-comment token in the token tree */
	T_TOKEN_PPDIR,   /* a preprocessor-directive token (#) */
	T_GENTREX,       /* subtree for all gentrex script results */
	T_GENTREX_RESULT,/* results of a script */
	T_GENTREX_MAP,   /* results of a script */
	T_SYMLINK,       /* name is an uid to a node to link to */
	T_INVALID        /* matches with the NULL in the name list */
} node_type_t;

extern const char *node_type_names[];

struct node_s {
	node_type_t type;
	int uid;

	char *name;         /* name field - textual payload for some nodes */
	loc_t loc;          /* location info */
	int print_data;     /* random data for the output layer */

	/* flags */
	int redefinable;    /* the type can be redefined without an error */

	node_t *next;
	node_t *children;

	/* added in post-processing */
	node_t *parent;

	/* temporary fields for building the tree */
	loc_t term_loc;          /* location info for the terminator token (';' usally) : not part of the the current node */
};

/* builds/frees type hash */
void tree_init();
void tree_uninit();


/* Look up type by name */
node_type_t Ntype(const char *typenam);

#ifdef PARSER_NALLOC_DEBUG
# define YLINEARG   , int yline
# define YLINEPARM  , yline
#else
# define YLINEARG
# define YLINEPARM
#endif

/* Allocate a new tree node - ->next is empty, line is filled in from global lineno */
node_t *Nalloc(node_type_t type, char *name, node_t *child YLINEARG);

/* Same as Nalloc, set start and/or end location */
node_t *Nalloc_wloc(node_type_t type, char *name, node_t *child, const loc_t *start, const loc_t *end YLINEARG);

/* Same as Nalloc, but take a token (with start location) instead of a name */
node_t *Nalloc_tok(node_type_t type, strloc_t *token, node_t *child YLINEARG);

/* Append src node to the last ->next starting from dest - returns dest */
node_t *Nappend(node_t *dest, node_t *src);

/* Create operator node with 2 operands; 2nd operand may be NULL */
node_t *Nop(const char *op, node_t *o1, node_t *o2, loc_t *start, loc_t *end YLINEARG);
node_t *Nop_tok(const strloc_t *op, node_t *o1, node_t *o2 YLINEARG);

typedef enum invalidate_loc_e {
	INV_LOC_NONE,
	INV_LOC_ALL,
	INV_LOC_ALL_BUT_ID
} invalidate_loc_t;

/* Clone a subtree recursively */
node_t *Nclone(const node_t *node, invalidate_loc_t invalidate_loc YLINEARG);

/* Free a subtree recurisively */
void Nfree(node_t *node);

/* Print node and all ->next nodes recursively, prefix lines with prefix. If
   local_only is non-zero, print nodes found in the main file only, not the
   nodes from includes
   nprint_ctx should point to a void * that is NULL before the call; the value
   after the call should be ignored.
   */
void Nprint(void **nprint_ctx, const char *prefix, node_t *node, int local_only);

/* return the last node on the ->next list of node */
node_t *Nlast(node_t *node);

/* set dest loc to span between the leftmost and rightmost item of subtree child */
void Nloc_cpy_subtree(node_t *dest, node_t *child);

/* token tree: append token node */
void tt_append(node_type_t type, char *text YLINEARG);

/* Append src node as dest node's last child
   Make copy of dest_ and src_ as they may be calls */
#define Nchappend(dest__, src__)	\
	do { \
		node_t *_dest_, *_src_; \
		_dest_ = dest__; \
		_src_ = src__; \
		if ((_dest_)->children != NULL) \
			Nappend((_dest_)->children, (_src_)); \
		else \
			(_dest_)->children = _src_; \
	} while(0)


/* Insert src node as dest node's first child
   Make copy of dest_ and src_ as they may be calls */
#define Nchinsert(dest_, src_)	\
	do { \
		node_t *_dest_, *_src_; \
		_dest_ = dest_; \
		_src_ = src_; \
		if ((_dest_)->children != NULL) {\
			if (_src_ != NULL) { \
				Nappend(_src_, (_dest_)->children); \
				(_dest_)->children = _src_; \
			} \
		} \
		else \
			(_dest_)->children = _src_; \
	} while(0)


#define NTYinsert(dest_, src_) \
	do { \
		node_t *_dest_, *_src_; \
		_dest_ = dest_; \
		_src_ = src_; \
		if ((_dest_->children == NULL) || (_dest_->children->type != T_TYPE)) {\
			node_t *n__; \
			n__ = Nalloc(T_TYPE, NULL, _dest_->children); \
			if (_src_ != NULL) \
				Nloc_cpy(n__, _src_); \
			_dest_->children = n__; \
		} \
		_src_->next = _dest_->children->children; \
		_dest_->children->children = _src_; \
	} while(0)

#ifdef PARSER_NALLOC_DEBUG
# ifndef YLINEDEF
#  define YLINEDEF -1
# endif
# ifndef LLINEDEF
#  define LLINEDEF -1
# endif
# define Nalloc(type, name, child)    Nalloc(type, name, child, YLINEDEF)
# define Nalloc_tok(type, tok, child) Nalloc_tok(type, tok, child, YLINEDEF)
# define Nclone(node, ivl)            Nclone(node, ivl, YLINEDEF)
# define Nop(op, o1, o2, s, e)        Nop(op, o1, o2, s, e, YLINEDEF)
# define Nop_tok(op, o1, o2)          Nop_tok(op, o1, o2, YLINEDEF)
# define Nalloc_wloc(type, name, child, start, end)    Nalloc_wloc(type, name, child, start, end, YLINEDEF)
# define tt_append(type, text)        tt_append(type, text, LLINEDEF)
#endif

/* postprocess the tree: fill in the parent field */
void tree_postproc(node_t *root);

#endif
