/*
   File: search.c
   searching in the lexicon.

   Copyright 2005 Radboud University of Nijmegen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Library General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   CVS ID: "$Id: search.c,v 1.4 2006/10/18 08:37:36 marcs Exp $
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

/* standard include files */
#include <stdio.h>
#include <string.h>

/* agfl include files */
#include <abase_error.h>
#include <abase_memalloc.h>
#include <lexicon.h>

/* local include files */
#include "search.h"

enum { WORD_SZ = sizeof(void *) };
enum { TRIE_PATH_IDX = 0, TRIE_SIZE_IDX = 1, TRIE_HEADER_SZ = 2 };

static unsigned char* align (unsigned char* p)
{ size_t mod = (size_t)(((unsigned long) p) % WORD_SZ);
  return ((mod)?(p + WORD_SZ - mod):p);
}

int dump_node (int mode, unsigned char *p)
{
   switch (mode)
   {
      case modeEMPTY:
         printf ("empty\n");
         break;
      case modeNODE:
         printf ("node: '%s'\n", p);
         break;
      case modePATH:
         printf ("path: '%s'\n", p);
         break;
      case modeLEAF:
         if (p == NULL) printf ("empty\n");
         else printf ("word: '%s'\n", p);
         break;
      default:
         printf ("mode?'\n");
         break;
   }
   return 1;
}

int walk_tree (int mode, Trie trie, long offset, unsigned char *word, int f(int, unsigned char *))
{
   /* 
   ** depth first passing each node to f()
   */
   int l, walk;
   unsigned char *p;

   /*
   // read trie header
   */
   unsigned char *node = (unsigned char *)trie + offset;
   unsigned char path = node[TRIE_PATH_IDX];
   unsigned char size = node[TRIE_SIZE_IDX];
   node += TRIE_HEADER_SZ;

   /*
   // what kind of information is requested?
   // construct what is needed.
   */
   if (mode == modeNODE)
   {  /* 
      // only node strings are passed but the path must be '\0' terminated. 
      */
      l = path;
      p = abs_calloc (sizeof (char), l + 2, "walk_tree");
      if (path > 0) memcpy (p, node, path);
   }
   else if (mode != modeEMPTY) /* && !modeNODE */
   {  /* 
      // the full string (from the root) is requested 
      */
      l = strlen (word);
      p = abs_calloc (sizeof (char), l + path + 2, "walk_tree");
      if (l > 0) memcpy (p, word, l);
      if (path > 0) 
      {
         memcpy (p+l, node, path);
         l += path;
      }
   } 
   else /* modeEMPTY */
   {
      /*
      // no string information requested
      */
      l = 0;
      p = NULL;
   }

   /* 
   // space at the end for c and '\0' 
   */
   if (mode != modeEMPTY)
   {
      p[l] = '\0';
      p[l+1] = '\0';
   }

   walk = 1;
   if (size == 0) walk = f(modeLEAF, p);
   else
   {
      int i;

      for (i=0; i<size && walk; i++) 
      {
         if (mode != modeEMPTY) p[l] = node[path+i];
         offset = *(long*)align(node + path + size + WORD_SZ * i);

         if (offset == 0) walk = f(modeLEAF, p);
         else if ((mode == modeNODE) || (mode == modePATH)) walk = f(mode, p);

         if (walk && (offset > 0)) walk =  walk_tree (mode, trie, *(long*)align(node + path + size + WORD_SZ * i), p, f); 
      }
   }

   if (mode != modeEMPTY) abs_free (p, "walk_tree");
   return walk;
}
