/* 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 <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#include <locale.h>
#include <time.h>
#include <ifile.h>        /* standard ifile library */

#define SEMKEY  10439838

int semid;
struct sembuf sops;

arguments args;
extern struct argp argp;
int msgs_read;        /* number of messages actually read in */

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

/* ifilter specific function prototypes */
int cmp(const void *e1, const void *e2);

/* Main program */
/* written by Jason Rennie <jrennie@ai.mit.edu> */
int 
main (int argc, char **argv)
{
  char *data_file = NULL;   /* full path of idata file */
  char *home_dir = NULL;    /* full path of user's home directory */
  FILE *MSG = NULL;         /* file pointer for a message */
  category_rating * ratings;
  ifile_db idata;
  htable *message = NULL;
  int i;
  int db_read_result = 0, db_write_result = 0;
  char *file_name;
  int trimmed_words;

  setlocale(LC_ALL, "" );

  /* Harry's semaphore stuff to protect two ifile jobs from stepping
   * on each other */
  /* Find the Semaphore id */
  if ((semid = semget(SEMKEY, 1, 0666)) < 0)
    if ((semid = semget(SEMKEY, 1, 0666|IPC_CREAT|IPC_EXCL)) < 0)
      {
	perror("semget");
	exit (-1);
      }

  /* Wait for Semaphore to clear */
  sops.sem_num = 0;
  sops.sem_op = 0;
  sops.sem_flg = 0;

  if (semop(semid, &sops, 1))
    {
      perror("semop");
      exit (-1);
    }
  
  /* Set the Semaphore to clear on exit */
  sops.sem_num = 0;
  sops.sem_op = 1;
  sops.sem_flg = SEM_UNDO;

  if (semop(semid, &sops, 1))
    {
      perror("semop");
      exit (-1);
    }
  
  ifile_init_args(&args);
  argp_parse (&argp, argc, argv, 0, 0, &args);
  
  ifile_verbosify(ifile_verbose, "%d file(s) passed\n", args.num_files);
  for (i=0; i < args.num_files; i++)
    ifile_verbosify(ifile_verbose, "file #%d: %s\n", i,
		    EXT_ARRAY_GET(args.file, char *, i));

  /* Get home directory */
  home_dir = getenv("HOME");
  if (home_dir == NULL)
    ifile_error("Fatal: HOME environment variable not defined!\n");
  ifile_verbosify(ifile_verbose, "home directory = %s\n", home_dir);

  /* Get the database file name */
  if (args.db_file != NULL)
    data_file = ifile_strdup (args.db_file);
  else
    data_file = ifile_sprintf("%s/%s", home_dir, DEFAULT_DB_FILE);

  /* remove the .idata file if requested */
  if (args.reset_data)
    {
      ifile_verbosify(ifile_progress, "Removing %s...\n", data_file);
      system(ifile_sprintf("rm %s", data_file));
    }

  ifile_db_init(&idata);
  ifile_open_log(argc, argv);
  ifile_default_lexer_init();

  /* argument variables that still need to be handled:
   * skip_header, minus_folder, plus_folder */

  /* Read the idata database */
  if ((args.read_db == TRUE) && (!args.print_tokens))
    db_read_result = ifile_read_db(data_file, &idata);

  /* If doing update, print warning & die if folder doesn't exist */
  if (args.plus_folder && !args.create_folder) {
    int match = FALSE;
    int i=0;
    if (!db_read_result)
      for (; i < idata.num_folders; ++i)
	if (strcmp(EXT_ARRAY_GET(idata.folder_name,char*,i), args.plus_folder) == 0)
	  match = TRUE;
    if (!match)
      ifile_error("Folder does not exist: %s\n", args.plus_folder);
  }

  /* read and lex the message(s) */
  if (args.read_message != TRUE)
    exit(0);

  msgs_read = 0;
  i = 0;
  DMZ_start = clock();
  do {
    if (args.num_files != 0) {
      {
	ifile_verbosify(ifile_verbose, "Reading message %d...\n",i);
	file_name = EXT_ARRAY_GET(args.file, char *, i);
	MSG = fopen(file_name, "r");
	if (MSG == NULL)
	  {
	    ifile_verbosify(ifile_quiet,
			    "Not able to open %s!  No action taken.\n",
			    file_name);
	    if( message != NULL )
              {
                htable_free(message,free,NULL);
                message = NULL;
              }
	  }
	else
	  {
	    message = ifile_read_message(MSG);
	    if (args.occur == TRUE)
	      ifile_bitify_document(message);
	    if (message != NULL)
	      msgs_read++;
	  }
	if (MSG && (args.verbosity >= ifile_debug || args.print_tokens))
	  ifile_print_message(message);
	if (MSG) fclose(MSG);
      }
    }
    else
      {
	ifile_verbosify(ifile_quiet, "Reading message from standard input...\n");
	message = ifile_read_message(stdin);
	msgs_read++;
	if (args.verbosity >= ifile_debug || args.print_tokens)
	  ifile_print_message(message);
      }

    /* Don't do anything else if we are printing tokens */
    if (args.print_tokens)
      continue;

    if ((args.query == TRUE || args.loocv_folder != NULL) && 
        idata.num_folders < 1)
      ifile_error(
            "Not able to perform query: no folders defined in database.\n");
    /* Do LOOCV queries if requested */
    if (args.loocv_folder != NULL)
      {
	if (db_read_result)
	  ifile_error("Not able to perform LOOCV: not able to open database\n");
	if (message == NULL)
	  continue;

	ifile_del_db(args.loocv_folder, message, &idata);
	ratings = ifile_rate_categories(message, &idata);
	qsort(ratings, idata.num_folders, sizeof(category_rating), cmp);

	if (args.concise)
	  {
	    file_name = EXT_ARRAY_GET(args.file, char *, i);
	    ifile_concise_ratings(file_name, stdout, ratings, &idata,
				  args.thresh);
	  }
	else
	  ifile_print_ratings(stdout, ratings, &idata, args.thresh);

	ifile_free_categories(ratings,&idata);
	ifile_add_db(args.loocv_folder, message, &idata, 0);
      }

    /* if a query was requested, make the calculations and output the results */
    if (args.query == TRUE)
      {
	if (db_read_result)
	  ifile_error("Not able to perform query: not able to open database\n");
	if (message != NULL)
	  {
	    ratings = ifile_rate_categories(message, &idata);
	    qsort(ratings, idata.num_folders, sizeof(category_rating), cmp);

	    if (args.concise)
	      {
		file_name = EXT_ARRAY_GET(args.file, char *, i);
		ifile_concise_ratings(file_name, stdout, ratings, &idata,
				      args.thresh);
	      }
	    else
	      ifile_print_ratings(stdout, ratings, &idata, args.thresh);

	    if (args.query_insert)
	      ifile_add_db(ratings[0].category, message, &idata, args.create_folder);
	    ifile_free_categories(ratings,&idata);
	  }
      }

    if (args.write_db == TRUE)
      {
	if (args.plus_folder != NULL)
	  if (message != NULL)
	    ifile_add_db(args.plus_folder, message, &idata, args.create_folder);

	if (args.minus_folder != NULL)
	  if (message != NULL)
	    ifile_del_db(args.minus_folder, message, &idata);

      }

    if (message) {
      htable_free(message, free, NULL);
      message = NULL;
    }
  } while (++i < args.num_files);

  DMZ_end = clock();
  ifile_verbosify(ifile_progress,
		  "Read %d message(s).  Time used: %.3f sec\n", msgs_read,
		  ((float)(DMZ_end-DMZ_start))/CLOCKS_PER_SECOND);

  if (args.write_db == TRUE) {
    if ((args.plus_folder != NULL || args.query_insert == TRUE) &&
	args.minus_folder == NULL)
      {
	trimmed_words = ifile_age_words(&idata, msgs_read);
	ifile_verbosify(ifile_progress,
			"Trimmed %d words due to lack of frequency\n",
			trimmed_words);
      }
    db_write_result = ifile_write_db(data_file, &idata);
    if (db_read_result != 0 && db_write_result == 0)
      {
	ifile_verbosify(ifile_quiet, "Created new %s file.\n", data_file);
	/* set proper permissions */
	system(ifile_sprintf("chmod 0600 %s\n", data_file));
      } 
  }

  ifile_close_log();

#ifdef DMALLOC  
  /* if we're debugging, clean up after malloc;  if not, don't bother
     spending the computrons since we're exiting anyhow. */
  ifile_stoplist_free();
  ifile_db_free(&idata);
#endif

  return 0;
}


/* a comparison function for sorting */
/* Written by Jason Rennie <jrennie@ai.mit.edu> for ifile */
int cmp (const void *e1, const void *e2)
{
  if (((category_rating *)e1)->rating > (((category_rating *)e2)->rating))
    return -1;
  else if (((category_rating *)e1)->rating < (((category_rating *)e2)->rating))
    return 1;
  else
    return 0;
}



