#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>

#include "global.h"
#include "custom.h"		/* for date formats */



#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_news_util.h"
#include "manhat-lib/shared_person_list.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_hotpot_exam_util.h"
#include "manhat-lib/shared_translate_url.h"
#include "manhat-lib/shared_access.h"


typedef struct student_node
{
  char username[MAX_USERNAME + 1];
  char realname[MAX_NAME + 1];
  int msgs;       /* total msgs to/from this student */
  int unread;     /* number of unread msgs */
  int hidden;     /* number of msgs marked teacher hidden */
  int flagged;    /* number of msgs marked with a flag */
  struct student_node *next;
}
STUDENT_NODE;



static void
read_parameters (char *course, char *key, long *offset, int *topic)
{
  char astring[80];


  cs_get_required_parameter ("crs", course, MAX_PATH);	/* shared_cs_util.c */
  cs_get_required_parameter ("id", key, MAX_KEY);	/* shared_cs_util.c */

  cs_get_required_parameter ("loc", astring, 79);	/* shared_cs_util.c */
  *offset = atol (astring);

  cs_get_required_parameter ("topic", astring, 79);	/* shared_cs_util.c */
  *topic = atoi (astring);

}

static void
memory_error ()
{
  cs_critical_error (ERR_MALLOC_FAILED, "");
}


static void
set_command_data (char *course, char *key)
{
  char url[MAX_PATH +1];

  snprintf (url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;grp=%d", "news_topic", course, key,ASSIGNMENTS);
  cs_set_value("back_url", url);

  snprintf (url, MAX_PATH +1, "%s?crs=%s&amp;id=%s", "main_menu", course, key);
  cs_set_value("main_url", url);
}






static void
cs_set_form_data (char *course, char *key, long offset, int topic)
{
   char temp[20];

  
   cs_set_value("crs", course);
   cs_set_value("id", key);
   snprintf (temp, 20, "%ld", offset);
   cs_set_value("loc", temp);
   snprintf (temp, 20, "%d", topic);
   cs_set_value("topic", temp);

}


static void
free_student_list (STUDENT_NODE *student_head)
{
  STUDENT_NODE *ptr;

  while (student_head)
    {
      ptr = student_head;
      student_head = student_head->next;
      free (ptr);
    }
}





STUDENT_NODE *
build_student_list(CONFIG_STRUCT *conf, P_NODE *person_list_head)
{
     STUDENT_NODE *student_ptr =0;
     STUDENT_NODE *current_ptr =0;
     P_NODE *ptr;
     STUDENT_NODE *student_head = 0;


     for(ptr = person_list_head; ptr; ptr=ptr->next)
     {
         if(ptr->data.group != FACULTY)
         {
            student_ptr = (STUDENT_NODE *)malloc(sizeof(STUDENT_NODE ));
            if(!student_ptr)
               memory_error();
            strcpy(student_ptr->username, ptr->data.username);
            strcpy(student_ptr->realname, ptr->data.realname);
            student_ptr->next = 0;
	    student_ptr->msgs = 0;
	    student_ptr->flagged = 0;
	    student_ptr->unread = 0;
	    student_ptr->hidden = 0;

         if(!student_head)
           student_head = student_ptr;
         else
           current_ptr->next = student_ptr;

         current_ptr = student_ptr;
        }  
     }
   return student_head;
}



/** A student can be deleted from the course after they have submitted
*** an assignment.  This function adds these students to the
*** student list
**/

static void
add_additional_students(MSG_TREE_NODE *root, P_NODE *person_head, STUDENT_NODE *student_head, int topic_id)
{
 STUDENT_NODE *s_ptr;
 P_NODE *p_ptr;
 int found;
 
 if(!root)
	 return;

 if(root->data.info.reply_to == topic_id) /* only consider responses to the original assignment 
                                             this will exclude faculty */
 {
  for(p_ptr=person_head, found=0; p_ptr && !found; p_ptr=p_ptr->next)
      found = !strcmp(root->data.info.sender, p_ptr->data.username);   

 if(!found)
  {
   if(!student_head)
    {
     student_head = (STUDENT_NODE *) malloc(sizeof(STUDENT_NODE));
     if(!student_head)
        memory_error();
     s_ptr = student_head;
    }
    else
     {
      for(s_ptr = student_head; s_ptr->next; s_ptr = s_ptr->next);
      s_ptr->next = (STUDENT_NODE *) malloc(sizeof(STUDENT_NODE));
      if(!s_ptr->next)
        memory_error();
      s_ptr = s_ptr->next;
     }     
     strcpy(s_ptr->username, root->data.info.sender);
     strcpy(s_ptr->realname, root->data.info.sender_realname);
     s_ptr->msgs = 0;
     s_ptr->unread = 0;
     s_ptr->hidden = 0;
     s_ptr->flagged = 0;
     s_ptr->next = (STUDENT_NODE *) 0;
   }         /* if !found */
 } /* if topic_id matches */

 if(root->next)
    add_additional_students(root->next,person_head, student_head, topic_id);
 if(root->reply)   
    add_additional_students(root->reply, person_head, student_head, topic_id);
 
} 




static void
add_inbox_data (long offset, MSG_TREE_NODE * assignment,
				 SESSION *user, P_NODE * person_head, STUDENT_NODE *student_head, time_t *current_time)
{
 MSG_TREE_NODE *msg_ptr;
 MSG_TREE_NODE *topic_ptr;
 int msg_count;

 STUDENT_NODE *student_ptr;

 build_one_topic_tree(user, ASSIGNMENTS, offset, &msg_ptr, &topic_ptr, &msg_count, 0, 1, current_time);  /* shared_news_util.c */
 if(!msg_ptr || !tree_head || !topic_ptr)
   cs_critical_error(ERR_CANT_FIND_THIS_OFFSET, "");
 *assignment = *topic_ptr; 

 add_additional_students(topic_ptr, person_head, student_head, topic_ptr->data.info.topic_id);
  
 for(student_ptr = student_head; student_ptr; student_ptr= student_ptr->next)
   count_username_msgs(topic_ptr, student_ptr->username, &(student_ptr->msgs), &(student_ptr->unread),
                                  &(student_ptr->hidden), &(student_ptr->flagged)); /* shared_news_util.c */
 free_msg_tree(tree_head); /*shared_news_util.c */
}





static 
void  set_one_student_result( char *realname,  char *username, 
                                     char *viewed_timestring,  char *score_string,  char *elapsed_timestring,
                                     char *start_timestring,  char *submit_timestring,
                                     char *scorepath,  int index)
{
#define MAX_TMP_NAME 40
char name[MAX_TMP_NAME];
static int j =0;

 /* realname(username) */
 if(*realname)
 {
    snprintf(name, MAX_TMP_NAME, "score.%d.realname", index);
    cs_set_value(name, realname);    /* shared_cs_util.c */
    
    snprintf(name, MAX_TMP_NAME, "score.%d.username", index);
    cs_set_value(name, username);   /* shared_cs_util.c */

    if(*viewed_timestring)
    {
       snprintf(name, MAX_TMP_NAME, "score.%d.viewed_time", index);
       cs_set_value(name, viewed_timestring);  /* shared_cs_util.c */
       
       snprintf(name, MAX_TMP_NAME, "score.%d.scorepath", index);
       cs_set_value(name, scorepath);          /* shared_cs_util.c */
	
    }
    j = 0;
 }   
 
 /* score */
 if(*score_string)
 {
    snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.num", index, j);
    cs_set_value(name, score_string);           	/* shared_cs_util.c */
 }
 
 /* elapsed_time */
 if(*elapsed_timestring)
 {
    snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.elapsed_time", index, j);
    cs_set_value(name, elapsed_timestring);		/* shared_cs_util.c */
 }
 
 /* start time/submit_time */
 if(*start_timestring)
 {
    snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.start_time", index, j);
    cs_set_value(name, start_timestring);   		/* shared_cs_util.c */

    if(*submit_timestring) 
     {
        snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.submit_time", index, j);
        cs_set_value(name, submit_timestring);   		/* shared_cs_util.c */
     }
    else
     {	 
       snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.no_submit", index, j);
       cs_set_int_value(name, 1); 		/* shared_cs_util.c */
     }
    j++;
 }
 
 
 snprintf(name, MAX_TMP_NAME, "score.%d.how_many", index);
 cs_set_int_value(name, j);  	/* shared_cs_util.c */
#undef MAX_TMP_NAME 
}





    
      
       
                 

static void
parse_score_line(char *line, char *elapsed_timestring, 
                                char *start_timestring, char *submit_timestring, char *score_string)
{
#define MAX_STRING 100

char string1[MAX_STRING + 1];
char string2[MAX_STRING + 1];
time_t sometime;

/* get number correct */
if(csv_value(line,0,string1, MAX_STRING))           /* shared_hotpot_exam_util.c */
 cs_critical_error(ERR_ERROR_PARSING_SCORE, line);  /* shared_cs_util.c */
   

/* get total questions */
if(csv_value(line,1,string2, MAX_STRING))               /* shared_hotpot_exam_util.c */
   cs_critical_error(ERR_ERROR_PARSING_SCORE, line);	/* shared_cs_util.c */
   
snprintf(score_string,50,"%s/%s", string1, string2);   /* caution- check calling function for correct size of score_string */


/* get elapsed time - it is already saved in correct format */
if(csv_value(line,2,elapsed_timestring, MAX_TIMESTRING))      	/* shared_hotpot_exam_util.c */
   cs_critical_error(ERR_ERROR_PARSING_SCORE, line);		/* shared_cs_util.c */
   

/* get time exam was started */
if(csv_value(line,3,string1, MAX_STRING))			/* shared_hotpot_exam_util.c */
   cs_critical_error(ERR_ERROR_PARSING_SCORE, line);           /* shared_cs_util.c */
 sometime = (time_t) atol(string1);
 strftime(start_timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime(&sometime));


/* get time exam was submitted */
if(csv_value(line,4,string1, MAX_STRING))                      	/* shared_hotpot_exam_util.c */
   cs_critical_error(ERR_ERROR_PARSING_SCORE, line);		/* shared_cs_util.c */
 sometime = (time_t) atol(string1);
 strftime(submit_timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime(&sometime));

#undef MAX_STRING
}





static 
void set_student_exam_data( char *score_path,  char *realname,  char *username, int index)
{
#define MAX_LINE 1000

 FILE *fp;
 int fd;
 time_t viewed_time;
 char viewed_timestring[MAX_TIMESTRING + 1] = "";
 char elapsed_timestring[MAX_TIMESTRING + 1] = "";
 char start_timestring[MAX_TIMESTRING + 1] = "";
 char submit_timestring[MAX_TIMESTRING + 1] = "";
 char score_string[50] = "";
 char line[MAX_LINE + 1];
 int unsubmitted =0;
int score_count = 0;
  
 fp = fopen(score_path,"r");
 if(!fp) 
  {
   set_one_student_result(realname, username, "", "","","","",  score_path,  index);
   return;
  } 

 fd = fileno(fp);
 get_shared_lock(fd,1);   /* shared_lock.c */
 
 if(fread(&viewed_time, sizeof(time_t), 1, fp) != 1)
    cs_critical_error(ERR_FREAD_FAILED, score_path);
 
 if(viewed_time != 0)
    strftime(viewed_timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime(&viewed_time));
 else
    *viewed_timestring = '\0';   
 
 /** it's possible the student left immediately after viewing the exam
 *** in that case the next read will fail
 **/
 if(!fgets(line, MAX_LINE,fp))
  {
   if(viewed_time)
       set_one_student_result(realname, username, viewed_timestring,
                          "", "", viewed_timestring, "", 
                          score_path,  index);  
   else
       set_one_student_result(realname, username, "", "","","","",score_path,  index);
  }                        
 else
  {  
   do 
    {
     parse_score_line(line, elapsed_timestring, start_timestring, submit_timestring, score_string);
     if(strcmp(viewed_timestring, start_timestring))
         unsubmitted = 1;
     else
         unsubmitted = 0;
     set_one_student_result(score_count?"":realname, score_count?"":username, score_count?"":viewed_timestring,
                          score_string, elapsed_timestring, start_timestring, submit_timestring, 
                           score_path,  index);
     score_count++;
    } while(fgets(line, MAX_LINE, fp)) ; 
  }
  if(unsubmitted)
       set_one_student_result("", "", viewed_timestring,"", "", viewed_timestring, "", 
                          score_path,  index);  

 
  
 release_lock(fd);
 fclose(fp);

#undef MAX_LINE
} 







static 
void set_exam_results_data(const char *course_path, const char *sender,
                                      const char *info_fname, 
                                       STUDENT_NODE *student_head)
{

 STUDENT_NODE *sptr;
 char score_path[MAX_PATH + 1];
 int index = 0; 
  

  for(sptr=student_head; sptr; sptr=sptr->next)
   {
     get_hotpot_score_path(course_path, sptr->username, sender,  info_fname, score_path);   /* shared_hotpot_exam_util.c */
     set_student_exam_data(score_path,sptr->realname,sptr->username,  index);
     index++;
   }  

#undef MAX_TMP_NAME 
}






static void
set_expand_topic_data (char *course, char *key, int topic,
               long offset, CONFIG_STRUCT *conf , SESSION *user, P_NODE *p)
{
#define MAX_TMP_NAME 30

  char *new_str = 0;
  int i;
  char name[MAX_TMP_NAME];
  char url[MAX_PATH +1];

  MSG_TREE_NODE  assignment;
  char info_prog[MAX_PATH + 1];
  STUDENT_NODE *sptr;
  char timestring[MAX_TIMESTRING + 1];
  int hotpot_exam;   			/* is this assignment a Hot Potatoes exam? */
  int contains_unreleased_msgs = 0;     /* for Hot Potatoes exams, is there one or more unreleased exam result? */

  char *cptr;
  char rname[MAX_NAME + 1];
  STUDENT_NODE *student_head;
  time_t current_time;

  student_head = build_student_list (conf,p);
  add_inbox_data (offset, &assignment, user, p, student_head, &current_time);
  
  /* is this a Hot Potatoes Exam? */
  hotpot_exam = is_hotpot_exam(&assignment.data, conf->course_path);    /* shared_news_util.c */
  
  snprintf (info_prog, MAX_PATH + 1,"%s?crs=%s&amp;id=%s&amp;grp=%d&amp;loc=","news_info", course, key, ASSIGNMENTS);

  /* set the data for the actual assignment */
  if(!assignment.user_data.time_read ) 
     cs_set_int_value("msg.is_unread", 1);
  if(assignment.data.info.attachments) 
     cs_set_int_value("msg.has_attachments", 1);

  if(assignment.user_data.flagged)
    cs_set_int_value("msg.flagged", 1);
    
   snprintf(url, 
       MAX_PATH + 1, "%s?crs=%s&amp;id=%s&amp;grp=%d&amp;msg_id=%d&amp;from=assign_fac_expand%ccrs%c%s%cid%c%s%cloc%c%ld%ctopic%c%d",
        "news_flag", course, key, ASSIGNMENTS, topic,
        QMARK_SUB,EQUAL_SUB, course,
        AMP_SUB, EQUAL_SUB, key,
        AMP_SUB, EQUAL_SUB, offset, 
        AMP_SUB, EQUAL_SUB, topic); 
   cs_set_value("msg.flag_url", url);  
    
    
    
  snprintf (url, MAX_PATH + 1, "%s%ld", info_prog, offset);
  cs_set_value("msg.info_url", url);    /* shared_cs_util.c */
  
  /* AMP_SUB, EQUAL_SUB, etc. are #defined in shared_translate_url.h */
  snprintf
    (url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;from=%s%ccrs%c%s%cid%c%s%ctopic%c%d%cloc%c%ld",
     "assign_read", course, key, offset, "assign_fac_expand", QMARK_SUB,
     EQUAL_SUB, course, AMP_SUB, EQUAL_SUB, key, AMP_SUB, EQUAL_SUB, topic,AMP_SUB,EQUAL_SUB,offset);


  if(USE_FIXED_FONT(conf->misc))     /* global.h */   
      strcat(url, "&amp;fixed=1");
     
  cs_set_value("msg.subject_url", url);    /* shared_cs_util.c */

  if (!strlen (assignment.data.info.subject))
     cs_set_int_value("msg.no_subject", 1);   /* shared_cs_util.c */
  else
  {
      /* next makes use of undocmented ClearSilver function */
    if(cgi_html_escape_strfunc(assignment.data.info.subject,&new_str) == STATUS_OK)
      {
       cs_set_value("msg.subject",new_str);    /* shared_cs_util.c */
       if(new_str)
          free(new_str);
      }
    else
       cs_set_value("msg.subject",assignment.data.info.subject); /* shared_cs_util.c */
  }
  
  cs_set_value("msg.sender_realname", assignment.data.info.sender_realname);   /* shared_cs_util.c */
  strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT,
	    localtime (&(assignment.data.info.time_sent)));
  cs_set_value("msg.sent_time", timestring);                                   /* shared_cs_util.c */

  if(hotpot_exam)
     cs_set_int_value("msg.hotpot_exam", 1);                                  /* shared_cs_util.c */
  

   /* is_teacher_hidden() is in shared_news_util.c 
   ** BASIC_STATUS() is a macro #defined in global.h 
   */
  if (is_teacher_hidden(assignment.data.status, assignment.data.release_time,
                                  assignment.data.unrelease_time, current_time) )
     cs_set_int_value("msg.hidden", 1);                                      /* shared_cs_util.c */

  else 
    if(BASIC_STATUS(assignment.data.status) == READ_ONLY) 			/* shared_cs_util.c */
        cs_set_int_value("msg.read_only", 1);


  for (sptr = student_head, i = 0; sptr; sptr = sptr->next, i++)
    {
	if(sptr->unread)
	{
	   snprintf(name, MAX_TMP_NAME, "std.%d.unread", i);
	   cs_set_int_value(name, 1);                          /* shared_cs_util.c */
	}
	
	if(sptr->flagged)
	{
	  snprintf(name, MAX_TMP_NAME, "std.%d.flagged", i);
	  cs_set_int_value(name, 1);                          /* shared_cs_util.c */
	}
	
        snprintf(name, MAX_TMP_NAME, "std.%d.count", i);
        cs_set_int_value(name, sptr->msgs);			/* shared_cs_util.c */

        /* replace spaces with underscores in realname */
        strncpy (rname, sptr->realname, MAX_NAME + 1);
        while ((cptr = strchr (rname, ' ')))
	    *cptr = '_';

	snprintf(url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;topic=%d&amp;realname=%s&amp;student=%s&amp;loc=%ld",
	      "assign_student_work", course, key, topic, rname, sptr->username, offset);
        snprintf(name, MAX_TMP_NAME, "std.%d.name_url", i);
	cs_set_value(name, url);            			/* shared_cs_util.c */

        snprintf(name, MAX_TMP_NAME, "std.%d.realname", i);
        cs_set_value(name, sptr->realname);			/* shared_cs_util.c */

        if(hotpot_exam && sptr->hidden)
        {
         snprintf(name, MAX_TMP_NAME, "std.%d.unreleased_hotpot", i);
	 cs_set_int_value(name, 1);
         contains_unreleased_msgs = 1;
        }   
    
    }	/* for(sptr=student_head...) */

  if(hotpot_exam && contains_unreleased_msgs)
       cs_set_int_value("msg.has_unreleased_hotpot", 1);			/* shared_cs_util.c */

  if(hotpot_exam)
  {
    cs_set_int_value("msg.hotpot_exam", 1);			/* shared_cs_util.c */
    set_exam_results_data(conf->course_path, assignment.data.info.sender,
                                      assignment.data.info_fname,  student_head);
  }

  free_student_list (student_head );
#undef MAX_TMP_NAME 
}





static void
set_assign_data (char *course, char *key, long offset, int topic, 
				CONFIG_STRUCT *conf, SESSION *user, int can_write)
{
 P_NODE *person_list_head;
 
  cs_set_course_info(conf);         /* shared_cs_util.c */
  cs_set_current_time();            /* shared_cs_util.c */
  
  if(can_write)
     cs_set_int_value("can_write", 1);    /* shared_cs_util.c */

  set_command_data (course, key);
  person_list_head =  build_option_person_list (conf);  /* shared_person_list.c */

  if(conf->access != ACCESS_CAPTURE)
  {
     cs_set_int_value("not_capture", 1);        /* shared_cs_util.c */
     set_option_userlist (person_list_head);	/* shared_person_list.c */
  }
  
  cs_set_form_data (course, key, offset,topic);
  set_expand_topic_data (course, key, topic, offset,  conf, user, person_list_head);

  free_option_person_list (person_list_head);	/* shared_person_list.c */
}





int
main (void)
{
  char course[MAX_PATH + 1];	/* from crs=? command line */
  char key[MAX_KEY + 1];	/* from id=? command line */
  long offset;			/* from loc=? command line */
  int topic;                    /* from topic=? command line */
  SESSION user;
  CONFIG_STRUCT conf;		/* the configuration read from config file */
  int can_write;                /* can this user write to this course? */

  cs_cgi_init();                /* shared_cs_util.c */

  read_parameters (course, key, &offset, &topic);
  read_configuration_file (course, &conf);	/* shared_util.c */
  validate_key (key, &user, &conf);		/* shared_util.c */


  if(user.group != FACULTY)
         cs_critical_error (ERR_REQUEST_DENIED, "");   /* shared_cs_util.c */
  
  can_write = has_write_permission(&user, &conf);      			/* shared_access.c */
  set_discussion_names (conf.course_path, user.username, ASSIGNMENTS);	/* shared_news_util.c */
  
  set_assign_data (course, key, offset, topic, &conf, &user, can_write);
  cs_cgi_display("assign_fac_expand", 1); 				/* shared_cs_util.c */
  cs_cgi_destroy();							/* shared_cs_util.c */
  return 0;
}

