/* checkresults.c */
/****************************
  Updated, Oct 23, 2003
  The earlier version had a check to confirm
  that a thread that wants to update a
  record could start updating immediately
  if no other thread was currently updating
  that record.  This update checks to see if
  some other thread is waiting to update 
  that record, and if so, it removes this
  check on the assumption that the other
  thread might get there first.

  This should also run on WIN32 as well
  as unix.
*********************************
Updated Oct 22, 2003
  Corrected a bug so that it can process
  more than six threads, and adds code
  to confirm that a thread that wants
  to update a record starts to update
  immediately if no other thread is 
  updating that record.
************************************/
/* Checks the contents of log.txt, which
   is the output of CSCI.4210 Fall '03 Proj3 
   It opens the file, reads one line 
   at a time, and reports any errors 
****************************************/
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#include <string.h>
#endif
#define OP_WANT 0
#define OP_START 1
#define OP_FINISH 2
#define OP_INVALID 3
#define MAXTHREADS 255

/************** INIT ****************
  Initializes stuff and opens log.txt
***********************************/
FILE *Init(int t[], int r[], int w[])
{
  int i;
  FILE *f;
  for (i=0;i<7;i++) r[i]=0;
  for (i=0;i<MAXTHREADS;i++) t[i]=w[i]=0;
  f = fopen("log.txt","r");
  if (f == NULL) {
    fprintf(stderr,"Error, could not open log.txt, Good bye\n");
#ifdef WIN32
    ExitProcess(0);
#else
    exit(0);
#endif
  }
  return f;
}

/***** Invalidline *******************
  called by parsetheline when the line could
  not be parsed.
***********************************/
void Invalidline(char line[], int num)
{
     fprintf(stderr,"Error, invalid line %d: %s\n",num, line);
     fprintf(stderr,"Good bye\n");
#ifdef WIN32
     ExitProcess(0);
#else   
     exit(0);
#endif
}
  
/********* PARSETHELINE *******************
  parses a line of the file, getting thread, record, and time
  It returns a code for the operation
******************************************/
int parsetheline(char *line, int *thread, int *record, float *tm, int num)
{
  int len;
  char words[7][80];
  int i;
  char *keywords[]={"Thread","record","at","time"};
  char *opwords[]={"wants","starts","is"};
  len = strlen(line);
  if (len < 40 || len > 78)  Invalidline(line,num);
  sscanf(line,"%s %d %s %s %s %s %d %s %s %f",
	 words[0],thread,words[1], words[2], words[3], words[4],
         record, words[5], words[6], tm);
  if (strcmp(words[0],keywords[0])!=0) Invalidline(line, num);
  if (strcmp(words[4],keywords[1])!=0) Invalidline(line, num);
  if (strcmp(words[5],keywords[2])!=0) Invalidline(line, num);
  if (strcmp(words[6],keywords[3])!=0) Invalidline(line, num);
  if (*record < 1 || *record > 6) Invalidline(line, num);
  if (*thread < 1 || *thread >= MAXTHREADS) Invalidline(line, num);
  i=0;
  while(i<3) {
      if (strcmp(words[1],opwords[i])==0)
	break;
      else i++;
  }
  return i;
}

/********PROCESSTHEFILE *****************
  Reads a line at a time and makes sure that all
  events are internally consistent, displaying
  an error message if an error is found.
  It returns the number of lines read.
******************************************/
int ProcessTheFile(int threads[], int records[], int wants[], float wanttimes[], FILE *fp)
{
  char line[255];
  int linenum = 0;
  int threadnum, recordnum;
  float timeval;
  int op;
  int i;

  while (fgets(line,254,fp)!=NULL) { /* read a line */
    linenum++;
    op = parsetheline(line, &threadnum, &recordnum, &timeval, linenum); 
    switch (op) {
    case OP_WANT:
      if (wants[threadnum] != 0) {
	fprintf(stderr,"Error, thread %d wants to update two records at once at line%d\n",
               threadnum, linenum);
      }
      wants[threadnum]=recordnum;
      /* if the record is not currently being updated, save the want time */
      if (records[recordnum] == 0) wanttimes[threadnum] = timeval;
      else wanttimes[threadnum] = (float)-1;
      for (i=0;i<MAXTHREADS;i++) {
	if (wants[i]==recordnum && i != threadnum) /* another thread is waiting
                                                      to update this record */
	  wanttimes[threadnum] = (float)-1;
	break;
      }                                                      
      break;
    case OP_START:
      if (records[recordnum] != 0) {
	fprintf(stderr,"Error, two threads are reading record %d at the same time at line %d\n",
		recordnum, linenum);
      }
      if (wants[threadnum] != recordnum) {
	fprintf(stderr,"Error, thread %d starts updating record %d without wanting to at line %d\n",
                 threadnum, recordnum, linenum);
      }
      if (threads[threadnum] != 0) {
	fprintf(stderr,"Error, thread %d is already updating a record at line %d\n",
                       threadnum, linenum);
      }
      if (wanttimes[threadnum] > 0) {
	if ((timeval - wanttimes[threadnum]) > 0.5)
	  fprintf(stderr,"Error, thread %d had to wait too long for record %d at line %d\n",
		  threadnum, recordnum, linenum);
      }
      wants[threadnum]=0;
      records[recordnum]=threadnum;
      threads[threadnum]=recordnum;
      break;
    case OP_FINISH:
      if (records[recordnum] != threadnum) {
	fprintf(stderr,"Error, thread %d finished updating record %d without starting at line %d\n",
		threadnum, recordnum, linenum);
      }
      if (threads[threadnum] != recordnum) {
        fprintf(stderr,"Error, thread %d finished updating a record without starting\n",
                threadnum);
      }
      records[recordnum] = 0;
      threads[threadnum]=0;
      break;

    default: fprintf(stderr,"Error, bad operation at line %d\n",linenum);
    } /* end of switch */
    /*   fprintf(stderr,"op is %d, thread %d, record %d, time %4.2f\n",
	 op, threadnum, recordnum, timeval); */
  }
  /* end of while loop */
  for (i=0;i<7;i++) if (records[i] != 0)
    fprintf(stderr,"Error, not all updates were completed\n");
  fclose(fp);
  return linenum;
}

     
/*********** MAIN ****************/
int main()
{
  FILE *fp;
  int records[7]; /* nonzero if that record is being updated */
  int threads[MAXTHREADS]; /* nonzero if that thread is updating a record */
  int wants[MAXTHREADS];  /* nonzero if that thread wants to update a record */
  float wanttimes[MAXTHREADS];
  int numlines;

  fp = Init(threads, records, wants);
  numlines = ProcessTheFile(threads, records, wants, wanttimes, fp);
  fprintf(stderr,"%d records were processed from log.txt\n",numlines);
  return 0;
}  

          

