/* c interfaces for the scorer program
   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.
*/

/*
 * scorer.funct.c
 * Guy Albertelli II 1997
 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#ifdef NeXT
#endif
#ifdef linux
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#endif
#include <tcl.h>
#include <tk.h>
#include "pProj/capaCommon.h"
#include "scorer.h"
#include "manager.h"
#ifdef DMALLOC
#include <dmalloc.h>
#endif

extern int Parsemode_f;
char NUMBER[10]={'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
char LETTER[10]={'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
PIDPINlist *studentList;

/* 2 arguments, string 1 and string 2 result is an integer of # charactes the same*/
int compareCapaID(ClientData notused, Tcl_Interp *interp, int argc,
		     char* argv[])
{
  int same=0,length,i;
  char buf[MAX_BUFFER_SIZE];
  length=strlen(argv[1]);
  for(i=0;i<length;i++) if(argv[1][i] == argv[2][i]) same++;
  sprintf(buf,"%d",same);
  Tcl_SetResult(interp,buf,TCL_VOLATILE);
  return TCL_OK;
}

/* 3 arguments, the PID of the student, the setID, the number of expected questions */
int getAnswersFromSet(ClientData notused, Tcl_Interp *interp, int argc,
		     char* argv[])
{
  int result,capaQuestions,questionIndex,numRight,total=0,numQuestions;
  Problem_t *curProblem,*headProblem;
  char answerstring[MAX_LINE_LENGTH],grade[MAX_LINE_LENGTH];
  char *buf;
  T_student capaStudent;
  char* questionPID;
  int setID;
  Tcl_Obj* tclResult;


  if (argc!=4) {
    Tcl_SetResult(interp,"usage is: PID setID numQuestions",TCL_VOLATILE);
    return TCL_ERROR;
  }
  questionPID=argv[1];
  setID=atoi(argv[2]);
  numQuestions=atoi(argv[3]);

  result=capa_parse(setID,&headProblem,questionPID,&capaQuestions,NULL);
  curProblem=headProblem;

  if (result==0 || result == -1) {
    buf=capa_malloc(MAX_LINE_LENGTH,sizeof(char));
    sprintf(buf,"The Parse failed: %d\n",result);
    Tcl_SetResult(interp,buf,TCL_VOLATILE);
    capa_mfree(buf);
    return TCL_ERROR;
  } else if (result != numQuestions)  {
    buf=capa_malloc(MAX_LINE_LENGTH,sizeof(char));
    sprintf(buf,"The parser found %d questions, there were supposed to be %d questions.\nDying\n",result,numQuestions);
    Tcl_SetResult(interp,buf,TCL_VOLATILE);
    capa_mfree(buf);
    return TCL_ERROR;
  }

  tclResult=Tcl_NewStringObj("",0);
  Tcl_IncrRefCount(tclResult);
  for(questionIndex=0;questionIndex<numQuestions;questionIndex++) {
    buf=(char*)capa_malloc(strlen(curProblem->answer)+MAX_LINE_LENGTH,sizeof(char));
    sprintf(buf,"%s ",curProblem->answer);
    Tcl_AppendStringsToObj(tclResult,buf,NULL);
    capa_mfree(buf);
    curProblem=curProblem->next;
  }
  free_problems(headProblem);
  Tcl_SetObjResult(interp,tclResult);
  Tcl_DecrRefCount(tclResult);
  return TCL_OK;
}

/*needs num (the unique ident)*/
int freeStudentList(ClientData notused, Tcl_Interp *interp, int agrc,
		     char* argv[])
{
  int i,num = atoi(argv[1]);
  PIDPINlist *cur,*prev;

  if (!studentList) { return TCL_OK; }

  cur=studentList;
  prev=NULL;
  while((cur->num!=num) && cur->next) { prev=cur; cur=cur->next; }
  if (cur->num!=num) { return TCL_OK; }
  
  capa_mfree(cur->classname);
  for(i=0;i<cur->numStudents;i++) capa_mfree(cur->classList[i].capaid_plus);
  capa_mfree((char*)cur->classList);
  if ( prev != NULL ) {
    prev->next=cur->next;
  } else {
    if (cur->next) studentList=cur->next;
  }
  capa_mfree((char*)cur);
  return TCL_OK;
}

/*needs num (unique ident), classname, setId, capaid_plus size*/
int buildStudentList(ClientData notused, Tcl_Interp *interp, int argc,
		     char* argv[])
{
  long i,j,numStudents,num,capaidplusSize,setId,part1,part2;
  T_student *curStudent,*headStudent;
  PIDPINlist *cur;
  char *buf,*buf2;
  Tcl_Obj* tclResult;

  if (argc!=5) {
    Tcl_SetResult(interp,"usage is: number classname setId capa_id_plus_size",
		  TCL_VOLATILE);
    return TCL_ERROR;
  }

  if (studentList) {
    cur=studentList;
    while(cur->next) { cur=cur->next; }
    cur->next=(PIDPINlist*)capa_malloc(1,sizeof(PIDPINlist));
    cur=cur->next;
  } else {
    studentList=(PIDPINlist*)capa_malloc(1,sizeof(PIDPINlist));
    cur=studentList;
  }
  cur->classname=(char*)capa_malloc(strlen(argv[2]),sizeof(char));
  strcpy(argv[2],cur->classname);
  cur->num=num=atoi(argv[1]);
  setId=atoi(argv[3]);
  capaidplusSize=atoi(argv[4]);

  numStudents=capa_get_section(&headStudent, 0);
  if ( numStudents == 0 ) {
    Tcl_SetResult(interp,"No students in class.",TCL_VOLATILE);
    return TCL_ERROR;
  } else if ( numStudents < 0 ) {
    Tcl_SetResult(interp,"Unable to read the classl file",TCL_VOLATILE);
    return TCL_ERROR;
  }
  cur->classList=(PIDPIN*)capa_malloc(numStudents,sizeof(PIDPIN));
  curStudent=headStudent;
  tclResult=Tcl_NewStringObj("",0);
  Tcl_IncrRefCount(tclResult);
  for(i=0;curStudent;curStudent=curStudent->s_next,i++) {
    capa_seed(&part1, &part2, curStudent->s_sn);setall(part1, part2);
    strcpy(cur->classList[i].PID,curStudent->s_sn);
    cur->classList[i].PIN=capa_PIN(curStudent->s_sn,setId,0);
    cur->classList[i].capaid_plus=capa_id_plus(curStudent->s_sn,setId,capaidplusSize);
    j=strlen(cur->classList[i].capaid_plus)+MAX_PIN_CHAR+
      strlen(cur->classList[i].PID)+strlen(curStudent->s_nm)+MAX_LINE_LENGTH;
    buf=(char*)capa_malloc(j,sizeof(char));
    sprintf(buf,"{{%s} %s %d %s} ",curStudent->s_nm,curStudent->s_sn,
	    cur->classList[i].PIN,cur->classList[i].capaid_plus);
    Tcl_AppendStringsToObj(tclResult,buf,NULL);
    capa_mfree(buf);
  }
  Tcl_SetObjResult(interp,tclResult);
  Tcl_IncrRefCount(tclResult);
  free_students(headStudent);
  cur->numStudents=numStudents;
  return TCL_OK;
}

int gradeQuestion(Question questions[MAX_QUEST],int questionIndex,
		  Problem_t *problem,Student *student,Flags* flags)
{
  int numRight=0,leafs;
  char one=1,zero=0;
  char *ansOn[20],*stuOn[20];
  int i,j;
  int sortArray[256]; 
  char newAnswer[MAX_LINE_LENGTH],*oldAnswer;

  switch(questions[questionIndex].type)
    {
    case ONE_OUT_OF_8:
    case GLE:
    case TF:
    case SINGLE_DIGIT:
      if (!flags->log)
	printf("The correct answer:%10s  The student's answer:%10s, \t",
	       problem->answer,student->Answers[questionIndex]);
      for(leafs=0;problem->answer[leafs]!='\0';leafs++)
	if (problem->answer[leafs]==student->Answers[questionIndex][leafs])
	  numRight++;
      if (!flags->log)
	printf("%d right\n",numRight);
      break;
    case ASSIGNED:
      if (!flags->log)
	printf("The student got a %s out of %d\n",
	       student->Answers[questionIndex],
	       questions[questionIndex].points);
      if (isspace(student->Answers[questionIndex][0]))
	numRight=0;
      else
	numRight=(int)(student->Answers[questionIndex][0]-'0');
      break;
    case N_OUT_OF_M:
      if (!flags->log)
	printf("The correct answer:%10s  The student's answer:%10s, \t",
	       problem->answer,student->Answers[questionIndex]);
      if (problem->ans_type == ANSWER_IS_CHOICE) {
	for(i=0;i<255;i++) sortArray[i]=0;
	for(i=0;i< strlen(problem->answer);i++)
	  sortArray[(int)problem->answer[i]]=1;
	for(i=0,j=0;i<255;i++) {
	  if (sortArray[i]) {
	    newAnswer[j]=i;
	    j++;
	  }
	}
	newAnswer[j]='\0';
	if (!flags->log) 
	  printf("\nThe sorted correct answer:%10s\t\t\t",newAnswer);
	oldAnswer=problem->answer;
	problem->answer=newAnswer;
      }
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  ansOn[leafs]=strchr(problem->answer,('A'+(char)leafs));
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  if (ansOn[leafs] != NULL ) 
	    ansOn[leafs]=&one; 
	  else
	    ansOn[leafs]=&zero;
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  stuOn[leafs]=strchr(student->Answers[questionIndex],
			      ('A'+(char)leafs));
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	{
	  if (stuOn[leafs] != NULL)
	    stuOn[leafs]=&one; 
	  else 
	    stuOn[leafs]=&zero;
	}
      for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
	if (ansOn[leafs] == stuOn[leafs]) 
	  numRight++;
      if (!flags->log)
	printf("%d right\n",numRight);
      if (problem->ans_type == ANSWER_IS_CHOICE) problem->answer=oldAnswer;
      break;
    case STRING_MATCH:
      if (!flags->log)
	printf("The correct answer:%10s  The student's answer:%10s, ",
	       problem->answer,student->Answers[questionIndex]);
      if (problem->ans_type == ANSWER_IS_CHOICE) {
	for(i=0;i<255;i++) sortArray[i]=0;
	for(i=0;i< strlen(problem->answer);i++)
	  sortArray[(int)problem->answer[i]]=1;
	for(i=0,j=0;i<255;i++) {
	  if (sortArray[i]) {
	    newAnswer[j]=i;
	    j++;
	  }
	}
	newAnswer[j]='\0';
	if (!flags->log) 
	  printf("\nThe sorted correct answer:%10s\t\t\t",newAnswer);
	oldAnswer=problem->answer;
	problem->answer=newAnswer;
      }
      if (!(strcasecmp(problem->answer,student->Answers[questionIndex]))) {
	if (!flags->log) printf("Answer is correct\n");
	numRight=questions[questionIndex].points;
      } else {
	if (!flags->log) printf("Answer is wrong\n");
	numRight=0;
      }
      if (problem->ans_type == ANSWER_IS_CHOICE) problem->answer=oldAnswer;
      break;
    default:
      fprintf(stderr,"Unknown question type while grading, %c.\nDying.\n",
	      questions[questionIndex].type);
      exit(E_UNKNOWN_QTYPE);
      break;
    }
  return numRight;
}


