/*
 * scorertoset.c
 * Copyright Guy Albertelli II 1997
 */
#include <stdio.h>
#include <ctype.h>
#include "Capa/capaCommon.h"
#include "scorer.h"

#ifdef __sun
#include <unistd.h>  /* lockf() */
#endif

#ifdef   F_DBUG
extern FILE *dfp; 
#endif

/********************************************************** file locking */
int flockstream_sh(FILE *sp)
{
  int fd = fileno(sp);
#ifdef __sun
  return ( lockf(fd,F_LOCK, 0L) );
#else
  return (flock(fd,LOCK_SH));
#endif
}

int flockstream(FILE *sp)
{
  int fd = fileno(sp);
#ifdef __sun
  return ( lockf(fd,F_LOCK, 0L) );
#else
  return (flock(fd,LOCK_EX));
#endif
}

int funlockstream(FILE *sp)
{
  int fd = fileno(sp);
#ifdef __sun
  return ( lockf(fd,F_ULOCK, 0L) );
#else
  return (flock(fd,LOCK_UN));
#endif
}

char* capa_malloc(unsigned int num,unsigned int sz)
{
  char *p;
  p = calloc(num, sz);
  return (p);
}


/****************************************************** Database Entry */
int /* RETURNS: error code */
scorer_set_entry(entry, student_number, set, offset) 
T_entry   *entry;          /* pointer to entry structure to fill in */
char      *student_number;
int        set;
long       offset;
{
   FILE    *fp;
   int      errcode=0;
   int      len;
   char     filename[FILE_NAME_LENGTH];
   char     a_line[MAX_LINE_LENGTH];

   sprintf(filename,"records/set%d.sb",set);
   if ((fp=fopen(filename,"r+"))==NULL)
     {
      printf("Error: can't open %s\n",filename);  return (-1);
     }
   sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,
	   entry->tries);
   len = strlen(a_line);
   flockstream(fp);
   fseek(fp,offset,0);
   if (!fwrite(a_line,len,1,fp) ) 
     {
       printf("Error writing data to file\n");
       errcode= (-1);
     }
   funlockstream(fp);
   fclose(fp);
   return (errcode);
}

/**************************************************** Get db entry*/

long /* RETURNS: byte offset to start of record, 0 if error,
                    -offset if not found & newly created  */
scorer_get_entry(entry, student_number, set) 
T_entry   *entry;           
char      *student_number;  
int        set;            
{
   char      filename[FILE_NAME_LENGTH];
   FILE     *fp;
   int       len, nq;          
   char     *ans_p, *tries_p,oneline[MAX_LINE_LENGTH],fmtbuf[MAX_LINE_LENGTH];
   long      offset=0, next_r;             
   int       ii, done, found=0;
   char      a_sn[MAX_STUDENT_NUMBER+1];
   
   sprintf(filename,"records/set%d.sb",set); 
   if ((fp=fopen(filename,"r"))==NULL) 
     {
      printf("Error: can't open %s\n",filename);
      return (0); 
     }
   sprintf(entry->student_number,"%s",student_number);
   sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
   flockstream(fp);
   fgets(oneline,MAX_LINE_LENGTH-1,fp); 
   len = strlen(oneline); sscanf(oneline,"%d",&nq);
   ans_p = capa_malloc(nq+1,1); tries_p = capa_malloc(3*nq,1);
   fgets(oneline,MAX_LINE_LENGTH-1,fp); /* skip weight line */
   fgets(oneline,MAX_LINE_LENGTH-1,fp); /* hand grading */
   done = 0;
   while(!done) 
     {
       done = !fgets(oneline,MAX_LINE_LENGTH-1,fp); len = strlen(oneline);
       if( !done ) 
	 {
	   sscanf(oneline,fmtbuf,a_sn);
	   if( !strncasecmp(a_sn,student_number,MAX_STUDENT_NUMBER) ) 
	     { /* Found */
	       next_r = ftell(fp); offset = next_r - len; done = 1; found = 1;
	     }
	 } 
       else 
	 {
	   fseek(fp,0L,SEEK_END);
	   offset = ftell(fp);  /* last byte, if last bye is cr, back up one */
	   fseek(fp,-1L,SEEK_END);
	   while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
	   offset = offset +2; /* last char and cr */
	   found = 0; done=1;
	 }
     }
   funlockstream(fp); fclose(fp);
   if(!found) 
     {
       for(ii=0;ii<nq;ii++) 
	 { /* Initialize answer string and tries string */
	   ans_p[ii] = '-'; tries_p[3*ii] = ' '; tries_p[3*ii + 1] = '0';
	   if(ii < nq-1) tries_p[3*ii + 2] = ',';
	 }
       entry->answers = ans_p;
       entry->tries   = tries_p;
       entry->e_probs = nq;
       if (scorer_set_entry(entry,student_number,set,offset)==-1)
	 offset=0;
       offset = -offset;
     } 
   else 
     {
       sprintf(fmtbuf, "%%%dc",nq);
       sscanf(oneline + MAX_STUDENT_NUMBER+1,fmtbuf,ans_p);
       sprintf(fmtbuf, "%%%dc",(3*nq-1));
       sscanf(oneline + MAX_STUDENT_NUMBER+1+nq+1,fmtbuf,tries_p);
       entry->answers = ans_p;
       entry->tries   = tries_p;
       entry->e_probs = nq;
     }
   return (offset);
}

void initScreen(void)
{
  printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
  printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
  printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
  printf("Covert form Scorer output to CAPA set file and score. ");
  printf("Arch %s, Version %s.%s\n",ARCHSTR,MAJORVER,MINORVER);
}

int getSetId()
{
  int setId;
  printf("Please enter the Set Id number:");
  scanf("%d",&setId);
  return setId;
}

FILE *openInputFile(int setId)
{
  char filename[MAX_LINE_LENGTH];
  FILE *inputFile;

  sprintf(filename,"records/scorer.output.%d",setId);
  inputFile=fopen(filename,"r");
  if (inputFile==NULL)
    {
      fprintf(stderr,"%s not found\n",filename);
      exit(-1);
    }
  return inputFile;
}

int getHeader(FILE *inputFile, int setId, Question questions[MAX_QUEST])
{
  char class[MAX_LINE_LENGTH],set[MAX_LINE_LENGTH],flags[MAX_LINE_LENGTH],buf,
    numQuestions[MAX_LINE_LENGTH];
  int i=0,done=FALSE;
  float pauseTime;

  fscanf(inputFile,"%s %s %s %s %f",class,set,numQuestions,flags,&pauseTime);
  printf("%s %s %s %s %f\n",class,set,numQuestions,flags,pauseTime);

  fscanf(inputFile,"%c",&buf);
  while(!done)
    {
      buf=fgetc(inputFile);
      switch (buf)
	{
	case '\n':
	  done=TRUE;
	  break;
	case ' ':
	  break;
	default:
	  questions[i].type=buf;
	  buf=fgetc(inputFile);
	  questions[i].points=(int)(buf-'0');
	  buf=fgetc(inputFile);
	  questions[i].leafs=(int)(buf-'0');
	  i++;
	  break;
	}
    }
  return i;
}

int getGradingMethod(void)
{
  int done=FALSE,i;
  while(!done)
    {
      printf("\t\t%d for Capa Standard Method\n",CAPA_METHOD);
      printf("\t\t%d for Lenient Method\n",LENIENT_METHOD);
      printf("\t\t%d for Strict Method\n",STRICT);
      printf("Which grading method?");
      
      scanf("%d",&i);
      switch(i)
	{
	case CAPA_METHOD:
	case LENIENT_METHOD:
	case STRICT:
	  done=TRUE;
	  break;
	default:
	  fprintf(stderr,"Please choose one of\n");
	  break;
	}
    }
  return i;
}

void processFile(FILE *inputFile,Question questions[MAX_QUEST],int setId,
		 int gradingMethod,int numQuestions)
{
  T_entry grade;
  char studentNumber[MAX_STUDENT_NUMBER+1],name[MAX_NAME_CHAR+1];
  int offset,score,section,buf,i,numRight,points=0,leafs,processed=0,unit;

  printf("Processing");
  while(fscanf(inputFile,"%s",studentNumber)!=EOF)
    {
      processed++;
      if (processed%100==1) { printf("%d",processed-1); }
      printf(".");
      fflush(stdout);
      if ((offset = scorer_get_entry(&grade,studentNumber,setId))==0)
	{
	  fprintf(stderr,"Please create the set%d.sb file\n",setId);
	  exit(-1);
	}
      fscanf(inputFile,"%30c",name);
      fscanf(inputFile,"%s",grade.answers); 
      fscanf(inputFile,"%d",&score);
      fscanf(inputFile,"%d",&section);
      if ( (grade.e_probs != strlen(grade.answers)) 
	   || 
	   (strlen(grade.answers) != numQuestions))
	{
	  fprintf(stderr,"There is a disagreement in the number of problems");
	  fprintf(stderr,"\nNumQuestions:%d\n",numQuestions);
	  fprintf(stderr,"strlen(grade.answers):%d\n",strlen(grade.answers));
	  fprintf(stderr,"grade.answers:%s\n",grade.answers);
	  fprintf(stderr,"grade.e_probs:%d\n",grade.e_probs);
	  fprintf(stderr,"The set.sb file may have bad entries, please\n");
	  fprintf(stderr,"check the file and fix the error.\n");
	  exit(-1);
	}
      buf='\0';
      while(buf!='\n')
	{
	  buf=fgetc(inputFile);
	}
#ifdef DEBUG
      printf("%d %d\n",numQuestions,strlen(grade.answers));
#endif /*DEBUG*/
      for(i=0;i<numQuestions;i++)
	{
	  switch(questions[i].type)
	    {
	    case ONE_OUT_OF_8:
	    case SINGLE_DIGIT:
	      numRight= (int) (grade.answers[i]-'0');
	      score=numRight*questions[i].points;
	      grade.answers[i]='0'+(char)score;
	      break;
	    case STRING_MATCH:
	      /*for STRING_MATCH the score is stroed as the NumRight*/
	      numRight= (int) (grade.answers[i]-'0');
	      score=numRight;
	      grade.answers[i]='0'+(char)score;
	      break;
	    case GLE:
	    case TF:
	    case N_OUT_OF_M:
	      numRight=(int) (grade.answers[i]-'0');
	      leafs=questions[i].leafs;
	      points=questions[i].points;
	      unit=(int)ceil((double)points/(double)leafs);
	      if (unit==0) unit=points;
	      switch (gradingMethod)
		{
		case CAPA_METHOD:
		  score=points-(2*unit*(leafs-numRight));
		  break;
		case LENIENT_METHOD:
		  score=points-(unit*(leafs-numRight));
		  break;
		case STRICT:
		  if (numRight==leafs) score=points;
		  else score=0;
		  break;
		default:
		  fprintf(stderr,"Unknown grading Method. %d\n",gradingMethod);
		  break;
		}
	      if (score<0)
		score=0;
	      grade.answers[i]='0'+(char)score;
	      break;
	    case ASSIGNED:
	      /*
	       *grade.answers already has the correct number of points. 
	       *i.e whatever the scorer.output file had in it and was put in
	       *grade.
	       */
	      break;
	    default:
	      fprintf(stderr,"Unknown question type %c\n",questions[i].type);
	      break;
	    }
	}
      for(i=0;i<strlen(grade.answers);i++)
	{
	  grade.tries[3*i]=' ';
	  grade.tries[3*i+1]='1';
	  grade.tries[3*i+2]=',';
	}
      grade.tries[3*i-1]='\0';
      grade.answers[i]='\0';
#ifdef DEBUG
      printf("%s\n",studentNumber);
#endif /*DEBUG*/
      if (scorer_set_entry(&grade,studentNumber,setId,abs(offset))==-1)
	{
	  fprintf(stderr,"Please create the set%d.sb file\n",setId);
	  exit(-1);
	}
    }
  printf("\nProcessed %d results\n",processed);
}

int main(void)
{
  FILE * inputFile;
  int setId,gradingMethod,numQuestions;
  Question questions[MAX_QUEST];

  initScreen();
  setId=getSetId();
  inputFile=openInputFile(setId);
  numQuestions=getHeader(inputFile,setId,questions);
  gradingMethod=getGradingMethod();

  processFile(inputFile,questions,setId,gradingMethod,numQuestions);

  printf("\nProcessing completed. Look in records/set%d.sb for results.\n",
	 setId);
  return 0;
}
