#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <strings.h>
#include <sys/param.h>
#include <math.h>
#include "hash.h"

extern int errno;
extern short int hash();

char *worddir;




/*
 * prime takes a short int and returns one if it is prime, 0 otherwise.
 */

prime(x)
short int x;
{
     double root = sqrt((double) x);
     short int divisor = 2;

     while (divisor <= root) {
	  if (x % divisor == 0)
	       return(0);
	  else
	       divisor++;
     }
     return(1);
}





/*
 * primer takes a short int and returns the closest (and bigger) short
 * int that is prime.
 */

primer(x)
short int x;
{
     if (prime(x))
	  return(x);
     else
	  return(primer(x + 1));
}

	  
		    
/*
 * words_in_file takes a FILE *, counts the number of lines (and
 * presumably therefore the number of words) in it and returns that
 * value after rewinding back to the beginning of the file.
 */

int words_in_file(wordfile)
FILE *wordfile;
{
     int lines = 0;
     int numread;
     char c[BUFSIZ];
     char *ptr;
     rewind(wordfile);

     while (numread = fread(c, sizeof(*c), BUFSIZ - 1, wordfile)) {
	  c[numread] = '\0';
	  ptr = c;
	  while (ptr = index(ptr, '\n')) {
	       ptr++;
	       lines++;
	  }
     }

     rewind(wordfile);

     return(lines);
}





/*
 * open_input_file takes a number of letters we're working with and
 * returns a FILE * to the word file containing the new-line separated
 * words of that length, prepared for reading.
 */

FILE *open_input_file(length)
char length;
{
     char filename[MAXPATHLEN];

     sprintf(filename, INPUT_TMPLT, worddir, length);

     return(fopen(filename, "r"));
}






/*
 * write_hash_table takes a number of letters, an array size and an
 * array of short integers and writes them into the appropriate file.
 */

int write_hash_table(letters, maxsize, indices)
char letters;
short int maxsize;
short int indices[];
{
     char filename[MAXPATHLEN];
     FILE *outfile;
     short int num_to_write;

     sprintf(filename, HASH_TMPLT, worddir, letters);
     if (! (outfile = fopen(filename, "w"))) {
	  fprintf(stderr, "buildhash: error opening index file %s.\n",
		  filename);
	  return(errno);
     }

     while (maxsize--) {
	  num_to_write = htons(*(indices)++);
	  if (! fwrite(&num_to_write, sizeof(num_to_write), 1, outfile))
	       return(errno);
     }

     fclose(outfile);
     return(0);
}





/*
 * make_hash_table takes simply a number of letters.  It processes the
 * file containing the words with that number of letters, builds a
 * hash table and a text table, and writes out the hash table and text
 * table.  It returns the number of words actually processed, or 0 if
 * the input file was empty, or -1 on error
 */
int make_hash_table(letters)
char letters;
{
    int num_words = 0, counter = 0;
    FILE *word_file, *txt_file;
    char word_file_name[MAXPATHLEN], txt_file_name[MAXPATHLEN];
    short int *indices;
    short int array_size = 0;
    short int hashval;
    static char word[MAX_LETTERS];

    sprintf(word_file_name, INPUT_TMPLT, worddir, letters);
    sprintf(txt_file_name, WORD_TMPLT, worddir, letters);

    word_file = fopen(word_file_name, "r");
    if (! word_file) {
	 fprintf(stderr, "buildhash: couldn't open word file %s.\n",
		 word_file_name);
	 return(-1);
    }
    txt_file = fopen(txt_file_name, "w");
    if (! txt_file) {
	 fprintf(stderr, "buildhash: couldn't open text file %s.\n",
		 txt_file_name);
	 return(-1);
    }
    for (counter = 0; counter < letters; counter++)
	 fwrite("1", 1, 1, txt_file);
    
    num_words = words_in_file(word_file);

    array_size = primer(2 * num_words);

    indices = (short int *) malloc(sizeof(short int) * array_size);
    if (! indices) {
	 fprintf(stderr, "buildhash: can't malloc for index table!\n");
	 return(-1);
    }
    for (counter = 0; counter < array_size; counter++)
	 *(indices + counter) = NULL;
    counter = 1;
    
    while (counter <= num_words) {
	 fgets(word, letters + 2, word_file);
	 *index(word, '\n') = '\0';
	 hashval = hash(word, array_size);
	 while (*(indices + hashval))
	      hashval = (hashval + 1) % array_size;

	 *(indices + hashval) = counter;

	 if (fwrite(word, 1, letters, txt_file) < letters) {
	      fprintf(stderr, "buildhash: error writing to text file.\n");
	      return(-1);
	 }
	 counter++;
    }

    fclose(txt_file);
    fclose(word_file);
    if (write_hash_table(letters, array_size, indices))
	 return(-1);

    return(num_words);
}






/*
 * make_hash_tables takes a minimum number of letters and a maximum
 * number of letters and makes all the hash tables for that range.
 * A return value of 0 signifies success.
 */

make_hash_tables(min_letters, max_letters)
char min_letters, max_letters;
{
     char loop = min_letters;

     while (loop <= max_letters) {
	  if (make_hash_table(loop++) == -1)
	       return(-1);
     }
     return(0);
}






main(argc, argv)
int argc;
char *argv[];
{
     char min_letters;
     char max_letters;

     if (argc < 3) {
	  fprintf(stderr,
		  "usage: buildhash worddir min_letters max_letters\n");
	  exit(1);
     }

     worddir = argv[1];
     min_letters = (char) atoi(argv[2]);
     max_letters = (char) atoi(argv[3]);

     exit(make_hash_tables(min_letters, max_letters));
}
