/* ifile - intelligent mail filter for EXMH/MH
   ifile is Copyright (C) 1997  Jason Rennie <jrennie@ai.mit.edu>
   
   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 (see file 'COPYING'); if not, write to the Free
   Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA  02111-1307, USA.
   */

#include <stdarg.h>
#include <time.h>
#include <ifile.h>      /* main ifile function library */

/* variables for keeping track of time/speed of ifile */
extern clock_t DMZ_start, DMZ_end, DMZ2_start;

/* returns a hash value for the string S */
/* written by Jason Rennie <jrennie@ai.mit.edu> */
unsigned long
hash (const char * s, long int size)
{
  long int hashval;

  if (s == NULL) return 0;

  for(hashval=0; *s!='\0'; s++)
    hashval = (*s + (hashval << 5) - hashval) % size;

  return hashval;
}


/* Given a printf style format string and an arbitrarily long list of
 * arguments which are in accordance with the format string, ifile_sprintf
 * will allocate memory for and create a string according to the given
 * information. */
/* Written by Jason Rennie <jrennie@ai.mit.edu> for ifile. */
char *
ifile_sprintf (char * format, ...)
{
  char buf[MAX_STR_LEN] = ""; /* holds string to be returned by function */
  va_list ap;
  char * rtn;

  va_start(ap, format);
  assert(vsprintf(buf, format, ap) < MAX_STR_LEN - 1);
  va_end(ap);

  rtn = malloc(strlen(buf)+1);
  if (!rtn) abort();
  strcpy(rtn, buf);
  return rtn;
}


/* Returns a string which is the concatenation of an arbitrary number
 * of strings passed as arguments to the function.
 * First argumet passed to function is the number of strings passed to
 * the function which are to be concatenated. */
/* written by Jason Rennie <jrennie@ai.mit.edu> for ifile */
char *
ifile_cats (long int num_strings, ...)
{
  va_list ap;
  long int string_size = 0;
  char * new_string = NULL;
  long int i = 0;

  va_start(ap, num_strings);
  for (i=0; i < num_strings; i++)
    string_size += strlen(va_arg(ap, char *));
  va_end(ap);

  new_string = malloc(string_size+1);
  if (!new_string) abort();
  new_string[0] = '\0';

  va_start(ap, num_strings);
  for (i=0; i < num_strings; i++)
    strcat(new_string, va_arg(ap, char *));
  va_end(ap);

  return new_string;
}


/* Given an integer value, allocates space for and returns a string
 * representing the integer in character form */
/* Written by Jason Rennie <jrennie@ai.mit.edu> for ifile */
char *
itoa (long int number)
{  
  linked_list * list = NULL;
  linked_list * new_digit = NULL;
  linked_list * list_ptr = NULL, * old_list_ptr = NULL;
  char buf[MAX_STR_LEN];
  long int negative = FALSE;
  long int i = 0;
  char * tmp = NULL;

  if (number == 0)
    { 
      tmp = malloc(strlen("0") + 1);
      if (!tmp) abort();
      strcpy(tmp, "0"); 
      return tmp;
    }
  
  if (number < 0)
    {
      number = abs(number);
      negative = TRUE;
    }

  /* Loads digits into stack-like linked list */

  while (number > 0)
    {
      new_digit = (linked_list *) malloc(sizeof(linked_list));
      if (!new_digit) abort();
      new_digit->next = list;
      new_digit->digit = number - ((number/10)*10);
      number /= 10;
       list = new_digit;
    }
  
  /* Removes digits from list, copying them into the string to be returned */

  i = 0;
  if (negative == TRUE)
    { 
      buf[0] = '-';
      i++;
    }
  list_ptr = list; 
  for (; i < MAX_STR_LEN - 1; i++)
    {
      buf[i] = 48 + list_ptr->digit;
      old_list_ptr = list_ptr;
      list_ptr = list_ptr -> next;
      free(old_list_ptr);
      if (list_ptr == NULL) break;
    }
  buf[i+1] = '\0';
  tmp = malloc(strlen(buf) + 1);
  if (!tmp) abort();
  strcpy(tmp, buf);
  return tmp;
}


/*
 * Reads up to and including the next feedline (\n).  Returns a pointer to
 * STRING on success, and NULL on EOF or error.  Updates bufp also.
 */
/* Written by Jason Rennie <jrennie@ai.mit.edu> for ifile */
char *
readline (char **bufp)
{
  char *first = *bufp, *last;

  last = strchr(first, '\n');
  if (last == NULL)
    {
      return NULL;
    }
  *last = '\0';
  *bufp = last + 1;

  return first;
}


/* Wrapper for standard free() function.  Frees memory and then sets
 * pointer equal to NULL. */
/* Written by Jason Rennie <jrennie@ai.mit.edu> for ifile */

/* This seems like a big noop that's costing us efficiency -- jhbrown */
void 
ifile_free (void * var)
{
  free(var);
  var = NULL;
}


/* Accepts a pointer to a message and prints out all the words of that
 * message */
void
ifile_print_message (htable * message)
{
  hash_elem * elem;

  for (elem = htable_init_traversal(message);
       elem != NULL; elem = htable_next_traversal(message, elem))
    printf("(%ld,%s) ", (long int) elem->entry, (char *) elem->index);
  printf("\n");
}


/* Accepts a file pointer and reads/lexes text from the associated file.
 * Returns a hash table which maps words appearing in the message to
 * their frequency in the message. */
/* The GOOD code!  This uses Andrew's cool lexing code :) */
/* Written by Jason Rennie <jrennie@ai.mit.edu> and others for ifile */
htable * 
ifile_read_message (FILE * FP)
{
  ifile_lex * document;
  char token[MAX_STR_LEN];
  long int token_len;  /* length of token */
  long int old_freq;   /* previous frequency of word */
  htable * message = malloc(sizeof(htable));
  if (!message) abort();

  ifile_verbosify(ifile_verbose, "Reading message...\n");
  htable_init(message, 100, (unsigned long (*)(const void *, long int)) hash);

  DMZ2_start = clock();

  document = ifile_default_lexer->open_text_fp (ifile_default_lexer, FP);

  if (document)
    {
      token_len = ifile_default_lexer->get_word (ifile_default_lexer, document,
						 token, MAX_STR_LEN);
      while (token_len != 0)
	{
	  ifile_verbosify(ifile_debug, "Read \'%s\'.  length=%d\n", token,
			  token_len);
	  /* update arrays which strictly concern message */
	  old_freq = (long int) htable_lookup(message, (void *) token);
	  htable_put(message, ((char *) token),
		     (void *) (old_freq+1));
	  
	  token_len = ifile_default_lexer->get_word (ifile_default_lexer,
						     document, token, MAX_STR_LEN);
	}
      
      ifile_default_lexer->close (ifile_default_lexer, document);
    }
  else
    {
      ifile_verbosify(ifile_quiet, "Unable to read message.\n");
      ifile_free(message);
      return NULL;
    }
    
  ifile_verbosify(ifile_debug, "\n");
  DMZ_end = clock();
  ifile_verbosify(ifile_verbose,
		  "Finishing reading message.  Time used: %.3f sec\n",
		  ((float)(DMZ_end-DMZ2_start))/CLOCKS_PER_SECOND);

  return message;
}


/* Given a hash table representing a message, changes all non-zero
 * frequency values to zero */
void
ifile_bitify_document(htable * message)
{
  hash_elem *elem;

  for (elem = htable_init_traversal(message);
       elem != NULL; elem = htable_next_traversal(message, elem))
    if ((long int) elem->entry > 0)
      elem->entry = (void *) 1U;
}


/* Given an array of categories and their respective ratings, prints
 * the information to the given file */
/* Written by Jason Rennie <jrennie@ai.mit.edu> for ifile */
void 
ifile_print_ratings (FILE * FP, category_rating * ratings, ifile_db * idata,
		     int thresh)
{
  long int i;

  for (i = 0; i < idata->num_folders; i++)
    fprintf(FP, "%s %.8f\n", ratings[i].category, ratings[i].rating);
  
  if (thresh != 0 &&
      idata->num_folders > 1 && 
      (ratings[0].rating + ratings[1].rating) != 0)
  {
      fprintf(FP, "diff[%s,%s](%%) %.2f\n", 
                ratings[0].category, ratings[1].category,
                -(ratings[0].rating - ratings[1].rating) / 
                  (ratings[0].rating + ratings[1].rating) * 100);
  }
  fprintf(FP, "---------\n");
}

/* Written by Karl Vogel <vogelke@dnaco.net> for ifile */
void
ifile_concise_ratings (char * path, FILE * FP, category_rating * ratings,
                     ifile_db * idata, int thresh)
{
  //if (path) fprintf (FP, "%s ", path);
  float diff=0;
  
  diff=thresh+1;

  if (thresh != 0 && 
      idata->num_folders > 1 &&
      (ratings[0].rating + ratings[1].rating) != 0)
  {
    diff = -(ratings[0].rating - ratings[1].rating) / 
                  (ratings[0].rating + ratings[1].rating) * 1000;
  }
  if (path) fprintf (FP, "%s ", path);
  if (diff < thresh) 
    fprintf (FP, "%s,%s\n", ratings[0].category, ratings[1].category);
  else
    fprintf (FP, "%s\n", ratings[0].category);
}

/* Returns a pointer to a new string that is an exact duplicate of the
 * string pointed to by the s1 parameter.  The malloc() function is
 * used to allocate space for the new string. */
/* written by Jason Rennie <jrennie@ai.mit.edu> for ifile */
char *
ifile_strdup (const char *s1)
{
  char *s = (char *) malloc(strlen(s1)+1);

  if (!s) abort();
  strcpy(s, s1);

  return s;
}
