// UIndex.cp
// UIndex.h
// ----------------------------------------------------------------------------------
// This class is an index for a main dictionary.
// ----------------------------------------------------------------------------------
// History:
// 		Art Pollard			June 94
//			Original.
//		Clark Goble			08/12/94
//			Made the index into an abstract C++ class.
// ----------------------------------------------------------------------------------

// ----------------------------------------------------------------------------------
// Includes

#include "UIndex.h"
#include "UHead.h"
#include <string.h>
#include <stdlib.h>
#include "UError.h"

#ifdef	TEST
//#include "UDebugWindow.h"
#endif
// ----------------------------------------------------------------------------------
//	UIndex		- Index constructor
// ----------------------------------------------------------------------------------
// This is the basic constructor.  It creates an empty index.

//#define TRACE

UIndex::UIndex()
	:UExtended()
{
	WordTable = NULL;
	WordPtr = NULL;
	position = 0;
	TableSize = 0;
	NumWords = 0;
} // UIndex

// ----------------------------------------------------------------------------------
//	UIndex		- Index constructor
// ----------------------------------------------------------------------------------
// This is the basic constructor.  It creates an empty index.

UIndex::~UIndex()
{
	if (WordTable != NULL)
		free(WordTable);
	if (WordPtr != NULL )
		free(WordPtr);
} // ~UIndex

// ----------------------------------------------------------------------------------
//	Create		- Index constructor
// ----------------------------------------------------------------------------------
// This sets up the structures for creating an index.  It allocates space of the
// proper size and stores words in NULL terminated format.

short
UIndex::Create()
{
    #ifdef TRACE
    printf("\nUIndex::Create()");
    #endif
	WordTable = (UInt8 *) ::malloc(MAX_WORD_TABLE_LENGTH);
	if (WordTable == NULL) 
	{	ErrorFunc(eNo_Mem, SET);
		return (eGeneral);
	}
	memset(WordTable, 0, MAX_WORD_TABLE_LENGTH);
	position = 0;
	TableSize = 0;
	NumWords = 0;
		return (OK);
} // Create

// ----------------------------------------------------------------------------------
//	Clear		- Clears index data
// ----------------------------------------------------------------------------------
// This clears the structures for creating an index.  This allows the index to be
// used again for another physical index, either created or read in from disk.

void
UIndex::Clear()
{
  #ifdef TRACE
  printf("\nUIndex::Clear");
  #endif
	free(WordTable);
	WordTable = NULL;
	free(WordPtr);
	WordPtr = NULL;

} // Clear

// ----------------------------------------------------------------------------------
//	Insert		- Inserts a 'word' into the index
// ----------------------------------------------------------------------------------
// This one is fairly self explanatory, no?

void
UIndex::Insert(UInt8 *Word)
{
    #ifdef TRACE
    printf("\nUIndex::Insert");
    #endif
	// copy the string so that we don't need to worry about deleting
	strcpy( (char *) WordTable + position, (char *) Word);

	// advance the position
	position += strlen((char *)Word) +1; 	// 1 for the NULL termination
	NumWords++;

} // Insert


// ----------------------------------------------------------------------------------
//	Write		- Writes an index to disk
// ----------------------------------------------------------------------------------
// This writes an index out to disk.  This assumes that a spell checking dictionary
// file is the output file.  It writes file header information.

short
UIndex::Write(Uio *File, long Offset)
{
	 #ifdef TRACE
	 printf("\nUIndex::Write");
	 #endif

	 File->SetPos(Offset, SEEK_SET);
	if ( ErrorFunc(0, GET) < eNo_Err )
		return ( ErrorFunc(0, GET) );

	File->WriteData( (void *) WordTable, position);
	if ( ErrorFunc(0, GET) < eNo_Err )
		return ( ErrorFunc(0, GET) );

	if ( UHeaders::WriteIndexOffset(File, Offset) != OK)
	{	ErrorFunc(eWriting_File, SET);
		return (eWriting_File);
	}

	if ( UHeaders::WriteIndexLength(File, position) != OK)
	{	ErrorFunc(eWriting_File, SET);
		return (eWriting_File);
	}

	if ( UHeaders::WriteNumPages(File, NumWords) != OK)
	{	ErrorFunc(eWriting_File, SET);
		return (eWriting_File);
	}

	return (OK);
} // Write


short
UIndex::Write(FILE *File, long Offset)
{
	 #ifdef TRACE
	 printf("\nUIndex::Write");
	 #endif

	 // Note, we don't need to seek to the exact right
	 // place to write these variables in as they
	 // have their position recorded within the
    // function
    if ( UHeaders::WriteIndexOffset(File, Offset) != OK)
	{	ErrorFunc(eWriting_File, SET);
		return (eWriting_File);
	}

	 if ( UHeaders::WriteNumPages(File, NumWords) != OK)
	{	//ErrorFunc(eWriting_File, SET);
		return (ERROR);
	}

	if ( UHeaders::WriteIndexLength(File, position) != OK)
	{	//ErrorFunc(eWriting_File, SET);
		return (ERROR);
	}

	//File->SetPos(Offset, SEEK_SET);
	//if ( ErrorFunc(0, GET) < eNo_Err )
	//	return ( ErrorFunc(0, GET) );


	if(fseek(File,Offset,SEEK_SET) != 0)
		return ERROR;

	if(fwrite(WordTable,1,position,File) != position) {
		return ERROR;
	}
	//File->WriteData( (void *) WordTable, position);
	//if ( ErrorFunc(0, GET) < eNo_Err )
	//	return ( ErrorFunc(0, GET) );


	//if ( UHeaders::WriteIndexOffset(File, Offset) != OK)
	//{	//ErrorFunc(eWriting_File, SET);
	//	return (ERROR);
	//}

	return (OK);
} // Write

// ----------------------------------------------------------------------------------
//	Read		- Reads an index
// ----------------------------------------------------------------------------------
// This reads an index from the disk.  This assumes that a spell checking dictionary
// file is the input file.  It requires file header information.

short
UIndex::Read(Uio *File, long Offset)
{
	 #ifdef TRACE
	 printf("\nUIndex::Read");
	 #endif

	NumWords = (int) UHeaders::ReadNumPages( File );

	if (NumWords == eReading_File)
	{
		return (eReading_File);
	}

	TableSize = (size_t) UHeaders::ReadIndexLength(File);

	WordTable = (UInt8 *) malloc(TableSize);
	if (WordTable == NULL)
	{	ErrorFunc(eNo_Mem, SET);
		return (eNo_Mem);
	}

	File->SetPos(Offset, SEEK_SET);
	if ( ErrorFunc(0, GET) < eNo_Err )
		return ( ErrorFunc(0, GET) );

	File->ReadData(WordTable, TableSize);
	if ( ErrorFunc(0, GET) < eNo_Err )
		return ( ErrorFunc(0, GET) );

	return(OK);

} // Read

// ----------------------------------------------------------------------------------
//	CreateWordPointers		- Creates pointers for words
// ----------------------------------------------------------------------------------
// This is used to create word pointers (needed for a search) from an index which
// has been loaded into the index structure.  It allocates the memory used for the
// word pointers etc.  The pointers are merely offsets within the table.  The
// table is scanned for NULLs and then pointers can obviously be determined for the
// strings.

short
UIndex::CreateWordPointers()
{
	short Counter = 0;	// basically the offset in the table

	#ifdef TRACE
	printf("\nUIndex::CreateWordPointers()");
	#endif
	WordPtr = (size_t *) malloc(NumWords * sizeof(long));
	if (WordPtr == NULL)
	{	ErrorFunc(eNo_Mem, SET);
		return (eNo_Mem);
	}
	short WordNum = 0;	// which word pointer we are dealing with

	while (WordNum < NumWords)
	{
		if ( WordTable[Counter] != NULL)
		{
			WordPtr[WordNum]=Counter;
			WordNum++;
		}
		// Move to the end of the word
		for ( ; WordTable[Counter] != NULL ; Counter++)
			;
		Counter++;
	}

	return  (OK);

} // CreateWordPointers

// ----------------------------------------------------------------------------------
//	FindPage		- Finds which page a word is in
// ----------------------------------------------------------------------------------
// This does a binary search on the Table and finds the page that a word is in.
// The WordPointers MUST have been created BEFORE this function is used.

short
UIndex::FindPage(short guess, UInt8 *Word)
{
	short  	Left = 0;
	// short  	Right = NumWords - 1;	// AP 12/11/95
	short	Right = NumWords;			// AP 12/11/95
	short  	Middle;
	short  	Result;
	short     oldNumber = -1;			// AP 12/11/95
	char 	*Word2;
	char 	*Word3;

	#ifdef TRACE
	printf("\nUIndex::FindPage()");
	#endif

	if ( (guess < NumWords - 1) && (guess >= 0) )
	{
	    Word2 = (char *) &WordTable[WordPtr[guess]];
	    Word3 = (char *) &WordTable[WordPtr[guess+1]];
		if ( EStrCmp( (char *)Word, Word2 ) > 0)
			if ( EStrCmp( (char *)Word, Word3 ) < 0)
				return (guess);
	}

	// while (Left <= Right)							// AP 12/11/95
	while (TRUE)									// AP 12/11/95
	{
		Middle = (Left + Right) / 2;

		if(Middle == NumWords || Middle == oldNumber)	// AP 12/11/95
			break;                                       // AP 12/11/95

		Word2 = (char *) &WordTable[WordPtr[Middle]];

		Result = EStrCmp( (char *)Word, Word2);

		if( Result  < 0 )
			// Right = Middle -1;					// AP 12/11/95
			Right = Middle;					  	// AP 12/11/95
		else if (Result > 0 )
			// Left = Middle + 1;                        // AP 12/11/95
			Left = Middle;						 	// AP 12/11/95
		else
			return (Middle); 	// it matches

		oldNumber = Middle;							// AP 12/11/95
	}

	if(Middle == NumWords)							// AP 12/11/95
		Middle = NumWords -1;						// AP 12/11/95

	// numbers and other characters might be too low
	/* if (Left <= 0)
		return ( 0 );

	if (Left > NumWords - 1)
		return ( NumWords); */

	if ( Middle < 0 ) 		Middle =  0;				// AP 12/11/95
	if ( Middle > NumWords) 	Middle = NumWords;			// AP 12/11/95

	if(Middle == NumWords)    						 // AP 12/11/95
		Middle = NumWords -1;

	//return (Left -1 );						  	 // AP 12/11/95
	return (Middle);

} // FindPage


/*
short
UIndex::FindPage(short guess, UInt8 *Word)
{
	short  Left = 0;
	short  Right = NumWords - 1;
	short  Middle;
	short  Result;
	char *Word2;
	char *Word3;

	#ifdef TRACE
	printf("\nUIndex::FindPage()");
	#endif

	if ( (guess < NumWords-1) && (guess >= 0) )
	{
	    Word2 = (char *) &WordTable[WordPtr[guess]];
	    Word3 = (char *) &WordTable[WordPtr[guess+1]];
		if ( EStrCmp( (char *)Word, Word2 ) > 0)
			if ( EStrCmp( (char *)Word, Word3 ) < 0)
				return (guess);
	}

	while (Left <= Right)
	{
		Middle = (Left + Right) / 2;
		Word2 = (char *) &WordTable[WordPtr[Middle]];

		Result = EStrCmp( (char *)Word, Word2);

		if( Result  < 0)
			Right = Middle -1;
		else if (Result > 0 )
			Left = Middle + 1;
		else
			return (Middle); 	// it matches
	}

	// numbers and other characters might be too low
	if (Left <= 0)
		return ( 0 );

	if (Left > NumWords - 1)
		return ( NumWords);

	return (Left -1 );

} // FindPage


*/