/*
* Copyright (c) 2000 - 2001 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/

#include <nw_tinytree.h>
#include "cxml_vector.h"

NW_TinyTree_t*
NW_TinyTree_new(){
  NW_TinyTree_t* tree = (NW_TinyTree_t*) NW_Mem_Malloc (sizeof(NW_TinyTree_t));
  if (tree != NULL){
    tree->tree = NULL;
    tree->root_index = 0;
    tree->ebuffer = NULL;
    tree->context = NULL;
  }
  return tree;
}

NW_Status_t
NW_TinyTree_construct(NW_TinyTree_t *tree,
                      CXML_Vector_Metric_t initialNodeCount,
                      void *buffer,
                      NW_TinyTree_Offset_t buffsz,
                      void *context,
                      NW_Bool freeBuff){

  NW_ASSERT(tree != NULL);

  tree->tree =
  NW_TinyTree_TreeVector_Construct (sizeof(NW_TinyTree_Node_t),
                              initialNodeCount,
                              tree);
  if(tree->tree == NULL) {
    return NW_STAT_OUT_OF_MEMORY;
  }

  /* Make the supplied buffer the first ebuffer segment */
  tree->ebuffer = NW_TinyTree_EBuffer_Construct((NW_Uint8*) buffer, 
                                          buffsz, 
                                          NW_TINY_TREE_BLOCK_SIZE_DEFAULT,
                                          freeBuff);
  if(tree->ebuffer == NULL){
    NW_TinyTree_TreeVector_Destruct(tree->tree);
    tree->tree = NULL;
    return NW_STAT_OUT_OF_MEMORY;
  }

  /* Init the rest of the tree */
  tree->root_index = 0;
  tree->context = context;
  return NW_STAT_SUCCESS;
}

void
NW_TinyTree_destruct(NW_TinyTree_t *tree)
{
  if (tree != NULL){
    if (tree->tree != NULL) {
      NW_TinyTree_TreeVector_Destruct (tree->tree);
    }
    if (tree->ebuffer != NULL){
      NW_TinyTree_EBuffer_Destruct (tree->ebuffer);
    }
    tree->tree = NULL;
    tree->root_index = 0;
    tree->ebuffer = NULL;
    tree->context = NULL;
  }
}

static 
NW_TinyTree_Node_t*
NW_TinyTree_appendNodeAt(NW_TinyTree_t *tree, 
                         NW_TinyTree_Node_t *node, 
                         NW_TinyTree_Index_t index){
  NW_TinyTree_TreeNode_t* sentinel;
	NW_TinyTree_Node_t* retNode = NULL;


  NW_ASSERT(tree != NULL);
  NW_ASSERT(tree->tree != NULL);
  NW_ASSERT(node != NULL);

	sentinel = (NW_TinyTree_TreeNode_t*) NW_Mem_Malloc(sizeof(NW_TinyTree_TreeNode_t));
	if(sentinel == NULL){
		return NULL;
	}
  sentinel->flags = TNODE_FLAG_TREE;
  sentinel->tree = tree;

  /* Add the new element at the specified index */
  retNode = (NW_TinyTree_Node_t*)
         CXML_Vector_InsertAt(tree->tree->vector, (void*)node, index, sentinel);
           NW_Mem_Free(sentinel);
	return retNode;
}

/* Add a node, appending storage to the tree vector.
 * The appended node is created as an orphan and needs to be attached
 * to the tree somewhere.
 */

static
NW_TinyTree_Node_t*
NW_TinyTree_appendNode(NW_TinyTree_t *tree, 
                       NW_TinyTree_Node_t *node){

  NW_ASSERT(tree != NULL);
  NW_ASSERT(node != NULL);

  /* Add the new element */

  return NW_TinyTree_appendNodeAt(tree, node, CXML_Vector_AtEnd);
}


/* Get the node at a given index */

static
NW_TinyTree_Node_t*
NW_TinyTree_getNode(NW_TinyTree_t *tree, 
                    NW_TinyTree_Index_t index){

  NW_ASSERT(tree != NULL);
  NW_ASSERT(tree->tree != NULL);
  NW_ASSERT(tree->tree->vector != NULL);
  NW_ASSERT(index > 0);
  
  return (NW_TinyTree_Node_t*)
    CXML_Vector_ElementAt(tree->tree->vector, (CXML_Vector_Metric_t)index);
}

/*
* Create an unattached node which references the source buffer at offset.
*/

NW_TinyTree_Node_t*
NW_TinyTree_createNode(NW_TinyTree_t* tree, 
                       NW_TinyTree_Offset_t offset){
  
  NW_TinyTree_Node_t node;

  NW_ASSERT(tree != NULL);
  NW_ASSERT(tree->tree != NULL);
  /* The root must have been set before creating any new nodes */
  NW_ASSERT(tree->root_index == 1);

  /* Initialize a node on the stack */
  node.source_offset = offset;
  node.flags = 0;
  node.first_child = 0;
  node.next_sibling = 0; 
  node.tree = tree;
  node.token = 0;
  /* Copy this into the tree */
  return NW_TinyTree_appendNode(tree, &node);
}

/* 
* Create a root node which references the source buffer at offset. 
* Returns a pointer to the root node.
*/

NW_TinyTree_Node_t*
NW_TinyTree_setRoot(NW_TinyTree_t *tree, 
                    NW_TinyTree_Offset_t offset){
  
  NW_TinyTree_Node_t root_node;
  
  NW_ASSERT(tree != NULL);
  NW_ASSERT(tree->tree != NULL);
  /* The root must not have set been already */
  NW_ASSERT(tree->root_index == 0);

  tree->root_index = 1;
  root_node.source_offset = offset;
  root_node.flags = TNODE_FLAG_ROOT;
  root_node.first_child = 0;
  root_node.next_sibling = 0;
  root_node.tree = tree;
  root_node.token = 0;
  /* TODO: make this a dummy element! */
  if (NW_TinyTree_appendNodeAt(tree, &root_node, 0) == NULL) {
    return NULL;
  }

  return NW_TinyTree_appendNodeAt(tree, &root_node, tree->root_index);
}

/*
* Get the root node of the tree.
*/

NW_TinyTree_Node_t*
NW_TinyTree_getRoot(NW_TinyTree_t *tree){

  NW_ASSERT(tree != NULL);
  /* Return NULL if not set */
  if(tree->root_index == 0)
    return NULL;
  return NW_TinyTree_getNode(tree, tree->root_index);
}

NW_TinyTree_Node_t*
NW_TinyTree_findNextSibling(NW_TinyTree_Node_t *node){

  NW_ASSERT(node != NULL);

  if (((node->flags & TNODE_FLAG_LASTSIBLING) == TNODE_FLAG_LASTSIBLING)
      || (node->next_sibling == 0)) {
    return NULL;
  }
  
  return node->next_sibling;
}

/* Find last sibling address */

NW_TinyTree_Node_t*
NW_TinyTree_findLastSibling(NW_TinyTree_Node_t *node)
{
  NW_TinyTree_Node_t* sibling = node;

  NW_ASSERT(node != NULL);

  while (((sibling->flags & TNODE_FLAG_LASTSIBLING) != TNODE_FLAG_LASTSIBLING)
         && (sibling->next_sibling != 0)) {
    sibling = sibling->next_sibling;
  }

  /* Because the array always starts with the tree node, no node ever has index 0 */
  if(sibling == node)
    return NULL;
  
  return sibling;
}

NW_TinyTree_Node_t*
NW_TinyTree_findFirstChild(NW_TinyTree_Node_t *node)
{
  NW_ASSERT(node != NULL);
  return node->first_child;
}


/*
* Find a node's last child
*/

NW_TinyTree_Node_t*
NW_TinyTree_findLastChild(NW_TinyTree_Node_t* node){
  
  NW_TinyTree_Node_t* first;
  NW_TinyTree_Node_t* last;

  NW_ASSERT(node != NULL);

  first = NW_TinyTree_findFirstChild(node);

  if(first == NULL){
    return NULL; /* No children */
  }

  last = NW_TinyTree_findLastSibling(first);

  if(last == NULL){ /* No siblings, only child */
    return first;
  }

  return last;
}

/* 
* Get a node's parent
*/

NW_TinyTree_Node_t* 
NW_TinyTree_findParent(NW_TinyTree_Node_t *node)
{
 
  NW_TinyTree_Node_t *last_sibling;

  NW_ASSERT(node != NULL);

  /* Is this the root node ? */
  if((node->flags & TNODE_FLAG_ROOT) == TNODE_FLAG_ROOT)
    return NULL;
  
  /* Is next sibling zero ? */
  if (node->next_sibling == 0)
    return NULL;

  /* The next sibling index of the last sibling points back to parent */

  last_sibling = NW_TinyTree_findLastSibling(node);

  if (last_sibling == NULL){
    last_sibling = node;  /* No siblings */
  }

  return last_sibling->next_sibling;
}


/*
* Attach a node as a sibling after another node.
*/

NW_Status_t 
NW_TinyTree_attachAfter(NW_TinyTree_Node_t *node, 
                        NW_TinyTree_Node_t *sibling)
{
  NW_ASSERT(node != NULL);
  NW_ASSERT(sibling != NULL);

  if((node->flags & TNODE_FLAG_LASTSIBLING) == TNODE_FLAG_LASTSIBLING){
    node->flags &= ~TNODE_FLAG_LASTSIBLING; 
    sibling->flags |= TNODE_FLAG_LASTSIBLING;
  }
  
  sibling->next_sibling = node->next_sibling;
  node->next_sibling = sibling;
  return NW_STAT_SUCCESS;
}

NW_TinyTree_Node_t*
NW_TinyTree_findPreviousSibling(NW_TinyTree_Node_t *node){

  NW_TinyTree_Node_t *sibling;
  NW_TinyTree_Node_t *parent; 

  NW_ASSERT(node != NULL);

  /* First find the parent */
  parent = NW_TinyTree_findParent(node);

  if(parent == NULL)
    return NULL;

  /* Then get first child */
  sibling = NW_TinyTree_findFirstChild(parent);
  NW_ASSERT(sibling != NULL);
  if(sibling == node)
    return NULL; /* Only child */

  /* Find a sibling whose next_sibling points to me */
  while(sibling->next_sibling != node){ 
    NW_ASSERT(sibling->next_sibling != NULL);
    sibling = sibling->next_sibling;
  }
  return sibling;
}

/*
* Attach a node as a sibling before another node.
*/

NW_Status_t 
NW_TinyTree_attachBefore(NW_TinyTree_Node_t *node, 
                         NW_TinyTree_Node_t *sibling)
{
  NW_TinyTree_Node_t *previous = NW_TinyTree_findPreviousSibling(node);

  NW_ASSERT(node != NULL);
  NW_ASSERT(sibling != NULL);

  /* Has a previous sibling, insert after */
  if(previous != NULL){
    NW_TinyTree_attachAfter(previous, sibling);
    return NW_STAT_SUCCESS;
  }

  /*Otherwise, insert as first child */
  previous = NW_TinyTree_findParent(node);
  if(previous == NULL)
     return NW_STAT_FAILURE; /* TODO: return a more descriptive error status */

  previous->first_child = sibling;
  /*lint -e{794} Conceivable use of null pointer */
  sibling->next_sibling = node;
  
  return NW_STAT_SUCCESS;
}

/*
* Attach a child (as last child) to a node 
*/

NW_Status_t
NW_TinyTree_attachChild(NW_TinyTree_Node_t *node, 
                        NW_TinyTree_Node_t *child)
{
  NW_TinyTree_Node_t *last_child = NW_TinyTree_findLastChild(node);
  
  NW_ASSERT(node != NULL);
  NW_ASSERT(child != NULL);

  /* If there are no children, attach as first child */
  if(last_child == NULL){
    node->first_child = child;
    /* Point child's next_sibling back to parent and set LASTSIBLING flag */
    child->next_sibling = node;
    child->flags |= TNODE_FLAG_LASTSIBLING;
    return NW_STAT_SUCCESS;
  }
  /* Else attach as sibling to last child */
  else{
    return NW_TinyTree_attachAfter(last_child, child);
  }
}

NW_Status_t
NW_TinyTree_deleteNode(NW_TinyTree_Node_t *node)
{
  NW_ASSERT(node != NULL);

  /* If not the root node, then adjust parent or sibling indexes */
  if((node->flags & TNODE_FLAG_ROOT)!= TNODE_FLAG_ROOT){
    NW_TinyTree_Node_t *previous_sibling;
    previous_sibling = NW_TinyTree_findPreviousSibling(node);
    /* If we are first child, modify parent's first_child index */
    if(previous_sibling == NULL){
      NW_TinyTree_Node_t *parent = NW_TinyTree_findParent(node);
      NW_ASSERT(parent != NULL);
      /* If also last child (i.e. only child) */
      if((node->flags & TNODE_FLAG_LASTSIBLING) == TNODE_FLAG_LASTSIBLING){
        /* Zero the parent's child index */
        parent->first_child = 0;
      }
      /* Not only child */
      else{
        /* Move parent child offset to my next sibling */
        parent->first_child = node->next_sibling;
      }
    }
    /* Not the first child */
    else {
      /* Adjust previous sibling next_sibling index */
      previous_sibling->next_sibling = node->next_sibling;
      if((node->flags & TNODE_FLAG_LASTSIBLING) == TNODE_FLAG_LASTSIBLING){
        previous_sibling->flags |= TNODE_FLAG_LASTSIBLING;
      }
    }
  }
  
 /* Note that we don't actually remove the node from the tree vector
  * since this may cause other nodes to be moved, invalidating any
  * references to them.
  */

  return NW_STAT_SUCCESS;
}

NW_Status_t
NW_TinyTree_setContext(NW_TinyTree_t *tree, 
                       void *context){
  NW_ASSERT(tree != NULL);
  tree->context = context;
  return NW_STAT_SUCCESS;
}

void*
NW_TinyTree_getContext(NW_TinyTree_t *tree){
  NW_ASSERT(tree != NULL);
  return tree->context;
}

NW_Uint16 
NW_TinyTree_Node_getFlags(NW_TinyTree_Node_t *node){
  NW_ASSERT(node != NULL);
  return node->flags;
}

NW_Status_t
NW_TinyTree_Node_setUserFlags(NW_TinyTree_Node_t *node, 
                              NW_Uint16 flags){
  NW_ASSERT(node != NULL);
  node->flags &= ~TNODE_USR_FLAGS; /*Zero user flags */
  node->flags |= (flags & TNODE_USR_FLAGS);
  return NW_STAT_SUCCESS;
}

NW_TinyTree_Offset_t
NW_TinyTree_Node_getSourceOffset(NW_TinyTree_Node_t *node){
  NW_ASSERT(node != NULL);

  return node->source_offset;
}

void *
NW_TinyTree_Node_getSourceAddress(NW_TinyTree_t *tree, 
                                  NW_TinyTree_Node_t* node){

  NW_ASSERT(node != NULL);
  NW_ASSERT(tree != NULL);
 
  /*lint -e{794} Conceivable use of null pointer */
  return (void *)(NW_TinyTree_EBuffer_AddressAt(tree->ebuffer, 
						node->source_offset));
}


NW_Status_t
NW_TinyTree_Node_GetSegmentAndOffset(NW_TinyTree_t* tree,
                                     NW_TinyTree_Node_t* node,
                                     NW_Uint8** segment,             
                                     NW_TinyTree_Offset_t* segSize, 
                                     NW_TinyTree_Offset_t* offset) {
  NW_ASSERT(tree != NULL);
  NW_ASSERT(node != NULL);
  
  return NW_TinyTree_EBuffer_GetSegmentAndOffset(tree->ebuffer, 
                                                 node->source_offset,
                                                 segment,
                                                 segSize,
                                                 offset);
}

NW_Status_t
NW_TinyTree_GetSourceOffset(NW_TinyTree_t* tree,
                            NW_Uint8*  segmentAddr,         
                            NW_TinyTree_Offset_t  offset,
                            NW_TinyTree_Offset_t* index){
  NW_ASSERT(tree != NULL);

  return NW_TinyTree_EBuffer_GetIndex(tree->ebuffer, segmentAddr, offset, index);
}



NW_Uint8*
NW_TinyTree_GetWritableBlock(NW_TinyTree_t* tree,
                             NW_TinyTree_Offset_t      size,
                             NW_TinyTree_Offset_t    * source_offset){  /* OUT */

  return NW_TinyTree_EBuffer_GetWritableBlock(tree->ebuffer, size, source_offset);

}

NW_TinyTree_t*
NW_TinyTree_Node_findTreeOld(NW_TinyTree_Node_t *node)
{
  NW_TinyTree_Node_t *parent = NW_TinyTree_findParent(node);
  NW_ASSERT(node != NULL);

  // find rootNode 
  while (parent != NULL)
  {
    node = parent;
    parent = NW_TinyTree_findParent(parent);
  }
  // root node should be next to sentinal tree node 
  while(node != NULL){
    if((node->flags & TNODE_FLAG_TREE) == TNODE_FLAG_TREE){
      return ((NW_TinyTree_TreeNode_t*)node)->tree;
    }
    --node;
  }
  // Not reached 
  NW_ASSERT(0);
  return NULL;
}

EXPORT_C NW_TinyTree_t*
NW_TinyTree_Node_findTree(NW_TinyTree_Node_t *node)
    {
    if (!node->tree)
        {
        return NW_TinyTree_Node_findTreeOld(node);
        }
    else
        {
        return node->tree;
        }
        
    }

/*
* Create a new node as a child of an existing node. The child
* references the source buffer at offset. The new child node is
* returned.  
*/

NW_TinyTree_Node_t*
NW_TinyTree_createChild(NW_TinyTree_t *tree, 
                        NW_TinyTree_Node_t *parent, 
                        NW_TinyTree_Offset_t offset){

  NW_TinyTree_Node_t *child = NW_TinyTree_createNode(tree, offset);

  if (child != NULL){
    NW_TinyTree_attachChild(parent, child);
  }
  
  return child;
}

/*
* Create a new node as an immediate sibling to an existing node. The
* child references the source buffer at offset. The new child node is
* returned.  
*/
NW_TinyTree_Node_t*
NW_TinyTree_createSibling(NW_TinyTree_t *tree, 
                          NW_TinyTree_Node_t *node, 
                          NW_TinyTree_Offset_t offset)
{
  NW_TinyTree_Node_t *sibling = NW_TinyTree_createNode(tree, offset);
  NW_TinyTree_attachAfter(node, sibling);
  return sibling;
}

void
NW_TinyTree_recurse(NW_TinyTree_t* tree,
                    NW_TinyTree_Node_t* start_node, 
                    void (*Node_CB) (NW_TinyTree_t*, NW_TinyTree_Node_t *, void *),
                    void * context)
{
  NW_TinyTree_Node_t *child;
  
  (*(Node_CB)) (tree, start_node, context);
  if((child = NW_TinyTree_findFirstChild(start_node)) != 0){
    NW_TinyTree_recurse(tree, child, Node_CB, context);
    while((child = NW_TinyTree_findNextSibling(child)) != 0){
      NW_TinyTree_recurse(tree, child, Node_CB, context);
    }
  }
}

/* Initialize a node iterator with a start node */

void
NW_TinyTree_NodeIterator_init(NW_TinyTree_Node_t* start_node, 
                              NW_TinyTree_NodeIterator_t* iterator)
{
  iterator->start_node = start_node; 
  iterator->traversal_node = NULL;
}

/* 
* Iterate through a subtree returning each node exactly once.
* The algorithm follows the toplogical ordering of the tree (if there is
* an edge from v0 to v1, always visit v0 before v1). It's equivalent to 
* solving a labyrinth by keeping your right hand on the wall!
*/

NW_TinyTree_Node_t*
NW_TinyTree_NodeIterator_iterate(NW_TinyTree_NodeIterator_t *iterator){
  
  NW_TinyTree_Node_t* node;

  if(iterator->start_node == NULL)
    return NULL;

  if(iterator->traversal_node == NULL){
    node = iterator->start_node;
    if(iterator->start_node->first_child != 0){
      iterator->traversal_node = iterator->start_node->first_child;
    }
    else{
      iterator->start_node = NULL;
    }
    return node;
  }

  node = iterator->traversal_node;
  
  /* First try to move down */
  if(iterator->traversal_node->first_child != 0){
    iterator->traversal_node = iterator->traversal_node->first_child;
    return node;
  }

  /* If you can't move down, move right */

  if((iterator->traversal_node->flags & TNODE_FLAG_LASTSIBLING) != TNODE_FLAG_LASTSIBLING){
    iterator->traversal_node = iterator->traversal_node->next_sibling;
    return node;
  }

  /* If you can't move right, move back up */
  /* TODO:  some other expression here */
  while(node != NULL){
    iterator->traversal_node = iterator->traversal_node->next_sibling;
    /* If you reached the start, quit */
    if(iterator->traversal_node == iterator->start_node){
      iterator->start_node = NULL;
      return node;
    }
    /* Otherwise, try to move right */
    if((iterator->traversal_node->flags & TNODE_FLAG_LASTSIBLING) != TNODE_FLAG_LASTSIBLING){
      iterator->traversal_node = iterator->traversal_node->next_sibling;
      return node;
    }
    /* Otherwise, continue to move up */
  }
  return NULL;
}

/* Iterate the siblings of a node */ 

NW_TinyTree_Node_t*
NW_TinyTree_NodeIterator_iterateSiblings(NW_TinyTree_NodeIterator_t *iterator){
  if((iterator->start_node->flags & TNODE_FLAG_LASTSIBLING)== TNODE_FLAG_LASTSIBLING)
    return NULL;
  else{
    iterator->start_node = iterator->start_node->next_sibling;
    return iterator->start_node;
  }
}





