//
//	wordlist table module version 5
//
//      modified from v4, Russel Lowke Nov 8th, 2001
//

#include			<stdio.h>
#include			<stdlib.h>
#include			<string.h>
#include			"wl6.h"

//  globals
//
struct link * gMyLink;							// link being looked at
int 		  gStreamAt;						// track current table stream


//
//  return stream for a given word
//
int getStream(char str[])
{
	int stream = str[0] - 'a';					// stream for this string
												// a = 0, z = 25

	if (stream < 0 || stream > 25)				// ensure stream is
			stream = -1;						// within bounds
	
	return stream;
}


//
//	initalize module
//
symtab * init_table()
{
	int i;										// counter
	symtab * newTable;
	
	// get memory and allocate values for struct	
	newTable = malloc(sizeof(struct symtab));
	if (newTable == NULL)						// out of memory
		return NULL;

	// create symbol table with 
	// 26 version links of head nodes - one for each letter a thru z
	for (i = 0; i < MAXTABLE; i++)
		newTable->head[i].next = NULL;

	return newTable;
}


//
//  search table for word, returning ptr to that word - if found
//
struct link *searchTable(symtab * tp, char str[])
{
	// linkp = head of table stream 
	struct  link * linkp = tp->head[getStream(str)].next;
	
	// loop through words
	while(linkp != NULL) {
		
		if (strcmp(str, linkp->word) == 0)		// found duplicate
			return linkp;			
		else if (strcmp(str, linkp->word) < 0)	// past word, stop looking
			return NULL;						// ( 1/2's search time )
		else {
			linkp = linkp->next;				// ... get next word
		}
	}
	
	return NULL;
}


//
//	check if word is in table
//
int in_table(symtab * tp, char str[])
{
	return (searchTable(tp, str) != NULL);
}


//
//  retrieve value from table
//	
int lookup(symtab * tp, char str[])
{
	struct link * linkp = searchTable(tp, str);
	
	if (linkp == NULL)
		return NO;								// not found
	else
		return linkp->value;					// return value
}


//
//  update value in table
//
int update(symtab * tp, char str[], int val)
{
	struct link * linkp = searchTable(tp, str);
	
	if (linkp == NULL)
		return NO;								// not found
	else {
		linkp->value = val;						// update
		return YES;
	}
}



//
//	insert a new value into table
//
int insert(symtab * tp, char str[], int val)
{
	int		      stream = getStream(str);		// stream for this word
	char		* newstr ;
	struct link	* newlink,
	       		* prevLink = NULL;

	// get memory and allocate values for struct
	newstr = malloc( 1 + strlen(str));
	if (newstr == NULL)							// out of memory
		return NO;
	strcpy( newstr, str );
	newlink = malloc(sizeof(struct link));
	if (newlink == NULL)						// out of memory
		return NO;
	
	// store word and value into newlink
	newlink->word      = newstr;
	newlink->value     = val;

	// traverse list, looking for place to insert word str
	for (prevLink =  &tp->head[stream], gMyLink = tp->head[stream].next;
		 gMyLink != NULL;
		 prevLink = gMyLink, gMyLink  = gMyLink->next ) {
		
		// break if link word is after newstr
		if (strcmp(newstr, gMyLink->word) < 0) {
			break;
		}
	}
	
	// squeeze newlink into linked list
	newlink->next = gMyLink;					// attach to next link
	prevLink->next = newlink;					// attach to previous link
	
	return YES;
}


//
//  delete word from table
//
void word_delete(symtab * tp, char str[])
{
	struct	link * linkp,						// link
	             * prevLink;					// previous link
	int		stream = getStream(str);			// stream for this word

	prevLink = &tp->head[stream];
	for( linkp = tp->head[stream].next ; linkp ; prevLink = linkp,
			                                 linkp    = linkp->next ) {
		if ( strcmp( linkp->word, str) == 0 ) {	// if found
			prevLink->next = linkp->next;		// kill link word
			free(linkp);						// free memory
			break;
		}
	}	
}


//
//	return first word
//
char *firstword(symtab *tp)
{
	gStreamAt = 0;								// set global streamAt
	gMyLink = tp->head[gStreamAt].next;			// set global gMyLink
	return nextword(tp);
}


//
//	return next word
//
char *nextword(symtab *tp)
{
	char *ret_val;

	if ( gMyLink == NULL ) {
		gStreamAt++;							// increment to next stream
		if (gStreamAt == MAXTABLE)				// check end of table
			return NULL;
		else
			gMyLink = tp->head[gStreamAt].next;	// recursive, load new stream
			return nextword(tp);				
	}
	else {
		ret_val = gMyLink->word;				// return word
		gMyLink = gMyLink->next;				// get next word
		return ret_val;
	}
}
