/* convert scorer.output files to set.db files
   Copyright (C) 1992-2000 Michigan State University

   The CAPA system 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.

   The CAPA system 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 the CAPA system; see the file COPYING.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   As a special exception, you have permission to link this program
   with the TtH/TtM library and distribute executables, as long as you
   follow the requirements of the GNU GPL in regard to all of the
   software in the executable aside from TtH/TtM.
*/

/*
 * scorertoset.c
 * Guy Albertelli II 1997
 */
#include <stdio.h>
#include <ctype.h>
#include <tcl.h>
#include <tk.h>
#include "pProj/capaCommon.h"
#include "scorer.h"

/**************************************************Set Database Entry */
int scorer_set_entry(ClientData notused, Tcl_Interp *interp, int argc, char** argv)
{
   FILE    *fp;
   int      fd;
   int      errcode=TCL_OK;
   int      done,len,new_len,item_cnt;
   long     next_r, orig_size, new_size, big_buf_size;
   char     filename[FILE_NAME_LENGTH];
   char     *a_line, tmpline[MAX_LINE_LENGTH], errorline[MAX_LINE_LENGTH], 
     tmp_sn[MAX_STUDENT_NUMBER+1], fmtbuf[SMALL_LINE_BUFFER];
   char    *big_buf;
   char    *student_number,*answers,*tries;
   int      set;
   long     offset;
   
   if (argc != 6) {
     Tcl_SetResult(interp,"usage is: studentNumber set fileOffset answers tries",
		   TCL_VOLATILE);
     return TCL_ERROR;
   }
   student_number=argv[1];
   set=atoi(argv[2]);
   offset=atoi(argv[3]);
   answers=argv[4];
   tries=argv[5];

   offset=(offset<0)?-offset:offset;

   sprintf(filename,"records/set%d.sb",set);
   if ((fp=fopen(filename,"r+"))==NULL) {
     sprintf(errorline,"Error: can't open %s\n",filename);
     Tcl_SetResult(interp,errorline,TCL_VOLATILE);
     return TCL_ERROR;
   }
   a_line=capa_malloc(strlen(tries)*5+MAX_STUDENT_NUMBER,1);
   sprintf(a_line,"%s %s,%s\n",student_number,answers,tries);
   new_len = strlen(a_line);
   sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
   flockstream(fp);
   fseek(fp,0L,SEEK_END);
   orig_size = ftell(fp);
   big_buf_size = orig_size + new_len;
   big_buf = capa_malloc(big_buf_size,1);
   fseek(fp,0L,SEEK_SET); /* rewind to beginning of file */
   fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* skip weight line, including \n */
   fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* hand grading */
   done = 0;
   while(!done) {
     done = !fgets(tmpline,TMP_LINE_LENGTH-1,fp); len = strlen(tmpline);
     if( !done ) {
       sscanf(tmpline,fmtbuf,tmp_sn);
       if( !strncasecmp(tmp_sn,student_number,MAX_STUDENT_NUMBER) ) { /* Found */
         next_r = ftell(fp); offset = next_r - len; done = 1;
         item_cnt=fread(big_buf,1,big_buf_size, fp); /* read remaining lines into buffer */
         if(item_cnt >= 0 ) { /* no error reading remaining lines */
           big_buf[item_cnt]=0;   /* terminate the buffer, for sure */
           fseek(fp,offset,SEEK_SET);  /* reposition file pointer to the record */
           if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
	     sprintf(errorline,"Error writing data to file: %s\n",filename);
	     Tcl_SetResult(interp,errorline,TCL_VOLATILE);
             errcode= TCL_ERROR;
           }
	   if (item_cnt != 0) {
	     if (!fwrite(big_buf,item_cnt,1,fp) ){/*write out the remainings*/
	       sprintf(errorline,"Error writing data to file: %s\n",filename);
	       Tcl_SetResult(interp,errorline,TCL_VOLATILE);
	       errcode= TCL_ERROR;
	     }
	   }
	   new_size = ftell(fp);
	   if(new_size < orig_size ) {
	     fd = fileno(fp);
	     ftruncate(fd,new_size);
	   }
         }
       }
     } else { /* end of file */
       fseek(fp,0L,SEEK_END);
       offset = ftell(fp);  /* last byte, if last byte 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 */
       done=1;
       fseek(fp,offset,SEEK_SET);
       if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
	 sprintf(errorline,"Error writing data to file: %s\n",filename);
	 Tcl_SetResult(interp,errorline,TCL_VOLATILE);
	 errcode= TCL_ERROR;
       }
     }
   }
   fflush(fp);
   funlockstream(fp);   /* <======= unlock the file */
   fclose(fp);
   capa_mfree(big_buf);  /* free up the buffer */
   return (errcode);
}

/**************************************************** Get db entry*/
/* RETURNS: byte offset to start of record, 0 if error,
                    -offset if not found & newly created  
		    
*/
int scorer_get_entry(ClientData notused, Tcl_Interp *interp, int argc, char** argv)
{
   char      filename[FILE_NAME_LENGTH];
   FILE     *fp;
   int       len, nq;          
   char     *ans_p, *tries_p,oneline[MAX_LINE_LENGTH],fmtbuf[MAX_LINE_LENGTH],
     buf[MAX_LINE_LENGTH];
   long      offset=0, next_r;             
   int       ii, done, found=0,set;
   char      a_sn[MAX_STUDENT_NUMBER+1];
   char     *student_number;
   T_entry  *entry;

   if (argc!=3) {
     Tcl_SetResult(interp,"usage is: studentNumber set",TCL_VOLATILE);
     return TCL_ERROR;
   }
   set=atoi(argv[2]);
   student_number=argv[1];
   entry=(T_entry*)capa_malloc(1,sizeof(T_entry));

   sprintf(filename,"records/set%d.sb",set); 
   if ((fp=fopen(filename,"r"))==NULL)  {
     sprintf(buf,"Error: can't open %s\n",filename);
     Tcl_SetResult(interp,buf,TCL_VOLATILE);
     return TCL_ERROR; 
   }
   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;
   }
   sprintf(buf,"%ld;%d;%s;%s",offset,entry->e_probs,entry->answers,entry->tries);
   Tcl_SetResult(interp,buf,TCL_VOLATILE);
   return TCL_OK;
}

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 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);
}
