/* struct::tree - critcl - layer 1 declarations
 * (b) Node operations.
 */

#include <t.h>
#include <tn.h>
#include <util.h>

/* .................................................. */

static void extend_children  (TNPtr n);
static int  fill_descendants (TNPtr n, Tcl_Size lc, Tcl_Obj** lv, Tcl_Size at);

/* .................................................. */

TNPtr
tn_new (TPtr t, CONST char* name)
{
    TNPtr n = ALLOC (TN);
    int	  new;

    n->name = Tcl_NewStringObj(name, TCL_AUTO_LENGTH); /* OK tcl9 */
    Tcl_IncrRefCount (n->name);
    tn_shimmer (n->name, n);

    if (Tcl_FindHashEntry (&t->node, name) != NULL) {
	Tcl_Panic ("struct::tree(c) tn_new - tried to use duplicate name for new node");
    }

    n->he = Tcl_CreateHashEntry(&t->node, name, &new);
    Tcl_SetHashValue (n->he, (ClientData) n);

    n->tree     = t;
    n->nextleaf = NULL;
    n->prevleaf = NULL;
    n->nextnode = NULL;
    n->prevnode = NULL;

    tn_node (n);
    tn_leaf (n);

    n->parent	   = NULL;
    n->child	   = NULL;
    n->maxchildren = 0;
    n->nchildren   = 0;
    n->left	   = NULL;
    n->right	   = NULL;
    n->attr	   = NULL;

    n->index	   = -1;
    n->depth	   = -1;
    n->height	   = -1;
    n->desc	   = -1;

    return n;
}

void
tn_delete (TNPtr n)
{
    T* t = n->tree;

    /* We assume that the node either has no parent or siblings anymore,
     * or that their presence does not matter. The node may still have
     * children. They are deleted recursively. That is the situation
     * where the parent/sibling information does not matter anymore, and
     * can be ignored.
     */

    tn_notleaf (n);
    tn_notnode (n);

    Tcl_DecrRefCount	(n->name); n->name = NULL;
    Tcl_DeleteHashEntry (n->he);   n->he   = NULL;

    if (n->child) {
	Tcl_Size i;

	for (i = 0; i < n->nchildren; i++) {
	    ASSERT_BOUNDS (i, n->nchildren);

	    tn_delete (n->child [i]);
	    n->child [i] = NULL;
	}
	ckfree ((char*) n->child);

	n->child       = NULL;
	n->nchildren   = 0;
	n->maxchildren = 0;
    }

    if (n->attr) {
	Tcl_HashSearch	hs;
	Tcl_HashEntry*	he;

	for(he = Tcl_FirstHashEntry(n->attr, &hs);
	    he != NULL;
	    he = Tcl_NextHashEntry(&hs)) {
	    Tcl_DecrRefCount ((Tcl_Obj*) Tcl_GetHashValue(he));
	}
	Tcl_DeleteHashTable(n->attr);
	ckfree ((char*) n->attr);
	n->attr = NULL;
    }

    ckfree ((char*) n);
}

/* .................................................. */

void
tn_node (TNPtr n)
{
    TPtr  t	= n->tree;
    TNPtr first = t->nodes;

    t->nnodes ++;

    n->nextnode = first;
    n->prevnode = NULL;
    t->nodes	= n;

    if (!first) return;
    first->prevnode = n;
}

void
tn_notnode (TNPtr n)
{
    T* t = n->tree;

    if ((t->nodes == n) || n->prevnode || n->nextnode) {
	if (t->nodes == n) {
	    t->nodes = n->nextnode;
	}
	if (n->prevnode) {
	    n->prevnode->nextnode = n->nextnode;
	}
	if (n->nextnode) {
	    n->nextnode->prevnode = n->prevnode;
	}
	n->prevnode = NULL;
	n->nextnode = NULL;
	t->nnodes --;
    }
}

void
tn_leaf (TNPtr n)
{
    TPtr  t	= n->tree;
    TNPtr first = t->leaves;

    if ((t->leaves == n) || n->prevleaf || n->nextleaf) {
	/* The node is already a leaf */
	return;
    }

    /* Now make the non-leaf it a leaf */

    t->nleaves ++;

    n->nextleaf = first;
    n->prevleaf = NULL;
    t->leaves	= n;

    if (!first) return;
    first->prevleaf = n;
}

void
tn_notleaf (TNPtr n)
{
    T* t = n->tree;

    if ((t->leaves == n) || n->prevleaf || n->nextleaf) {
	if (t->leaves == n) {
	    t->leaves = n->nextleaf;
	}
	if (n->prevleaf) {
	    n->prevleaf->nextleaf = n->nextleaf;
	}
	if (n->nextleaf) {
	    n->nextleaf->prevleaf = n->prevleaf;
	}
	n->prevleaf = NULL;
	n->nextleaf = NULL;
	t->nleaves --;
    }
}

/* .................................................. */

void
tn_structure (TNPtr n, Tcl_Size depth)
{
    n->depth = depth;
    n->desc  = n->nchildren; /* #direct children */

    depth ++;

    if (n->nchildren) {
	Tcl_Size i, maxh, h;

	for (i = 0, maxh = -1;
	     i < n->nchildren;
	     i++) {
	    ASSERT_BOUNDS (i, n->nchildren);

	    tn_structure (n->child [i], depth);

	    h = n->child [i]->height;

	    if (h > maxh) {
		maxh = h;
	    }
	}

	n->height = maxh + 1;
    } else {
	n->height = 0;
    }

    /* Extend parent with our descendants. Do not count
     * ourselves. This is already done in the parent through
     * the 'direct children' clause above at the beginning
     * of the function.
     */

    if (n->parent) {
	n->parent->desc += n->desc;
    }
}

/* .................................................. */

void
tn_detach (TNPtr n)
{
    /* Detaches the node from the tree by removing it from its parent
     * node. The sibling relationships are squashed as well, and the
     * index information for all right siblings is adjusted. The node
     * does retain its children. After this function is called the node
     * and its children are ready for tn_delete'. Or reinsertion in a
     * different place.
     */

    TNPtr p = n->parent;

    if (p->nchildren == 1) {
	/* This node is the last node in its parent. We can release the
	 * whole array of children now, and declare the parent to be a
	 * leaf again. There is no need to touch the siblings references,
	 * we know that they are NULL.
	 */

	ckfree ((char*) p->child);
	p->child       = NULL;
	p->maxchildren = 0;
	p->nchildren   = 0;

	tn_leaf (p);

    } else {
	/* The node is somewhere in the array of children of its
	 * parent. We know the exact location, through 'index'. All
	 * siblings to the right are moved down one slot, and their index
	 * is adjusted in the same way. This is an O(n)
	 * operation. Afterward we adjust the left/right references of the
	 * node's siblings, if there are any, and reset the node's sibling
	 * references as well.
	 */

	Tcl_Size i;
	for (i = n->index; i < (p->nchildren-1); i++) {

	    ASSERT_BOUNDS (i,	p->nchildren);
	    ASSERT_BOUNDS (i+1, p->nchildren);

	    p->child [i] = p->child [i+1];
	    p->child [i]->index --;
	}
	p->nchildren --;
	/* Note regarding the decrement: As the node was _not_ the last
	 * child we know that the condition 'nchildren > 0' still holds, and
	 * that there is no need to free the 'child' array.
	 */

	if (n->left) {
	    n->left->right = n->right;
	}
	if (n->right) {
	    n->right->left = n->left;
	}

	n->left	  = NULL;
	n->right  = NULL;
    }

    n->parent = NULL;
    n->tree->structure = 0;
}

TNPtr*
tn_detachmany (TNPtr n, Tcl_Size len)
{
    /* Detaches the node n and its 'len -1' right siblings from the tree
     * by removing them from their parent node. In toto 'len' nodes are
     * removed. The sibling relationships are squashed as well, and the
     * index information for all right siblings is adjusted. The nodes
     * retain their children. After this function is called thes node
     * and their children are ready for tn_delete'. Or reinsertion in a
     * different place.
     *
     * The operation fails with a panic if there are less children we
     * can cut than requested. It also panics when trying to cut
     * nothing.
     *
     * Note: This function does not reset the parent reference in the
     * cut nodes.
     */

    TNPtr* ch;
    TNPtr  p   = n->parent;
    int	   at  = n->index;
    int	   end = at + len;

    ASSERT (end <= p->nchildren, "tn_detachmany - tried to cut too many children");
    ASSERT (len > 0,		 "tn_detachmany - tried to cut nothing");

    if ((at == 0) && (end == p->nchildren)) {
	/* All children are taken. There is no need to copy anything, we
	 * can use the whole array, and reset the other information in the
	 * parent. Note that the condition above implies 'at == 0'. The
	 * parent node becomes a leaf again.
	 */
    
	ch = p->child;

	p->child       = NULL;
	p->maxchildren = 0;
	p->nchildren   = 0;

	tn_leaf (p);

    } else {
	/* Copies the cut nodes into a result array. Shifts the right
	 * siblings down, if there are any.
	 */

	Tcl_Size i, k;

	ch = NALLOC (len, TNPtr);

	/* Examples. We always have nchildren = 10.
	 *
	 * _______________________________
	 * at  = 2, len = 3.
	 * 
	 * 01 234 56789 i = 0, k = 2
	 *	  012	    i = 3, k = 5
	 *
	 * 01 234 56789 i = 2, k = 5
	 * 01 567 89    i = 7, k = 10
	 *
	 * _______________________________
	 * at  = 7, len = 3.
	 * 
	 * 0123456 789 i = 0, k = 7
	 *	       012 i = 3, k = 10
	 *
	 * 0123456 789 i = 7, k = 10
	 * 0123456	   nothing
	 *
	 * _______________________________
	 * at  = 6, len = 3.
	 * 
	 * 012345 678 9 i = 0, k = 6
	 *	      012   i = 3, k = 9
	 *
	 * 012345 678 9 i = 6, k = 9
	 * 012345 9	    i = 7, k = 10
	 *
	 * _______________________________
	 * at  = 6, len = 1.
	 * 
	 * 012345 6 789 i = 0, k = 6
	 *	      0	    i = 1, k = 7
	 *
	 * 012345 6 789 i = 6, k = 7
	 * 012345 7 89  i = 9, k = 10
	 */

	for (i = 0, k = at; i < len; i++, k++) {

	    ASSERT_BOUNDS (k, p->nchildren);
	    ASSERT_BOUNDS (i, len);

	    ch [i] = p->child [k];
	}

	for (i = at, k = end; k < p->nchildren; i++, k++) {

	    ASSERT_BOUNDS (k, p->nchildren);
	    ASSERT_BOUNDS (i, p->nchildren);

	    p->child [i] = p->child [k];
	    p->child [i]->index -= len;
	}

	p->nchildren -= len;

	if (ch [0]->left) {
	    ch [0]->left->right = ch [len-1]->right;
	}
	if (ch [len-1]->right) {
	    ch [len-1]->right->left = ch [0]->left;
	}

	ch [0]->left	  = NULL;
	ch [len-1]->right = NULL;
    }

    n->tree->structure = 0;
    return ch;
}

TNPtr*
tn_detachchildren (TNPtr n, Tcl_Size* nc)
{
    TNPtr* children = n->child;

    *nc = n->nchildren;

    n->child	   = NULL;
    n->maxchildren = 0;
    n->nchildren   = 0;

    tn_leaf (n);
    return children;
}

/* .................................................. */

void
tn_append (TNPtr p, TNPtr n)
{
    /* Appending is O(1) */

    /* The node chosen as parent cannot be a leaf (anymore) */

    int at = p->nchildren;

    tn_notleaf (p); 

    /* Allocate/Extend child array as needed */

    p->nchildren ++;
    extend_children (p);

    /* Link the node into the parent and to its left sibling, if
     * any. This overwrites any existing relationships. Make sure
     * that the node n is either new or was cut before.
     */

    ASSERT_BOUNDS (at, p->nchildren);

    p->child [at] = n;

    n->parent = p;
    n->index  = at;
    n->right  = NULL;

    if (at > 0) {
	TNPtr sib;

	ASSERT_BOUNDS (at-1, p->nchildren);

	sib = p->child [at-1];
	n->left	   = sib;
	sib->right = n;
    }

    p->tree->structure = 0;
}

void
tn_appendmany (TNPtr p, Tcl_Size nc, TNPtr* nv)
{
    /* Appending is O(1) */
    /* The node chosen as parent cannot be a leaf (anymore) */

    Tcl_Size i, at = p->nchildren;

    tn_notleaf (p); 

    /* Allocate/Extend child array as needed */

    p->nchildren += nc;
    extend_children (p);

    /* Link the nodes into the parent and to their left siblings, if
     * any. This overwrites any existing relationships. Make sure that
     * the nodes are either new or were cut before.
     */

    for (i = 0; i < nc; i++, at++) {

	ASSERT_BOUNDS (at, p->nchildren);
	ASSERT_BOUNDS (i, nc);

	p->child [at] = nv [i];

	nv [i]->parent = p;
	nv [i]->index  = at;
	nv [i]->right  = NULL;

	if (at > 0) {
	    TNPtr sib;

	    ASSERT_BOUNDS (at, p->nchildren);

	    sib = p->child [at-1];
	    nv [i]->left = sib;
	    sib->right	 = nv [i];
	}
    }

    p->tree->structure = 0;
}

/* .................................................. */

void
tn_insert (TNPtr p, Tcl_Size at, TNPtr n)
{
    Tcl_Size i, k;

    if (at >= p->nchildren) {
	tn_append (p, n);
	return;
    }

    /* Insertion at beginning, or somewhere in the middle */

    if (at < 0) {
	at = 0;
    }

    /* The node chosen as parent cannot be a leaf */
    /* anymore */

    tn_notleaf (p); 

    /* Allocate/Extend child array as needed */

    p->nchildren ++;
    extend_children (p);

    /* Link the node into the parent and to its left and right siblings,
     * if any. This overwrites any existing relationships. Make sure
     * that the node n is either new or was cut before.
     *
     * Shift all nodes at 'at' and to the right one slot up.
     * Note that 'nchildren' is incremented already.
     */

    for (i = p->nchildren-1, k = i-1; i > at; i--, k--) {

	ASSERT_BOUNDS (i, p->nchildren);
	ASSERT_BOUNDS (k, p->nchildren);

	p->child [i] = p->child [k];
	p->child [i]->index ++;
    }

    p->child [at] = n;

    n->parent = p;
    n->index  = at;

    /* We have to have a right sibling, otherwise it would have been
     * appending. We may have a left sibling.
     */

    ASSERT_BOUNDS (at+1, p->nchildren);

    n->right = p->child [at+1];
    p->child [at+1]->left = n;

    if (at == 0) {
	n->left = NULL;
    } else {
	ASSERT_BOUNDS (at-1, p->nchildren);

	n->left	       = p->child [at-1];
	p->child [at-1]->right = n;
    }

    p->tree->structure = 0;
}

void
tn_insertmany (TNPtr p, Tcl_Size at, Tcl_Size nc, TNPtr* nv)
{
    Tcl_Size i, k;

    if (at >= p->nchildren) {
	tn_appendmany (p, nc, nv);
	return;
    }

    if (at < 0) {
	at = 0;
    }

    /* The node chosen as parent cannot be a leaf */
    /* anymore */

    tn_notleaf (p); 

    /* Allocate/Extend child array as needed */

    p->nchildren += nc;
    extend_children (p);

    /* Link the node into the parent and to its left and right siblings,
     * if any. This overwrites any existing relationships. Make sure
     * that the node n is either new or was cut before.
     *
     * Shift all nodes at 'at' and to the right one slot up.
     * Note that 'nchildren' is incremented already.
     */

    for (i = p->nchildren-1, k = i-nc; k >= at; i--, k--) {

	ASSERT_BOUNDS (i, p->nchildren);
	ASSERT_BOUNDS (k, p->nchildren);

	p->child [i] = p->child [k];
	p->child [i]->index += nc;
    }

    for (i = 0, k = at; i < nc; i++, k++) {

	ASSERT_BOUNDS (i, nc);
	ASSERT_BOUNDS (k, p->nchildren);

	nv [i]->parent = p;
	nv [i]->index  = k;
	p->child [k]   = nv [i];
    }

    for (i = 0, k = at; i < nc; i++, k++) {
	if (k > 0) {
	    ASSERT_BOUNDS (k,	p->nchildren);
	    ASSERT_BOUNDS (k-1, p->nchildren);

	    p->child [k]->left	  = p->child [k-1];
	    p->child [k-1]->right = p->child [k];
	}

	if (k < (p->nchildren-1)) {
	    ASSERT_BOUNDS (k,	p->nchildren);
	    ASSERT_BOUNDS (k+1, p->nchildren);

	    p->child [k]->right	 = p->child [k+1];
	    p->child [k+1]->left = p->child [k];
	}
    }

    p->tree->structure = 0;
}

/* .................................................. */

void
tn_cut (TNPtr n)
{
    TNPtr p  = n->parent; /* Remember the location of n in its */
    int at = n->index;	/* parent, this is the point there its
			 * children are re-inserted */
    Tcl_Size nc;
    TNPtr*   nv;

    nv = tn_detachchildren (n, &nc);
    tn_detach (n);

    tn_insertmany (p, at, nc, nv);
    ckfree ((char*) nv);

    tn_delete (n);
}

TNPtr
tn_dup (TPtr dst, TNPtr src)
{
    TNPtr dstn;

    dstn = tn_new (dst, Tcl_GetString (src->name));

    if (src->attr) {
	int i, new;
	Tcl_HashSearch hs;
	Tcl_HashEntry* he;
	Tcl_HashEntry* dhe;
	CONST char*    key;
	Tcl_Obj*       val;

	dstn->attr = ALLOC (Tcl_HashTable);
	Tcl_InitHashTable(dstn->attr, TCL_STRING_KEYS);

	for(i = 0, he = Tcl_FirstHashEntry(src->attr, &hs);
	    he != NULL;
	    he = Tcl_NextHashEntry(&hs), i++) {

	    key = Tcl_GetHashKey (src->attr, he);
	    val = (Tcl_Obj*) Tcl_GetHashValue(he);

	    dhe = Tcl_CreateHashEntry(dstn->attr, key, &new);

	    Tcl_IncrRefCount (val);
	    Tcl_SetHashValue (dhe, (ClientData) val);
	}
    }

    if (src->nchildren) {
	Tcl_Size i;

	dstn->child	  = NALLOC (src->nchildren, TNPtr);
	dstn->maxchildren = src->nchildren;
	dstn->nchildren	  = 0;

	for (i = 0; i < src->nchildren; i++) {

	    ASSERT_BOUNDS (i, src->nchildren);

	    tn_append (dstn,
		       tn_dup (dst, src->child [i]));
	}
    }

    return dstn;
}

/* .................................................. */

void
tn_set_attr (TNPtr n, Tcl_Interp* interp, Tcl_Obj* dict)
{
    Tcl_HashEntry* he;
    CONST char*	   key;
    Tcl_Obj*	   val;
    int		   new;
    Tcl_Size	   listc, i;
    Tcl_Obj**	   listv;

    if (Tcl_ListObjGetElements (interp, dict, &listc, &listv) != TCL_OK) { /* OK tcl9 */
	Tcl_Panic ("Malformed nodes attributes, snuck through validation of serialization.");
    }

    if (!listc) {
	return;
    }

    tn_extend_attr (n);

    for (i = 0; i < listc; i+= 2) {

	ASSERT_BOUNDS (i,   listc);
	ASSERT_BOUNDS (i+1, listc);

	key = Tcl_GetString (listv [i]);
	val = listv [i+1];

	he = Tcl_CreateHashEntry(n->attr, key, &new);

	Tcl_IncrRefCount (val);
	Tcl_SetHashValue (he, (ClientData) val);
    }
}

/* .................................................. */

void
tn_extend_attr (TNPtr n)
{
    if (n->attr != NULL) {
	return;
    }

    n->attr = ALLOC (Tcl_HashTable);
    Tcl_InitHashTable(n->attr, TCL_STRING_KEYS);
}

/* .................................................. */

Tcl_Size
tn_depth (TNPtr n)
{
    if (!n->tree->structure) {
	t_structure (n->tree);
    }
    return n->depth;
}

Tcl_Size
tn_height (TNPtr n)
{
    if (!n->tree->structure) {
	t_structure (n->tree);
    }
    return n->height;
}

Tcl_Size
tn_ndescendants (TNPtr n)
{
    if (n == n->tree->root) {
	/* For the root we do not need to know the structure data of the
	 * tree to determine the number of descendants. It is the number
	 * of nodes minus one, i.e. all nodes except the root.
	 */

	return n->tree->nnodes - 1;

    } else if (!n->nchildren) {
	/* If the node has no direct children we know there are no
	 * descendants as well
	 */

	return 0;

    } else if (!n->tree->structure) {
	t_structure (n->tree);
    }

    return n->desc;
}

Tcl_Obj**
tn_getdescendants (TNPtr n, Tcl_Size* nc)
{
    int	      end;
    Tcl_Size  lc = tn_ndescendants (n);
    Tcl_Obj** lv;

    *nc = lc;

    if (lc == 0) {
	return NULL;
    }

    lv	= NALLOC (lc, Tcl_Obj*);
    end = fill_descendants (n, lc, lv, 0);

    ASSERT (end == lc, "Bad list of descendants");
    return lv;
}

Tcl_Obj**
tn_getchildren (TNPtr n, Tcl_Size* nc)
{
    if (!n->nchildren) {
	*nc = 0;
	return NULL;
    } else {
	Tcl_Size  i;
	Tcl_Obj** lv;

	*nc = n->nchildren;
	lv  = NALLOC (n->nchildren, Tcl_Obj*);

	for (i = 0; i < n->nchildren; i++) {

	    ASSERT_BOUNDS (i, n->nchildren);

	    lv [i] = n->child [i]->name;
	}

	return lv;
    }
}

int
tn_filternodes (Tcl_Size* nc,   Tcl_Obj** nv,
		Tcl_Size  cmdc, Tcl_Obj** cmdv,
		Tcl_Obj*  tree, Tcl_Interp* interp)
{
    Tcl_Size  ec, i;
    Tcl_Obj** ev;

    if (cmdc && (*nc > 0)) {
	/* Run the filter command over all nodes in the list.
	 * Keep only the nodes passing the filter in the list.
	 */

	Tcl_Size lc = *nc;
	Tcl_Size src, dst;
	int      res, flag;

	/* Set up the command vector for the callback.
	 * Two placeholders for tree and node arguments.
	 */

	ec = cmdc + 2;
	ev = NALLOC (ec, Tcl_Obj*);

	for (i = 0; i < cmdc; i++) {
	    ASSERT_BOUNDS (i, ec);

	    ev [i] = cmdv [i];
	    Tcl_IncrRefCount (ev [i]);
	}
	ASSERT_BOUNDS (cmdc, ec);

	ev [cmdc] = tree; /* Tree */
	Tcl_IncrRefCount (ev [cmdc]);

	/* Run the callback on each element of the list */

	for (src = 0, dst = 0;
	     src < lc;
	     src++) {

	    /* Fill the placeholders */

	    ASSERT_BOUNDS (cmdc+1, ec);
	    ASSERT_BOUNDS (src, lc);

	    ev [cmdc+1] = nv [src]; /* Node */

	    /* Run the callback */

	    Tcl_IncrRefCount (ev [cmdc+1]);

	    res = Tcl_EvalObjv (interp, ec, ev, 0); /* OK tcl9 */

	    Tcl_DecrRefCount (ev [cmdc+1]);

	    /* Process the result */

	    if (res != TCL_OK) {
		goto abort;
	    }

	    if (Tcl_GetBooleanFromObj (interp,
				       Tcl_GetObjResult (interp),
				       &flag) != TCL_OK) {
		goto abort;
	    }

	    /* Result is valid, use this decide retain/write over */

	    if (!flag)
		continue;

	    ASSERT_BOUNDS (dst, lc);
	    ASSERT_BOUNDS (src, lc);

	    nv [dst] = nv [src];
	    dst++;
	}

	/* Cleanup state */

	Tcl_ResetResult (interp);

	for (i = 0; i < cmdc; i++) {
	    ASSERT_BOUNDS (i, ec);
	    Tcl_DecrRefCount (ev [i]);
	}
	ASSERT_BOUNDS (cmdc, ec);
	Tcl_DecrRefCount (ev [cmdc]); /* Tree */

	ckfree ((char*) ev);

	*nc = dst;
    }

    return TCL_OK;

 abort:
    /* We do not reset the interp result. It either contains
     * the non-boolean result, or the error message
     */

    for (i = 0; i < cmdc; i++) {
	ASSERT_BOUNDS (i, ec);
	Tcl_DecrRefCount (ev [i]);
    }
    ASSERT_BOUNDS (cmdc, ec);
    Tcl_DecrRefCount (ev [cmdc]); /* Tree */

    ckfree ((char*) ev);
    return TCL_ERROR;
}

/* .................................................. */

int
tn_isancestorof (TNPtr na, TNPtr nb)
{
    /* True <=> a is ancestor of b */

    for (nb = nb->parent; nb != NULL; ) {
	if (na == nb) {
	    return 1;
	}
	nb = nb->parent;
    }

    return 0;
}

/* .................................................. */

Tcl_Obj*
tn_get_attr (TNPtr tdn, Tcl_Obj* empty)
{
    Tcl_Obj*	   res;
    Tcl_Size	   listc, i;
    Tcl_Obj**	   listv;
    Tcl_HashSearch hs;
    Tcl_HashEntry* he;
    CONST char*	   key;

    if ((tdn->attr == NULL) || (tdn->attr->numEntries == 0)) {
	return empty;
    }

    listc = 2 * tdn->attr->numEntries;
    listv = NALLOC (listc, Tcl_Obj*);

    for(i = 0, he = Tcl_FirstHashEntry(tdn->attr, &hs);
	he != NULL;
	he = Tcl_NextHashEntry(&hs)) {

	key = Tcl_GetHashKey (tdn->attr, he);

	ASSERT_BOUNDS (i,   listc);
	ASSERT_BOUNDS (i+1, listc);

	listv [i] = Tcl_NewStringObj (key, TCL_AUTO_LENGTH); /* OK tcl9 */ i++;
	listv [i] = (Tcl_Obj*) Tcl_GetHashValue(he); i++;
    }

    res = Tcl_NewListObj (listc, listv); /* OK tcl9 */
    ckfree ((char*) listv);
    return res;
}

int
tn_serialize (TNPtr tdn, Tcl_Size listc, Tcl_Obj** listv, Tcl_Size at, Tcl_Size parent, Tcl_Obj* empty)
{
    Tcl_Size self = at;

    ASSERT_BOUNDS (at+0, listc);
    ASSERT_BOUNDS (at+1, listc);
    ASSERT_BOUNDS (at+2, listc);

    listv [at++] = tdn->name;
    listv [at++] = (parent < 0 ? empty : Tcl_NewSizeIntObj (parent)); /* OK tcl9 */
    listv [at++] = tn_get_attr (tdn, empty);

    if (tdn->nchildren) {
	Tcl_Size i;
	for (i = 0; i < tdn->nchildren; i++) {
	    at = tn_serialize (tdn->child [i], listc, listv, at, self, empty);
	}
    }

    return at;
}

/* .................................................. */
static int
fill_descendants (TNPtr n, Tcl_Size lc, Tcl_Obj** lv, Tcl_Size at)
{
    /* The descendants of the root are simply all nodes except the root
     * itself. That is easy to retrieve.
     */

    if (n == n->tree->root) {
	TNPtr iter;

	for (iter = n->tree->nodes;
	     iter != NULL;
	     iter = iter->nextnode) {

	    /* Skip the root node, it is not a descendant! */
	    if (iter == n) continue;

	    ASSERT_BOUNDS (at, lc);

	    lv [at] = iter->name;
	    at++;
	}
    } else if (n->child) {
	Tcl_Size i;
	TNPtr    c;

	for (i = 0; i < n->nchildren; i++) {
	    c = n->child [i];

	    ASSERT_BOUNDS (at, lc);
	    ASSERT_BOUNDS (i,  n->nchildren);

	    lv [at] = c->name;
	    at++;

	    at = fill_descendants (c, lc, lv, at);
	}
    }

    return at;
}

static void
extend_children (TNPtr n)
{
    if (n->nchildren > n->maxchildren) {
	if (n->child == NULL) {
	    n->child = NALLOC (n->nchildren, TNPtr);
	} else {
	    int	   nc  = 2 * n->nchildren;
	    TNPtr* new = (TNPtr*) attemptckrealloc ((char*) n->child,
						    nc * sizeof (TNPtr));
	    if (new == NULL) {
		nc  = n->nchildren;
		new = (TNPtr*) ckrealloc ((char*) n->child, nc * sizeof (TNPtr));
	    }
	    n->child	   = new;
	    n->maxchildren = nc;
	}
    }
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
