/* interfaces to the C portions of CAPA for grader
   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.
*/

/*
 * grader.funct.c
 * Guy Albertelli II 1996
 */
#include <stdio.h>
#include <tk.h>
#include <pProj/capaCommon.h>
#include <grader.h>
#include <common.h>
#include <ctype.h>
#include <time.h>

/*
 * used to remeber the weights and partial credit from a parse for when 
 * setting a DBHeader
 */

/* Reads a header form the file and sets up the time and date variables
 */ 
int capaGetHeader(ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  T_header header;
  char * setNumber,buf[BUFFER_SIZE],*sectionNumber,dateStr[BUFFER_SIZE];
  int set,section,result;


  setNumber=Tcl_GetVar(interp,"gSetLoad",TCL_GLOBAL_ONLY);
  sectionNumber=Tcl_GetVar(interp,"gSectionLoad",TCL_GLOBAL_ONLY);

  if ( setNumber[0] == '\0' )  return TCL_OK;
  if ( sectionNumber[0] == '\0' )  return TCL_OK;

  set=atoi(setNumber);
  section=atoi(sectionNumber);

  result=capa_get_header(&header,set);

  if ( result == -1 )
    {
      Tcl_ResetResult(interp);
      Tcl_AppendElement(interp,"0");
      return TCL_OK;
    }

  result=capa_get_date(CHECK_OPEN_DATE,NULL,section,set,dateStr);
  if (result<0) {
    Tcl_ResetResult(interp);
    Tcl_AppendElement(interp,"-1");
    return TCL_OK;
  }
  sscanf(dateStr,"%10c",buf);
  buf[10]='\0';
  Tcl_SetVar(interp,"gOpenDate",buf,TCL_GLOBAL_ONLY);
  sscanf((dateStr)+11,"%5c",buf);
  buf[5]='\0';
  Tcl_SetVar(interp,"gOpenTime",buf,TCL_GLOBAL_ONLY);

  result=capa_get_date(CHECK_DUE_DATE,NULL,section,set,dateStr);
  if (result<0) {
    Tcl_ResetResult(interp);
    Tcl_AppendElement(interp,"-1");
    return TCL_OK;
  }
  sscanf(dateStr,"%10c",buf);
  buf[10]='\0';
  Tcl_SetVar(interp,"gDueDate",buf,TCL_GLOBAL_ONLY);
  sscanf((dateStr)+11,"%5c",buf);
  buf[5]='\0';
  Tcl_SetVar(interp,"gDueTime",buf,TCL_GLOBAL_ONLY);

  result=capa_get_date(CHECK_ANS_DATE,NULL,section,set,dateStr);
  if (result<0) {
    Tcl_ResetResult(interp);
    Tcl_AppendElement(interp,"-1");
    return TCL_OK;
  }
  sscanf(dateStr,"%10c",buf);
  buf[10]='\0';
  Tcl_SetVar(interp,"gAnswerDate",buf,TCL_GLOBAL_ONLY);
  sscanf((dateStr)+11,"%5c",buf);
  buf[5]='\0';
  Tcl_SetVar(interp,"gAnswerTime",buf,TCL_GLOBAL_ONLY);
  
  Tcl_ResetResult(interp);
  Tcl_AppendElement(interp,"1");
  capa_mfree(header.weight);
  capa_mfree(header.partial_credit);
  return TCL_OK;
}

/* get the information for all of the students in the current section
 * and puts them into the listbox.
 * Arguments: the name of the variable the widget name of the listbox
 * is in.
 */
int capaGetStudents(ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  T_student *headStudent,*currentStudent;
  int result,section,set,setScore,maxScore,num=0;
  char buf[BUFFER_SIZE],buf2[BUFFER_SIZE],*answers,*listWidget;
  
  section=atoi(Tcl_GetVar(interp,"gSectionLoad",TCL_GLOBAL_ONLY));
  set=atoi(Tcl_GetVar(interp,"gSetLoad",TCL_GLOBAL_ONLY));
  
  if ( (listWidget = Tcl_GetVar(interp,argv[1],
				 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG)) == NULL)
    {
      fprintf(stderr,"Tcl_GetVar error\n");
      fprintf(stderr,"%s\n",interp->result);
      return TCL_ERROR;
    }

  result=capa_sorted_section(&headStudent,section);

  if (result == -1 || result == 0)
    {
      Tcl_Eval(interp,"displayError \"Invalid section\"");
      Tcl_ResetResult(interp);
      return TCL_OK;
    }

  currentStudent=headStudent;

  while(currentStudent!=NULL)
    {
      num++;
      setScore = capa_get_score(currentStudent->s_sn,set,&maxScore,&answers);
      if ( setScore == -1 ) setScore=0;
      sprintf(buf,"%s %s   %2d/%2d      %2d      ",currentStudent->s_nm,
	      currentStudent->s_sn,setScore,maxScore,
	      capa_PIN(currentStudent->s_sn,set,0));
      
      capaPrepareBuffer(buf,buf2,0);
      sprintf(buf,"%s insert end \"%s\" ",listWidget,buf2);
      
      if (Tcl_Eval(interp,buf) != TCL_OK)
	{
	  fprintf(stderr,"Tcl_Eval error\n");
	  fprintf(stderr,"%s\n",interp->result);
	  free_students(headStudent);
	  return TCL_ERROR;
	}
      
      if (!(num%10)) {
	if (Tcl_Eval(interp,"update") != TCL_OK)
	  {
	    fprintf(stderr,"Tcl_Eval error\n");
	    fprintf(stderr,"%s\n",interp->result);
	    free_students(headStudent);
	    return TCL_ERROR;
	  }
      }
      currentStudent=currentStudent->s_next;
      capa_mfree(answers);
    }

  free_students(headStudent);
  if (!(num%5)) {
    if (Tcl_Eval(interp,"update") != TCL_OK)
      {
	fprintf(stderr,"Tcl_Eval error\n");
	fprintf(stderr,"%s\n",interp->result);
	return TCL_ERROR;
      }
  }
  return TCL_OK;
}

/* searches for the section a student is in by either name or number
 * Arguments: one of (name or number) and then the name or number to
 * search for.
 */
int capaFindSection(ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{ 
  int   errorNm;
  char  searchStr[MAX_NAME_CHAR+1],buf[MAX_NAME_CHAR+1];
  T_student student;
  
  switch(argv[1][1])
    {
      case 'a':
	strncpy(searchStr,argv[2],MAX_NAME_CHAR+1);
	if ( (errorNm = capa_student_byname(searchStr,&student ))==0 ) 
	  {
	    sprintf(buf,"%d",0);
	  }
	else
	  {
	    sprintf(buf,"%d",student.s_sec);
	  }
	break;
    case 'u':
	strncpy(searchStr,argv[2],MAX_NAME_CHAR+1);
	if ( (errorNm = capa_get_student(searchStr,&student ))==0 ) 
	  {
	    sprintf(buf,"%d",0);
	  }
	else
	  {
	    sprintf(buf,"%d",student.s_sec);
	  }      
      break;
    default:
      break;
    }
  Tcl_ResetResult(interp);
  Tcl_AppendElement(interp,buf);
  return TCL_OK;
}

/* makes a student report
 * Arguments: the student number
 */
int capaGetReportInfo(ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  char          studentNumber[MAX_STUDENT_NUMBER+1], 
                lineOne[BUFFER_SIZE],*wgt,*partialCredit;
  T_header      header;
  T_entry       entry;
  int		ii, setIdx, setScores, setValids, 
                totalSet, totalProbs, neverLogin;
  int           termScores, termValids;

  strncpy(studentNumber,argv[1], MAX_STUDENT_NUMBER+1);
  totalSet  = howManySetDBFile();

  Tcl_ResetResult(interp);

  for(termScores=0,termValids=0,setIdx=1;setIdx <= totalSet ;setIdx++) 
    {
      capa_get_entry(&entry, studentNumber, setIdx);
      totalProbs = entry.e_probs;
      if( capa_get_header( &header, setIdx ) == -1 ) 
	{
	  Tcl_ResetResult(interp);
	  Tcl_AppendElement(interp,"File Error");
	  return TCL_OK;
	}
      wgt=header.weight;
      partialCredit=header.partial_credit;

      for(setScores=0, setValids=0,neverLogin=1,ii=0;ii<totalProbs;ii++) 
	{
	  switch(entry.answers[ii]) 
	    { 
	    case 'Y': case 'y':  
	      neverLogin = 0;
	      setScores    += (wgt[ii] - '0'); 
	      setValids    += (wgt[ii] - '0');  
	      break; 
	    case '-': case 'N': case 'n': 
	      setValids    += (wgt[ii] - '0');  
	      break; 
	    case 'E': case 'e':
	      break;
	    default : 
	      if( entry.answers[ii] >= '0' && entry.answers[ii] <= '9' ) 
		{
		  setScores    += (entry.answers[ii] - '0');
		  setValids    += (wgt[ii] - '0');
		  neverLogin = 0;
		} 
	      break;
	    } 
	}
      entry.answers[totalProbs]='\0'; /* get rid of last unknown chars */
      entry.tries[3*totalProbs]='\0';
      termScores += setScores;
      termValids += setValids;
    
      if(neverLogin) 
	{
	  sprintf(lineOne,"%3d   -/%3d %s\n",setIdx,setValids,entry.answers);
	} 
      else 
	{
	  sprintf(lineOne,"%3d %3d/%3d %s\n",setIdx,setScores,setValids,entry.answers);
	}
      Tcl_AppendResult(interp,lineOne,NULL);
      sprintf(lineOne,"           %s\n",entry.tries);
      Tcl_AppendResult(interp,lineOne,NULL);
      capa_mfree(entry.answers);
      capa_mfree(entry.tries);
    }
  sprintf(lineOne,"========================\n   Total = %3d/%3d",termScores,termValids);
  Tcl_AppendResult(interp,lineOne,NULL);
  capa_mfree(header.weight);
  capa_mfree(header.partial_credit);
  return TCL_OK;
}

int compare_string( a, b)
char *a, *b;
{
  return( strcasecmp(a, b) );
}

/* Builds a set summary
 * Argument: the filename to write to
 */
int capaGetSetSummary(ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{   
  T_student *stuPtr, *currentStudentPtr;
  T_header   a_header;
  int        section, set;
  int        studentCount,currentStudent, setScores, validScores;
  char      fmt1[64], fmt2[64],buf[BUFFER_SIZE],*str1="Student Name",*str2=" ";
  char      *answersPtr;
  FILE      *outputFile;

  section=atoi(Tcl_GetVar(interp,"gSectionLoad",TCL_GLOBAL_ONLY));
  set=atoi(Tcl_GetVar(interp,"gSetLoad",TCL_GLOBAL_ONLY));
  Tcl_ResetResult(interp);

  studentCount = capa_sorted_section(&stuPtr, section);
  if( studentCount > 0 ) 
    {
      if( capa_get_header( &a_header, set) == -1 ) 
	{
	  Tcl_Eval(interp,"displayerror \"Cannot open set.db file\"");
	  Tcl_ResetResult(interp);
	  free_students(stuPtr);
	  return TCL_OK;
	}
      capa_mfree(a_header.weight);
      capa_mfree(a_header.partial_credit);
      sprintf(fmt1,"%%-%ds\t%%%ss\t Score\n", MAX_NAME_CHAR,a_header.num_questions);

      outputFile=fopen(argv[1],"w");
      fprintf(outputFile, "Section %-3d, Set %-3d Score Report\n",section, set);

      fprintf(outputFile, fmt1,str1,str2);

      sprintf(fmt1,"%%-%ds\t%%s\t  -/%%3d\n", MAX_NAME_CHAR);
      sprintf(fmt2,"%%-%ds\t%%s\t%%3d/%%3d\n", MAX_NAME_CHAR);

      for(currentStudentPtr=stuPtr,currentStudent=0;currentStudentPtr;
	  currentStudentPtr=currentStudentPtr->s_next,currentStudent++) 
	{
	  sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	  Tcl_Eval(interp,buf);
	  if( (setScores = capa_get_score(currentStudentPtr->s_sn,
					  set,&validScores,&answersPtr) ) == -2 ) 
	    {
	      Tcl_Eval(interp,"displayerror \"Cannot open set.db file\"");
	      Tcl_ResetResult(interp);
	      free_students(stuPtr);
	      capa_mfree(answersPtr);
	      fclose(outputFile);
	      return TCL_OK;
	    }
	  if( setScores < 0 ) 
	    {
	      fprintf(outputFile,fmt1,currentStudentPtr->s_nm,answersPtr,validScores );
	    } 
          else 
	    {
	      fprintf(outputFile,fmt2,currentStudentPtr->s_nm,answersPtr,setScores,validScores );
	    }
	  capa_mfree(answersPtr);
	}
      free_students(stuPtr);
      fclose(outputFile);
    }
  return TCL_OK;
}

/* builds a term summary
 * Arguments: filename
 */
int capaGetTermSummary(ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  T_student *studentPtr, *currentStudentPtr;
  int        section, totalSet;
  int        setIndex, setScores, termScores, validScores, termValids;
  int        studentCount,currentStudent=1;
  char       fmt[64];
  char      *answersPtr,buf[BUFFER_SIZE];
  FILE      *outputFile;

  outputFile=fopen(argv[1],"w");
  section=atoi(Tcl_GetVar(interp,"gSectionLoad",TCL_GLOBAL_ONLY));
  totalSet  = howManySetDBFile();
  Tcl_ResetResult(interp);

  studentCount = capa_sorted_section(&studentPtr, section);
  if( studentCount > 0 ) 
    {
      for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	  currentStudentPtr=currentStudentPtr->s_next,currentStudent++) 
	{
	  sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	  Tcl_Eval(interp,buf);
	  sprintf(fmt,"%%-%ds\n", MAX_NAME_CHAR);
	  fprintf(outputFile,fmt,currentStudentPtr->s_nm);

	  for( termScores = 0, termValids = 0, setIndex = 1; setIndex <= totalSet; setIndex++) 
	    {
	      if( (setScores = capa_get_score(currentStudentPtr->s_sn,
					      setIndex,&validScores,&answersPtr) ) == -2 ) 
		{
		  sprintf(buf,"displayerror \"Cannot open set%d.db file\"",setIndex);
		  Tcl_Eval(interp,buf);
		  Tcl_ResetResult(interp);
		  capa_mfree(answersPtr);
		  free_students(studentPtr);
		  fclose(outputFile);
		  return TCL_OK;
		}
	      if( setScores < 0 ) 
		{
		  fprintf(outputFile,"Set %-3d\t%s\t  -/%3d\n", setIndex,answersPtr, validScores);
		} 
              else 
		{
		  fprintf(outputFile, "Set %-3d\t%s\t%3d/%3d\n",
			   setIndex,answersPtr,setScores,validScores );
		  termScores += setScores;
		}
	      capa_mfree(answersPtr);
	      termValids += validScores; 
	    }
	  fprintf(outputFile,"\t\t\tTerm Score = %3d/%3d\n", termScores,termValids);
	  fprintf(outputFile, "-----------------------------------------------------------------------\n");
	}
      free_students(studentPtr);
    }
  fclose(outputFile);
  return TCL_OK;
}

extern int Parsemode_f;

/* runs capaParse and puts the results into a text widget
 * Arguments: the name it is registered under (enscriptParse, texParse,
 *            webParse, bubblerParse); what results of the parse to show
 *            0 (Problem Only), 1 (Question and Answer), 2 (Answer Only);
 *            the set number; either (Random or Specific) student; section;
 *            student Number; student Name ; the name of the variable to
 *            find the widget name of the text widget to put the text into
 */
int capaTclParse (ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  extern  char      *EndText_p;
  T_student student;
  Problem_t *headProblem,*currentProblem;
  int numOfQuestions,numAnswers,problemNumber=0;
  int result,i=1,j,length;
  char *buf, *buf2, *temp, *previewText=NULL;
  char lower[32],upper[32],ans[32], unit[32];
  double  targetAns;
  int  c_set;
#ifdef GRADER_UPDATE
  char *update=";update";
#else
  char *update=" ";
#endif

  switch(argv[0][0])
    {
    case 'e':Parsemode_f = ASCII_MODE;break;
    case 't':Parsemode_f = TeX_MODE;break;
    case 'w':Parsemode_f = HTML_MODE;break;
    case 'b':Parsemode_f = BUBBLE_MODE;break;
    default:
      Tcl_ResetResult(interp);
      Tcl_AppendElement(interp,"Invalid call to capaTclParse\n");
      return TCL_ERROR;
      break;
    }

  if ( (previewText = Tcl_GetVar(interp,argv[7],
				 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG)) == NULL)
    {
      fprintf(stderr,"Tcl_GetVar error\n");
      fprintf(stderr,"%s\n",interp->result);
      return TCL_ERROR;
    }
  c_set = atoi(argv[2]);
  switch (argv[3][0])
    {
    case 'R':
      result = capa_pick_student(atoi(argv[4]),&student);
      if (result == -1)
	{
	  buf=capa_malloc(BUFFER_SIZE,1);
	  sprintf(buf,"displayError \"There are no students in section %d.\"",
		  atoi(argv[4]));
	  Tcl_Eval(interp,buf);
	  capa_mfree(buf);
	  Tcl_ResetResult(interp);
	  Tcl_AppendElement(interp,"-1");
	  return TCL_OK;
	}
      
      result = capa_parse(atoi(argv[2]),&headProblem,student.s_sn,&numOfQuestions,NULL);
      break;
    case 'S':
      result = capa_get_student(argv[5],&student);
      if ((result == -1) || (result == 0))
	{
	  buf=capa_malloc(BUFFER_SIZE,1);
	  sprintf(buf,"displayError \"The student %s does not exist.\"",
		  argv[5]);
	  Tcl_Eval(interp,buf);
	  capa_mfree(buf);
	  Tcl_ResetResult(interp);
	  Tcl_AppendElement(interp,"-1");
	  return TCL_OK;
	}
      result = capa_parse(atoi(argv[2]),&headProblem,argv[5],&numOfQuestions,NULL);
      break;
    default:
      Tcl_ResetResult(interp);
      Tcl_AppendElement(interp,"Invalid 2nd argument to capaTclParse\n");
      return TCL_ERROR;
      break;
    }

  if (result==-1)
    {
      Tcl_ResetResult(interp);
      Tcl_AppendElement(interp,"-1");
      return TCL_OK;
    }

  currentProblem=headProblem;
    
  if(argv[1][0] == Q_ANSWER)
    {
      /*
      switch(Parsemode_f)
	{
	case ASCII_MODE:
	  sprintf(buf,"Section %d                               Set %d\n Name: %s    PIN: %d\n\n",
		  student.s_sec, atoi(argv[2]), student.s_nm,
		  capa_PIN(student.s_sn, atoi(argv[2]),0));
	  break;
	case TeX_MODE:
	  sprintf(buf,"{\\bf Section %d\\qquad Set %d}\\\\\\medskip \n{\\bf Name: \
%s\\qquad PIN: %d}\\\\ \\bigskip\n\n", student.s_sec, atoi(argv[2]), student.s_nm,
		  capa_PIN(student.s_sn, atoi(argv[2]),0));
	  break;
	case HTML_MODE:
	  sprintf(buf,"Section %d                               Set %d\n Name: %s    PIN: %d\n\n",
		  student.s_sec, atoi(argv[2]), student.s_nm,
		  capa_PIN(student.s_sn, atoi(argv[2]),0));
	  break;
	case BUBBLE_MODE:
	  break;
	}
      j=capaPrepareBuffer(buf,buf2,0);

      sprintf(buf,"%s insert end \" %s \" header",previewText,buf2);

      if (Tcl_Eval(interp,buf) != TCL_OK)
	{
	  fprintf(stderr,"Tcl_Eval error\n");
	  fprintf(stderr,"%s\n",interp->result);
	  return TCL_ERROR;
	}
     */
    }

  while (currentProblem!=NULL)
    {
      switch (argv[1][0])
	{
	case Q_PROBLEM:
	  if (currentProblem->question) { 
	    length=strlen(currentProblem->question); 
	  } else { 
	    length=0;
	  }
	  buf=capa_malloc(BUFFER_SIZE+(2*length),1);
	  buf2=capa_malloc(BUFFER_SIZE+(2*length),1);
	  if(currentProblem->question) {
	    j=capaPrepareBuffer(currentProblem->question,buf2,0);
	    buf2[j-1]='\n';
	    buf2[j]='\0';
	  } else {
	    buf2[0]='\n';buf2[1]='\0';
	  }
	  if(currentProblem->question) {
	    j=capaPrepareBuffer(currentProblem->question,buf2,0);
	    buf2[j-1]='\n';
	    buf2[j]='\0';
	  } else {
	    buf2[0]='\n';buf2[1]='\0';
	  }

	  switch(Parsemode_f)
	    {
	    case ASCII_MODE:
	      sprintf(buf,"%s insert end \"%s \n-----------\n\n\" problem",
		      previewText,buf2);
	      break;
	    case TeX_MODE:
	      sprintf(buf,"%s insert end \"%s\" problem",previewText,buf2);
	      break;
	    case HTML_MODE:
	      sprintf(buf,"%s insert end \"%s\" problem",previewText,buf2);
	      break;
	    case BUBBLE_MODE:
	      break;
	    }	      
	  if (Tcl_Eval(interp,buf) != TCL_OK)
	    {
	      fprintf(stderr,"Tcl_Eval error\n");
	      fprintf(stderr,"%s\n",interp->result);
	      return TCL_ERROR;
	    }
	  capa_mfree(buf);
	  capa_mfree(buf2);
	  break;
	case Q_PROBLEM_AND_ANSWER:
	  if (currentProblem->question) { 
	    length=strlen(currentProblem->question); 
	  } else { 
	    length=0;
	  }
	  buf=capa_malloc(BUFFER_SIZE+(2*length),1);
	  buf2=capa_malloc(BUFFER_SIZE+(2*length),1);
	  temp=capa_malloc(BUFFER_SIZE+(2*length),1);
	  j=capaPrepareBuffer(currentProblem->question,buf2,0);

	  switch(Parsemode_f)
	    {
	    case ASCII_MODE:
	      sprintf(buf,"%s insert end \"%s \n-----------\n\n\" problem",
		      previewText,buf2);
	      break;
	    case TeX_MODE:
	      sprintf(buf,"%s insert end \"%s\" problem",previewText,buf2);
	      break;
	    case HTML_MODE:
	      sprintf(buf,"%s insert end \"%s\" problem",previewText,buf2);
	      break;
	    case BUBBLE_MODE:
	      break;
	    }
	  if (Tcl_Eval(interp,buf) != TCL_OK)
	    {
	      fprintf(stderr,"Tcl_Eval error\n");
	      fprintf(stderr,"%s\n",interp->result);
	      return TCL_ERROR;
	    }

	  capa_mfree(buf);
	  capa_mfree(buf2);
	  capa_mfree(temp);
	  capaInsertAnswer(currentProblem,interp,previewText);
	  break;
	case Q_ANSWER:
	  print_begin_item(Parsemode_f,interp,previewText,problemNumber+1);
	  capaInsertAnswer(currentProblem,interp,previewText);
	  break;
	}

      currentProblem=currentProblem->next;
      problemNumber++;
    }
  if( ( EndText_p != NULL) ) 
    {
      buf=capa_malloc(BUFFER_SIZE+(2*strlen(EndText_p)),1);
      buf2=capa_malloc(BUFFER_SIZE+(2*strlen(EndText_p)),1);
      temp=capa_malloc(BUFFER_SIZE+(2*strlen(EndText_p)),1);
      sprintf(temp,"%s", EndText_p);
      j=capaPrepareBuffer(temp,buf2,0);
      
      sprintf(buf,"%s insert end \"%s\" answer%s",previewText,buf2,update);
      
      if (Tcl_Eval(interp,buf) != TCL_OK)
        {
	  fprintf(stderr,"Tcl_Eval error 7\n");
	  fprintf(stderr,"%s\n",interp->result);
	  return TCL_ERROR;
	}
      capa_mfree(buf);
      capa_mfree(buf2);
      capa_mfree(temp);
    }
  free_problems(headProblem);
  
  if (result==0) 
    {
      Tcl_ResetResult(interp);
      Tcl_AppendElement(interp,"0");
    }
  else
    {
      buf=capa_malloc(BUFFER_SIZE,1);
      sprintf(buf,"%d",result);
      Tcl_ResetResult(interp);
      Tcl_AppendElement(interp,buf);
      capa_mfree(buf);
    }
  
  return TCL_OK;
}

/* setup gQuestionType for all of the questions
 * Arguments: number of Questions
 */
int capaGetQuestionTypes(ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  int numQuestions,result,i;
  T_header header;
  char buf[BUFFER_SIZE], buf2[BUFFER_SIZE],*weight;

  if (argc != 2) 
    {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"Wrong number of arguments to getQuestionTypes",
		       NULL);
      return TCL_ERROR;
    }
  
  numQuestions=atoi(argv[1]);
  
  result = capa_get_header(&header,atoi(Tcl_GetVar(interp,"gSetLoad",
						   TCL_GLOBAL_ONLY)));
  weight=header.weight;
			   
  if (result == -1)
    {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"capa_get_header returned error.",NULL);
      return TCL_ERROR;
    }
  
  for(i=1;i<=numQuestions;i++)
    {
      sprintf(buf,"%d",i);
      switch(header.partial_credit[i-1])
	{
	case '0':
	  Tcl_SetVar2(interp,"gQuestionType",buf,"autoGrade",TCL_GLOBAL_ONLY);
	  break;
	case '1':
	  Tcl_SetVar2(interp,"gQuestionType",buf,"handGrade",TCL_GLOBAL_ONLY);
	  sprintf(buf,"max%d",i);
	  sprintf(buf2,"%c",weight[i-1]);
	  Tcl_SetVar2(interp,"gAnswer",buf,buf2,TCL_GLOBAL_ONLY);
	  break;
	default:
	  Tcl_ResetResult(interp);
	  Tcl_AppendResult(interp,"Header for the set.db file is incorrect.",
			   NULL);
	  return TCL_ERROR;
	  break;
	}
    }
  capa_mfree(header.weight);
  capa_mfree(header.partial_credit);
  Tcl_ResetResult(interp);
  return TCL_OK;
}

/* setup gAnswer for all of the questions
 * Arguments: number of Questions
 */
T_entry graderEntry;
int capaSetupGAnswer (ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  int numQuestions,i,result;
  char buf[BUFFER_SIZE],buf2[BUFFER_SIZE],*questionType;

  if (argc != 2) 
    {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"Incorrect number of arguments to setup gAnswer",
		       NULL);
      return TCL_ERROR;
    }

  numQuestions=atoi(argv[1]);

  result = capa_get_entry(&graderEntry,Tcl_GetVar2(interp,"gGrading","number",
					     TCL_GLOBAL_ONLY),
			  atoi(Tcl_GetVar(interp,"gSetLoad",TCL_GLOBAL_ONLY)));

  if (result == 0)
    {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"capa_get_entry returned error.",NULL);
      return TCL_ERROR;
    }
  for(i=1;i<=numQuestions;i++)
    {
      sprintf(buf,"%d.tries",i);
      buf2[0]=graderEntry.tries[(i-1)*3];buf2[1]=graderEntry.tries[(i-1)*3+1];
      buf2[2]='\0';
      Tcl_SetVar2(interp,"gAnswer",buf,buf2,TCL_GLOBAL_ONLY);
      sprintf(buf,"%d",i);
      switch(graderEntry.answers[i-1])
	{
	case '-':
	  Tcl_SetVar2(interp,"gAnswer",buf,"-",TCL_GLOBAL_ONLY);
	  break;
	case 'y':
	case 'n':
	case 'E':
	  sprintf(buf2,"%c",graderEntry.answers[i-1]);
	  Tcl_SetVar2(interp,"gAnswer",buf,buf2,TCL_GLOBAL_ONLY);
	  break;
	case 'Y':
	  sprintf(buf2,"%c",graderEntry.answers[i-1]);
	  Tcl_SetVar2(interp,"gAnswer",buf,buf2,TCL_GLOBAL_ONLY);
	  questionType=Tcl_GetVar2(interp,"gQuestionType",buf,
				   TCL_GLOBAL_ONLY);
	  switch(questionType[0])
	    {
	    case 'a':
	      sprintf(buf,"$gGradeCanvas.dash%d configure -state disabled",i);
	      if (Tcl_Eval(interp,buf)!=TCL_OK) 
		return TCL_ERROR;
	      sprintf(buf,"$gGradeCanvas.y%d configure -state disabled",i);
	      if (Tcl_Eval(interp,buf)!=TCL_OK) 
		return TCL_ERROR;
	      sprintf(buf,"$gGradeCanvas.n%d configure -state disabled",i);
	      if (Tcl_Eval(interp,buf)!=TCL_OK) 
		return TCL_ERROR;
	      sprintf(buf,"$gGradeCanvas.e%d configure -state disabled",i);
	      if (Tcl_Eval(interp,buf)!=TCL_OK) 
		return TCL_ERROR;
	      break;
	    case 'h':
	      sprintf(buf,"$gGradeCanvas.hand%d configure -state disabled",i);
	      if (Tcl_Eval(interp,buf)!=TCL_OK) 
		return TCL_ERROR;
	      break;
	    default:
	      Tcl_ResetResult(interp);
	      Tcl_AppendElement(interp,"questionType contains invlaid data in capaSetupGAnswer.");
	      return TCL_ERROR;
	      break;
	    }
	  break;
	default:
	  sprintf(buf2,"%c",graderEntry.answers[i-1]);
	  Tcl_SetVar2(interp,"gAnswer",buf,buf2,TCL_GLOBAL_ONLY);
	  break;
	}
    }

  Tcl_ResetResult(interp);
  return TCL_OK;
}

/* save gAnswer to the set.db file
 * Arguments: number of Questions
 */
int capaSaveGAnswer (ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  T_entry entry;
  int numQuestions,i,result;
  char buf[BUFFER_SIZE],*gAnswer;

  if (argc != 2) 
    {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"Incorrect number of arguments to save gAnswer",NULL);
      return TCL_ERROR;
    }

  numQuestions=atoi(argv[1]);

  entry.answers=capa_malloc(numQuestions+1,1);
  entry.tries=capa_malloc(3*numQuestions+1,1);

  for(i=0;i<numQuestions;i++) {
    sprintf(buf,"%d",i+1);
    gAnswer=Tcl_GetVar2(interp,"gAnswer",buf,TCL_GLOBAL_ONLY);
    if ( gAnswer[0] != graderEntry.answers[i] ) {
      entry.answers[i]=gAnswer[0];
    } else {
      entry.answers[i]='?';
    }
    sprintf(buf,"%d.tries",i+1);
    gAnswer=Tcl_GetVar2(interp,"gAnswer",buf,TCL_GLOBAL_ONLY);
    if (atoi(gAnswer) != atoi(&(graderEntry.tries[i*3]))) {
      sprintf(&(entry.tries[i*3]),"%2d",atoi(gAnswer));
      if (i<numQuestions-1) entry.tries[i*3+2]=',';
    } else {
      entry.tries[i*3]='-';entry.tries[i*3+1]='1';
      if (i<numQuestions-1) entry.tries[i*3+2]=',';
    }
  }
  entry.answers[numQuestions]='\0';
  entry.tries[numQuestions*3]='\0';
  sprintf(entry.student_number,Tcl_GetVar2(interp,"gGrading","number",TCL_GLOBAL_ONLY));
  entry.e_probs=numQuestions;
  result = capa_change_entry(&entry,Tcl_GetVar2(interp,"gGrading","number",
						TCL_GLOBAL_ONLY),
			     atoi(Tcl_GetVar(interp,"gSetLoad",TCL_GLOBAL_ONLY)));

  if (result == -1) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp,"capa_change_entry returned an error.",NULL);
    return TCL_ERROR;
  }

  Tcl_ResetResult(interp);
  return TCL_OK;
}

/* a wrapper for capa_excuse
 * Arguments: set; problem; section
 */
int capaExcuse (ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  int result;

  result = capa_excuse(atoi(argv[1]),atoi(argv[2]),atoi(argv[3]));

  if (result == -1)
    {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"capa_excuse returned error.",NULL);
      return TCL_ERROR;
    }

  return TCL_OK;
}

/* creates a summary report
 * Arguments: the filename
 */
int capaCreateSummary (ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{    
  int          section = atoi(Tcl_GetVar2(interp,"gSummary","section",TCL_GLOBAL_ONLY)), 
    set = atoi(Tcl_GetVar2(interp,"gSummary","set",TCL_GLOBAL_ONLY)); 
  int          studentCount,currentStudent;
  int          setScores, termScores, validScores, termValids;
  int          setIndex, maxSet=0;
  int          whatSection;
  char         fmt[64];
  char         grades[4], sectionChar[4], *answersPtr, buf[BUFFER_SIZE], *who,*which,*sortOne,*sortTwo;
  T_student   *studentPtr, *currentStudentPtr;
  FILE        *outputFile;

  who=Tcl_GetVar2(interp,"gSummary","who",TCL_GLOBAL_ONLY);
  which=Tcl_GetVar2(interp,"gSummary","which",TCL_GLOBAL_ONLY);
  sortOne=Tcl_GetVar2(interp,"gSummary","first",TCL_GLOBAL_ONLY);
  sortTwo=Tcl_GetVar2(interp,"gSummary","second",TCL_GLOBAL_ONLY);
  maxSet=howManySetDBFile();
  if ( ( ( strcmp(which,"upto") == 0 ) 
       &&
       ( ( set <= 0 ) 
	|| 
	( set >= NUM_SET_LIMIT ) ) ) 
      ||
       ( set > maxSet ) )
    {
      sprintf(buf,"displayError \"The set number (%d) doesn't exist.\"",set);
      Tcl_Eval(interp,buf);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"Error",NULL);
      return TCL_ERROR;
    }
  outputFile=fopen(argv[1],"w");
  if ( strcmp(who,"all") == 0 ) 
      whatSection = GET_ALL_SECTIONS;
  else 
      whatSection = section;
  studentCount = capa_get_section(&studentPtr, whatSection);

  if (Tcl_Eval(interp,"updateStatusMessage \"Creating primary sort key\"") != TCL_OK)
    {
      free_students(studentPtr);
      fclose(outputFile);
      return TCL_ERROR;
    }

  if( studentCount > 0 ) 
    {
      switch (sortOne[1]) 
	{
	case 'a': /*BY_NAME*/   
	  for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	      currentStudentPtr=currentStudentPtr->s_next,currentStudent++)
	    {
	      sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	      Tcl_Eval(interp,buf);
	      sprintf(currentStudentPtr->s_key,"%s",currentStudentPtr->s_nm);
	    }
	  break;
	case 'u': /*BY_NUMBER*/  
	  for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	      currentStudentPtr=currentStudentPtr->s_next,currentStudent++)  
	    {
	      sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	      Tcl_Eval(interp,buf);
	      sprintf(currentStudentPtr->s_key,"%s",currentStudentPtr->s_sn);
	    }
	  break;
	case 'e': /*BY_SECTION*/ 
	  for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	      currentStudentPtr=currentStudentPtr->s_next,currentStudent++)  
	    {
	      sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	      Tcl_Eval(interp,buf);
	      sprintf(currentStudentPtr->s_key,"%03d",currentStudentPtr->s_sec);
	    }
	  break;
	case 'r': /*BY_GRADE*/
	  if(strcmp(which,"specific") == 0 ) 
	    {
	      for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
		  currentStudentPtr=currentStudentPtr->s_next,currentStudent++) 
		{
		  sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
		  Tcl_Eval(interp,buf);
		  if( (setScores = capa_get_score(currentStudentPtr->s_sn,set,
						  &validScores,&answersPtr)) == -2 ) 
		    break;
		  if( setScores < 0 ) 
		    sprintf(currentStudentPtr->s_key,"-");
                  else 
		    sprintf(currentStudentPtr->s_key,"%03d",setScores);
		  capa_mfree(answersPtr);
		} 
	    } 
          else 
	    {
	      for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
		  currentStudentPtr=currentStudentPtr->s_next,currentStudent++) 
		{
		  sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
		  Tcl_Eval(interp,buf);
		  for( termScores = 0, termValids = 0, setIndex = 1; 
		       setIndex <= set; setIndex++) 
		    {
		      if( (setScores = capa_get_score(currentStudentPtr->s_sn,setIndex,
						      &validScores,&answersPtr)) >= 0 ) 
			  termScores += setScores;
		      capa_mfree(answersPtr);
		      termValids += validScores; 
		    }
		  sprintf(currentStudentPtr->s_key,"%03d",termScores);
		}
	    }
	  break;
	}
      if (Tcl_Eval(interp,"updateStatusMessage \"Creating secondary sort key\"") != TCL_OK)
	{
	  free_students(studentPtr);
	  fclose(outputFile);
	  return TCL_ERROR;
	}
      switch (sortTwo[1]) 
	{
	case 'a':/*BY_NAME*/    
	  for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	      currentStudentPtr=currentStudentPtr->s_next,currentStudent++)  
	    {
	      sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	      Tcl_Eval(interp,buf);
	      strcat(currentStudentPtr->s_key,currentStudentPtr->s_nm);
	    }
	  break;
	case 'u':/*BY_NUMBER*/  
	  for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	      currentStudentPtr=currentStudentPtr->s_next,currentStudent++)  
	    {
	      sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	      Tcl_Eval(interp,buf);
	      strcat(currentStudentPtr->s_key,currentStudentPtr->s_sn);
	    }
	  break;
	case 'e':/*BY_SECTION*/ 
	  for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	      currentStudentPtr=currentStudentPtr->s_next,currentStudent++)  
	    {
	      sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	      Tcl_Eval(interp,buf);
	      sprintf(sectionChar,"%03d",currentStudentPtr->s_sec); 
	      strcat(currentStudentPtr->s_key,sectionChar);
	    }
	  break;
	case 'r':/*BY_GRADE*/
	  if(strcmp(which,"specific") == 0 ) 
	    {
	      for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
		  currentStudentPtr=currentStudentPtr->s_next,currentStudent++) 
		{
		  sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
		  Tcl_Eval(interp,buf);
		  if( (setScores = capa_get_score(currentStudentPtr->s_sn,set,&validScores,
						  &answersPtr) ) == -2 ) 
		    break;
		  if( setScores < 0 ) 
		    strcat(currentStudentPtr->s_key,"-");
                  else 
		   {
		     sprintf(grades,"%03d",setScores);
		     strcat(currentStudentPtr->s_key,grades);
		   }
		   capa_mfree(answersPtr);
		} 
	    } 
         else 
	   {
	     for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
		 currentStudentPtr=currentStudentPtr->s_next,currentStudent++) 
	       {
		 sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
		 Tcl_Eval(interp,buf);
		 for( termScores = 0, termValids = 0, setIndex = 1; 
		      setIndex <= set; setIndex++) 
		   {
		     if( (setScores = capa_get_score(currentStudentPtr->s_sn,setIndex,
						     &validScores,&answersPtr) ) >= 0 ) 
		       termScores += setScores;
		     capa_mfree(answersPtr);
		     termValids += validScores; 
		   }
		 sprintf(grades,"%03d",termScores);
		 strcat(currentStudentPtr->s_key,grades);
	       }
	   }
	   break;
	}
      if (Tcl_Eval(interp,"updateStatusMessage \"Sorting\"") != TCL_OK)
	{
	  free_students(studentPtr);
	  fclose(outputFile);
	  return TCL_ERROR;
	}
      msort_main(&studentPtr);
      Tcl_ResetResult(interp);

      sprintf(fmt,"%%-%ds\t%%-%ds %%2d\t", MAX_NAME_CHAR,MAX_STUDENT_NUMBER);
      if (Tcl_Eval(interp,"updateStatusMessage \"Creating Report\"") != TCL_OK)
	{
	  free_students(studentPtr);
	  fclose(outputFile);
	  return TCL_ERROR;
	}
      for(currentStudentPtr=studentPtr,currentStudent=1;currentStudentPtr;
	  currentStudentPtr=currentStudentPtr->s_next,currentStudent++) 
	{
	  sprintf(buf,"updateStatusBar %f",(float)currentStudent/(float)studentCount);
	  Tcl_Eval(interp,buf);
	  fprintf(outputFile,fmt,currentStudentPtr->s_nm,currentStudentPtr->s_sn,
		  currentStudentPtr->s_sec);
	  if( strcmp(which,"specific") == 0) 
	    {
	      setScores = 0; validScores = 0;
	      if( (setScores = 
		   capa_get_score(currentStudentPtr->s_sn,set,&validScores,&answersPtr) ) == -2 ) 
		  break;
	      if( setScores < 0 ) 
		{
		  fprintf(outputFile, "  -\t%3d\n", validScores);
		} 
              else 
		{
		  fprintf(outputFile, "%3d\t%3d\n",setScores, validScores);
		}
		capa_mfree(answersPtr);
	    } 
         else 
	   {
	     for( setScores=0, validScores=0, termScores = 0, termValids = 0, setIndex = 1; 
		  setIndex <= set; setIndex++) 
	       {
		 if( (setScores = capa_get_score(currentStudentPtr->s_sn,setIndex,
						 &validScores,&answersPtr) ) >= 0 ) 
		   termScores += setScores;
		 capa_mfree(answersPtr);
		 termValids += validScores;
		 if( setScores >= 0 ) 
		   {
		     fprintf(outputFile, "%3d ",setScores);
		   } 
                 else 
		   {
		     fprintf(outputFile, "  - ");
		   }
	       }
	     fprintf(outputFile, "\t %3d\t%3d\n",termScores,termValids);
	   }
	}
      free_students(studentPtr);
    }
  fclose(outputFile);
  return TCL_OK;
}

/* set the gAnswer score to what ever the user entered
 * Arguments: problemNumber
 */
int capaSetHandGrade (ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  int problemNumber=atoi(argv[1]),handGrade,maxGrade;
  char *handGradeChar,buf[BUFFER_SIZE];

  handGradeChar=Tcl_GetVar(interp,"gNewHandGrade",TCL_GLOBAL_ONLY);
  sprintf(buf,"max%d",problemNumber);
  maxGrade=atoi(Tcl_GetVar2(interp,"gAnswer",buf,TCL_GLOBAL_ONLY));

  if (isdigit(handGradeChar[0])) {
    handGrade=atoi(handGradeChar);
    if (handGrade > maxGrade) {
      sprintf(buf,"displayError \"Invalid response, you must enter a number between 0 and %d, or an E to excuse the problem.\"",maxGrade);
      if (Tcl_Eval(interp,buf)!=TCL_OK) {
	return TCL_ERROR;
      }
    } else {
      sprintf(buf,"%d",handGrade);
      Tcl_SetVar2(interp,"gAnswer",argv[1],buf,TCL_GLOBAL_ONLY);
    }
  } else {
    if (handGradeChar[0]=='E') {
      Tcl_SetVar2(interp,"gAnswer",argv[1],"E",TCL_GLOBAL_ONLY);
    } else {
      sprintf(buf,"displayError \"Invalid response, you must enter a number between 0 and %d, or an E to excuse the problem.\"",maxGrade);
      if (Tcl_Eval(interp,buf)!=TCL_OK) return TCL_ERROR;
	    
    }
  }
  Tcl_ResetResult(interp);
  return TCL_OK;
}

/* save gAnswer to the set.db file
 * Arguments: setNum, questNum, stuId, score 
 */
int capaSetScore (ClientData clientdata, Tcl_Interp *interp, int argc, char *argv[])
{
  T_entry entry;
  int score,i,result,question,numQuest,set;
  char buf[BUFFER_SIZE],*gAnswer,*stuId;
  T_header header;

  if (argc != 5) 
    {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp,"Incorrect number of arguments to save gAnswer",NULL);
      return TCL_ERROR;
    }

  set=atoi(argv[1]);
  question=atoi(argv[2]);
  stuId=argv[3];
  score=atoi(argv[4]);

  result=capa_get_header(&header,set);

  numQuest=atoi(header.num_questions);
  result = capa_get_entry(&entry,stuId,set);

  entry.answers=capa_malloc(numQuest+1,1);
  entry.tries=capa_malloc(3*numQuest+1,1);

  for(i=0;i<numQuest;i++) {
    if ( i==(question-1) ) {
      entry.answers[i]=score+'0';
    } else {
      entry.answers[i]='?';
    }
    entry.tries[i*3]='-';entry.tries[i*3+1]='1';
    if (i<numQuest-1) entry.tries[i*3+2]=',';
  }

  entry.answers[numQuest]='\0';
  entry.tries[(numQuest)*3]='\0';
  sprintf(entry.student_number,stuId);
  entry.e_probs=numQuest;
  result = capa_change_entry(&entry,stuId,set);

  if (result == -1) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp,"capa_change_entry returned an error.",NULL);
    return TCL_ERROR;
  }

  Tcl_ResetResult(interp);
  return TCL_OK;
}
