// 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"
#import "WebsterExceptions.h"
#import "codebook.h"

static char*	codebookFileName =		// The name of the codebook file
  "/NextLibrary/References/Webster-Dictionary/codebook";

static BOOL	codebookInited = NO;

extern char* strrealloc(const char*);

char *websterErrorMessages[] = {
  "Webster: Internal error (%s)",
  "Webster: No codebook can be found (%s)",
  "Webster: Bad codebook (%s)",
  "Webster: No btree can be found (%s)",
  "Webster: Bad entry (%s)",
};

void
WebsterErrorReporter(NXHandler *errorState)
{
  if (errorState->code - NX_APPBASE < sizeof(websterErrorMessages)/sizeof(char*))
    NXLogError(websterErrorMessages[errorState->code -NX_APPBASE],
	       errorState->data1);
  else 
    NXLogError("Webster error: unknown %d (%s)\n",
	       errorState->code -NX_APPBASE, errorState->data1);
  return;
}

@implementation WebsterVolume

+ (void)setCodebookFileName: (const char*) theFileName
{
  codebookFileName = strrealloc(theFileName);
}

+ initialize
{
  NXRegisterErrorReporter(websterInternalError,
			  websterInternalError + sizeof(websterErrorMessages)/sizeof(char *), 
			  WebsterErrorReporter);
  return self;
}

+ (const char *)libraryCopyrightString
{
  return 
    "Webster access Library version 1.0, Copyright (C) 1994 BENOIT GRANGE.\n";
}

+ (const char *)volumeCopyrightString
{
  return 
    "Webster dictionary & thesaurus Copyright (C) 1983-1989 Merriam-Webster Inc.\n";
}

- init
// Just in case, initialize everything to nils
{
  fileName = NULL;
  headwordsBTree = entriesBTree = nil;
  headwordsCursor = entriesCursor = nil;
  websterModes = NULL;
  return self;
}

- initFromFile: (const char*) theFileName
// Just load the two BTrees, raising exceptions if anything fails
{
  [self init];

  NX_DURING

  if (!codebookInited) {
    initCodebook(codebookFileName);
    codebookInited = YES;
  }

  fileName = strrealloc(theFileName);
  headwordsBTree = [[IXBTree alloc] initFromName: "Headwords" inFile: fileName forWriting: NO];
  if (!headwordsBTree) NX_RAISE(websterNoBTree, fileName, NULL);
  headwordsCursor = [[IXBTreeCursor alloc] initWithBTree: headwordsBTree];
  if (!headwordsBTree) NX_RAISE(websterInternalError, fileName, NULL);
  entriesBTree = [[IXBTree alloc] initFromName: "Entries" inFile: fileName forWriting: NO];
  if (!entriesBTree) NX_RAISE(websterNoBTree, fileName, NULL);
  entriesCursor = [[IXBTreeCursor alloc] initWithBTree: entriesBTree];
  if (!entriesBTree) NX_RAISE(websterInternalError, fileName, NULL);
  return self;

  NX_HANDLER

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

  NX_ENDHANDLER;
}

- (WebsterWord*) defineWord: (const char*) word useCursor: (IXBTreeCursor*) cursor
{
  char* newWord = strrealloc(word);
  char* c = newWord;
  while (*c) { *c = NXToLower(*NXToAscii(*c)); c++; }

  if ([cursor setKey: newWord andLength: strlen(newWord)]) {
    WebsterWord* w;
    websterKey* keys = NULL;
    int length = [cursor readValue: (void **)&keys];
    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: newWord fromVolume: self andKeys: keys number: length/4];
    if (!w) NX_RAISE(websterInternalError, "[WebsterWord initForWord] failed", NULL);
    return w;
  } else {
    free(newWord);
    return nil;
  }
}

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

- (WebsterWord*) defineByReference: (const char*) word
// Default implementation is void
{
  return nil;
}

- (List*) completeWord: (const char*) word maxCount: (int) max;
// Default implementation is void
{
  return nil;
}

- (List*) completeWord: (const char*) word;
{
  return [self completeWord: word maxCount: 256];
}

- (List*) wildcardWord: (const char*) word
// Default implementation is void
{
  return nil;
}

- (BOOL)canOutputDefinitionsAs: (const char*)set
{
  websterMode* m = websterModes;
  while (m->modeName) {
    if (!strcmp(set, m->modeName)) return YES;
    m++;
  }
  return NO;
}

- (websterMode *)getModeEntry: (const char*)mode
{
  websterMode* m = websterModes;
  while (m->modeName) {
    if (!strcmp(mode, m->modeName)) return m;
    m++;
  }
  return NULL;
}

- (IXBTreeCursor*) getEntriesCursor
{
  return entriesCursor;
}

- free
{
  if (headwordsCursor) [headwordsCursor free], headwordsCursor = nil;
  if (headwordsBTree) [headwordsBTree free], headwordsBTree = nil;
  if (entriesCursor) [entriesCursor free], entriesCursor = nil;
  if (entriesBTree) [entriesBTree free], entriesBTree = nil;
  if (fileName) free(fileName), fileName = NULL;
  return [super free];
}

@end
