// Hey ! emacs, it is an -*- objective-c -*- file

/*

    Webster Access, a program to use NeXT online Webster dictionary.
    Copyright (C) 1994 Benoit Grange, ben@fizz.fdn.org

    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 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., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#import "WebsterVolume.h"
#import "WebsterWord.h"

extern char* strrealloc(const char*);

extern void convertToAscii(const void* data, int length,
			   const char* word, NXStream** outstream, int select);

static websterMode dicowebsterModes[] = {
{ "ascii", convertToAscii, 0 },
{ "next", convertToAscii, 1 },
{ "internal", convertToAscii, 2 },
{ NULL, NULL }
};

@implementation WebsterDictionary
- initFromFile: (const char*) theFileName
{
  if ([super initFromFile:theFileName]) {
    NX_DURING
    referenceBTree = nil;
    referenceCursor = nil;
      
    referenceBTree = [[IXBTree alloc] initFromName: "FullTextIndex" 
		      inFile: fileName forWriting: NO];
    if (!referenceBTree) NX_RAISE(websterNoBTree, fileName, NULL);
    referenceCursor = [[IXBTreeCursor alloc] initWithBTree: referenceBTree];
    if (!referenceCursor) NX_RAISE(websterInternalError, fileName, NULL);

    websterModes = dicowebsterModes;
    return self;

    NX_HANDLER

    // If some error occurs
    if (headwordsCursor) [headwordsCursor free], headwordsCursor = nil;
    if (headwordsBTree) [headwordsBTree free], headwordsBTree = nil;
    if (referenceCursor) [referenceCursor free], referenceCursor = nil;
    if (referenceBTree) [referenceBTree free], referenceBTree = nil;
    if (entriesCursor) [entriesCursor free], entriesCursor = nil;
    if (entriesBTree) [entriesBTree free], entriesBTree = nil;
    NX_RERAISE();

    NX_ENDHANDLER;
  } else return nil;
}

- (WebsterWord*) defineByReference: (const char*) word
{
  return [self defineWord:word useCursor:referenceCursor];
}

- free
{
  [referenceCursor free], referenceCursor = nil;
  [referenceBTree free], referenceBTree = nil;
  return [super free];
}

- (List*) completeWord: (const char*) word maxCount: (int) max
// Provide the list of the first 'n' endings of that word
{
  List* theList = NULL;
  
  char* newWord = strrealloc(word);
  char* c = newWord;
  int wordLength = strlen(word);

  char* theKey;
  unsigned int l, hint;

  while (*c) { *c = NXToLower(*NXToAscii(*c)); c++; }

  [headwordsCursor setKey: newWord andLength: strlen(newWord)];
  while ([headwordsCursor getKey: (void **)&theKey andLength: &l withHint: &hint] &&
	 !strncmp(word, theKey, wordLength)) {
    register char* theWord = malloc(l + 1);
    if (theWord) {
      WebsterWord* w;
      websterKey* keys = NULL;
      int length = [headwordsCursor readValue: (void **)&keys];

      strncpy(theWord, theKey, l);
      theWord[l] = 0;

      if ((length % 4) || (length < 8) || (keys[0] != 0xFFFFFFFF)) {
	free(keys); 
	NX_RAISE(websterBadEntry, word, NULL);
      }

      // Put all in the word, this should not fail
      w = [[WebsterWord alloc] initForWord: theWord fromVolume: self andKeys: keys number: length/4];
      if (!w) NX_RAISE(websterInternalError, "[WebsterWord initForWord] failed", NULL);
      if (!theList) theList = [[List alloc] init];
      [theList addObject: w];
      if (--max <= 0) break;
    }
    [headwordsCursor setNext];
  }
  free(newWord);
  return theList;
}

@end

