#include "gskprefixtree.h"
#include <string.h>

static inline GskPrefixTree *
tree_alloc (const char *str)
{
  GskPrefixTree *rv = g_new (GskPrefixTree, 1);
  rv->prefix = g_strdup (str);
  rv->next_sibling = rv->children = NULL;
  rv->has_data = FALSE;
  return rv;
}

static inline GskPrefixTree *
tree_alloc_n (const char *str, guint len)
{
  GskPrefixTree *rv = g_new (GskPrefixTree, 1);
  rv->prefix = g_strndup (str, len);
  rv->next_sibling = rv->children = NULL;
  rv->has_data = FALSE;
  return rv;
}
static inline void
tree_set_prefix (GskPrefixTree *tree,
                  const char *prefix)
{
  char *dup = g_strdup (prefix);
  g_free (tree->prefix);
  tree->prefix = dup;
}

gpointer gsk_prefix_tree_insert       (GskPrefixTree   **tree,
                                       const char       *prefix,
                                       gpointer          data)
{
  g_return_val_if_fail (prefix[0] != 0, NULL);

  /* look for a node that shares some prefix in common with us */
  while (*tree)
    {
      if (prefix[0] == (*tree)->prefix[0])
        {
          const char *tp_at = (*tree)->prefix;
          const char *p_at = prefix;
          while (*p_at != 0 && *p_at == *tp_at)
            {
              p_at++;
              tp_at++;
            }
          if (*p_at == 0 && *tp_at == 0)
            {
              /* *tree is an exact match */
              gpointer rv = (*tree)->has_data ? (*tree)->data : NULL;
              (*tree)->has_data = TRUE;
              (*tree)->data = data;
              return rv;
            }

          if (*tp_at == 0)      /* p_at not empty */
            {
              tree = &((*tree)->children);
              prefix = p_at;
              continue;
            }

          if (*p_at == 0)
            {
              /* pattern is too short for tree: must restructure tree:
                  
                  if tree is 'prevsib's' pointer, and *tree is the crrent node,
                  then
                    
                     prevsib                         prevsib
                       |                                |
                       v                                v
                     *tree -> child        becomes     new  -> *tree  -> child
                       |                                |
                       v                                v
                     nextsib                         nextsib */
              GskPrefixTree *new = tree_alloc_n (prefix, p_at - prefix);
              new->next_sibling = (*tree)->next_sibling;
              (*tree)->next_sibling = NULL;
              new->children = (*tree);
              tree_set_prefix (new->children, tp_at);
              *tree = new;
              new->has_data = TRUE;
              new->data = data;
              return NULL;
            }

          /* else, a mismatch.  we must make a new family of trees.

                  prevsib                   prevsib
                     |                         |
                     v                         v
                   *tree       becomes      common -> *tree(end)
                     |                         |         |
                     v                         v         v
                   nextsib                   nextsib    new */
          {
            GskPrefixTree *common = tree_alloc_n (prefix, p_at - prefix);
            GskPrefixTree *cur = *tree;
            common->next_sibling = cur->next_sibling;
            common->children = cur;
            *tree = common;
            cur->next_sibling = NULL;
            tree = &(cur->next_sibling);
            tree_set_prefix (cur, tp_at);
            prefix += p_at - prefix;

            /* prefix not empty */
          }
        }
      else
        {
          tree = &((*tree)->next_sibling);
        }
    }

  /* create a new node with the remaining characters is in */
  *tree = tree_alloc (prefix);
  (*tree)->has_data = TRUE;
  (*tree)->data = data;
  return NULL;
}


gpointer gsk_prefix_tree_lookup       (GskPrefixTree    *tree,
                                       const char       *str)
{
  gpointer found = NULL;
  GskPrefixTree *at = tree;

  while (*str && at)
    {
      for (         ; at; at = at->next_sibling)
        if (g_str_has_prefix (str, at->prefix))
          {
            str += strlen (at->prefix);
            if (at->has_data)
              found = at->data;
            at = at->children;
            break;
          }
    }
  return found;
}

gpointer gsk_prefix_tree_lookup_exact (GskPrefixTree    *tree,
                                       const char       *str)
{
  gpointer found = NULL;
  GskPrefixTree *at = tree;

  while (*str && at)
    {
      for (         ; at; at = at->next_sibling)
        if (g_str_has_prefix (str, at->prefix))
          {
            str += strlen (at->prefix);
            if (at->has_data)
              found = at->data;
            at = at->children;
            break;
          }
    }
  return *str ? NULL : found;
}

GSList  *gsk_prefix_tree_lookup_all   (GskPrefixTree    *tree,
                                       const char       *str)
{
  GSList *rv = NULL;
  GskPrefixTree *at = tree;

  while (*str && at)
    {
      for (         ; at; at = at->next_sibling)
        if (g_str_has_prefix (str, at->prefix))
          {
            str += strlen (at->prefix);
            if (at->has_data)
              rv = g_slist_prepend (rv, at->data);
            at = at->children;
            break;
          }
    }
  return rv;
}
// good idea, but we don't need it.
//gpointer gsk_prefix_tree_remove       (GskPrefixTree    *tree,
//                                       const char        *prefix)
//{
//  ...
//}
//
void     gsk_prefix_tree_foreach      (GskPrefixTree    *tree,
                                       GFunc              func,
                                       gpointer           func_data)
{
  while (tree)
    {
      if (tree->has_data)
        func (tree->data, func_data);
      gsk_prefix_tree_foreach (tree->children, func, func_data);
      tree = tree->next_sibling;
    }
}
void     gsk_prefix_tree_destroy      (GskPrefixTree    *tree)
{
  while (tree)
    {
      GskPrefixTree *next = tree->next_sibling;
      g_free (tree->prefix);
      gsk_prefix_tree_destroy (tree->children);
      tree = next;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1