#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>

#include "global.h"
#include "custom.h"		/* for date FORMAT strings */


#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_news_util.h"
#include "manhat-lib/shared_potato_util.h"
#include "manhat-lib/shared_person_list.h"  /* for get_user_info()  */
#include "manhat-lib/shared_cs_util.h" 
#include "manhat-lib/shared_bio.h" 
#include "manhat-lib/shared_time.h"

typedef enum event
{ NEW_MSG, READ_MSG, LOG_IN, SEND_MSG, ENTER_CHAT , TOOK_SELFTEST}
EVENT;

#define NO_MODULE 99

struct event_node
{
  time_t date;			/* this is a 'normalized' time 01:01.01 on the date of the event */
  time_t time;			/* this is the standard unix time (secs after 1/1/70) */
  int module;			/* one of MAIL, GRADES, TEAM_DISCUSSION, etc.. see global.h
				   ** for logins, this is set to NO_MODULE, #defined above */
  EVENT event;			/* what happened */
  char *tooltip;		/* a description of what happened */
  struct event_node *next;
};

typedef struct event_node EVENT_NODE;

EVENT_NODE *head = 0;
EVENT_NODE *tail = 0;



/* next two global variables are used in the recursive
** add_news_msg() function... they are made global to 
** conserve stack space
*/

char temp_tip[MAX_PATH + 1];              
char temp_timestring[MAX_TIMESTRING + 1];
HDF *hdf;

void
set_back_url(const char *prog, const char *course, const char *key, int show_list)
{
  char url[MAX_PATH +1];
  snprintf(url, MAX_PATH +1, "%s?id=%s&amp;crs=%s&amp;list=%d", prog, key, course, show_list);
  cs_set_value("back_url", url);

}

static void
read_parameters (char *course, char *key, char *username)
{
#define MAX_TEMP_USERNAME (MAX_USERNAME + MAX_NAME + 2)

  char temp_username[MAX_TEMP_USERNAME];        /* may look like: "snarmont:Steve Narmontas" */
  char *ptr;
  int show_list;
  char prog[MAX_PATH +1];        /* may look like: "snarmont:Steve Narmontas" */

   cs_get_required_parameter ("crs", course, MAX_PATH);
   cs_set_value("crs", course);
   cs_get_required_parameter ("id", key, MAX_ID);
   cs_set_value("id", key);
   cs_get_optional_parameter ("list", temp_username, MAX_ID);
   show_list = atoi(temp_username);
  if(strlen(temp_username))
  {
     cs_get_required_parameter ("username", temp_username, MAX_TEMP_USERNAME);
    ptr = strchr (temp_username, ':');
    if (!ptr)
       cs_critical_error(ERR_PARAM_MISSING, "username");
    *ptr = '\0';
  if(!strlen(temp_username))
     cs_critical_error(ERR_PARAM_MISSING, "username");
   strncpy (username, temp_username, MAX_USERNAME);
   }
  else
    cs_get_required_parameter ("username", username, MAX_USERNAME);
   cs_get_required_parameter ("prog", prog, MAX_PATH);
   cs_set_value("prog", prog);
  cs_set_int_value("show_list", show_list);
  set_back_url(prog, course, key, show_list);
#undef MAX_TEMP_USERNAME
}


void
unexpected_end ()
{
  cs_critical_error (ERR_UNEXPECTED_END_OF_LINE, "");
}




int
record_cmp (EVENT_NODE * a, EVENT_NODE * b)
{
  if ((a->time) < (b->time))
    return -1;
  else if ((a->time) > (b->time))
    return 1;

  return 0;
}


void
sort_event_list ()
{
  EVENT_NODE *q, *t, *p, *r;
  if (head != NULL)
    {
      t = head;
      while (t->next)
	{
	  q = t->next;
	  if (record_cmp (q, head) < 0)
	    {
	      t->next = q->next;
	      q->next = head;
	      head = q;
	    }
	  else
	    {
	      r = head;
	      p = r->next;
	      while (record_cmp (q, p) > 0)
		{
		  r = p;
		  p = r->next;
		}
	      if (q == p)
		t = q;
	      else
		{
		  t->next = q->next;
		  q->next = p;
		  r->next = q;
		}
	    }
	}
    
    /* adjust global tail pointer */
    for(tail=head; tail->next; tail=tail->next);
    }
}




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




/* allocates a new node at the end of the linked list with global variable 'tail' pointing to the
** new node...
*/
void
new_event_node ()
{

  if (!head)
    {
      head = (EVENT_NODE *) malloc (sizeof (EVENT_NODE));
      if (!head)
	memory_error ();
      tail = head;
    }
  else
    {
      tail->next = (EVENT_NODE *) malloc (sizeof (EVENT_NODE));
      if (!tail->next)
	memory_error ();
      tail = tail->next;
    }
  tail->next = (EVENT_NODE *) 0;
}



/* returns a time_t value which is 01:01.01 on the same date as 'a' */
time_t normalized_time (time_t a)
{
  struct tm a_tm;

  a_tm = *localtime (&a);

  a_tm.tm_hour = 1;
  a_tm.tm_min = 1;
  a_tm.tm_sec = 1;

  return (mktime (&a_tm));
}



/* searches mail directory for *.new files and adds the new arrivals to the
** event list
*/
void
add_newfile_postoffice_msgs (char *username, SESSION *user)
{
  DIR *dir_p;
  struct dirent *dir_entry_p;
  char *cptr;
  char newpath[MAX_PATH + 1];
  NEWFILE_RECORD newfile_info;
  FILE *fp;
  char timestring[MAX_TIMESTRING + 1], temp[MAX_PATH + 1];
  int viewing_own;		/* is teacher viewing his own record? */
  int tip_len;

  dir_p = opendir (discussion_path);	/* discussion_path is in shared_news_util.c */
  if (!dir_p)
    cs_critical_error (ERR_OPENDIR_FAILED, "");

  while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      cptr = strstr (dir_entry_p->d_name, ".new");
      if (cptr && *(cptr + 4) == '\0')
	{			/* i.e. if file ends in .new */
	  new_event_node ();
	  snprintf (newpath, MAX_PATH + 1, "%s/%s", discussion_path,
		    dir_entry_p->d_name);
	  fp = fopen (newpath, "r");
	  if (!fp)
	    cs_critical_error (ERR_FOPEN_READ_FAILED, "");
	  if (fread (&newfile_info, sizeof (NEWFILE_RECORD), 1, fp) != 1)
	    cs_critical_error (ERR_FREAD_FAILED, "");
	  fclose (fp);

	  tail->event = NEW_MSG;
	  tail->time = newfile_info.time_sent;

	  /* if msg is from this (faculty) user, or if faculty is viewing his own 
	     ** stats, then show some detail
	     ** otherwise, say "(private Post Office message)")
	   */
	  viewing_own = !strcmp (user->username, username);
	  if (viewing_own || !strcmp (user->username, newfile_info.sender))
	    {
	      strftime (timestring, MAX_TIMESTRING, TIME_FORMAT,
			localtime (&newfile_info.time_sent));
	      snprintf (temp, MAX_PATH + 1, "%s (%s %s)",
			newfile_info.subject,
			viewing_own ? newfile_info.sender_realname : "",
			timestring);
	    }
	  else
	    strncpy (temp, hdf_get_value(hdf, "stats_post_office", "(private Post Office message)"), MAX_PATH + 1);
	  tip_len = strlen (temp);
	  tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
	  if (!tail->tooltip)
	    memory_error ();
	  strncpy (tail->tooltip, temp, tip_len + 1);

	  tail->module = MAIL;
	  tail->date = normalized_time (newfile_info.time_sent);

	}
    }
  closedir (dir_p);
}







void
add_inbox_postoffice_msgs (char *username, SESSION *user)
{
  char inbox_path[MAX_PATH + 1];
  FILE *fp;
  INBOX_RECORD msg;
  char timestring[MAX_TIMESTRING + 1];
  char temp[MAX_PATH + 1];
  struct tm;
  int viewing_own;		/* is this person viewing his own record? */
  int tip_len;

  snprintf (inbox_path, MAX_PATH + 1, "%s/%s", discussion_path, INBOX_FNAME);
  fp = fopen (inbox_path, "r");
  if (fp)
    {
      while (fread (&msg, sizeof (INBOX_RECORD), 1, fp) == 1)
	{

	  /* FIRST DO NEW_MSG EVENTS */
	  new_event_node ();
	  tail->event = NEW_MSG;
	  tail->module = MAIL;
	  tail->time = msg.newfile_info.time_sent;

	  /* if msg is from this (faculty) user, or if faculty is viewing his own calendar,
	     **  then show some detail
	     ** otherwise, say "(private Post Office message)")
	   */

	  viewing_own = !strcmp (user->username, username);
	  if (viewing_own || !strcmp (user->username, msg.newfile_info.sender))
	    {
	      strftime (timestring, MAX_TIMESTRING, TIME_FORMAT,
			localtime (&msg.newfile_info.time_sent));
	      snprintf (temp, MAX_PATH + 1, "%s (%s %s)",
			msg.newfile_info.subject,
			viewing_own ? msg.newfile_info.sender_realname : "",
			timestring);
	    }
	  else
	    strncpy (temp, hdf_get_value(hdf, "stats_post_office", "(private Post Office message)"), MAX_PATH + 1);
	  tip_len = strlen (temp);
	  tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
	  if (!tail->tooltip)
	    memory_error ();
	  strncpy (tail->tooltip, temp, tip_len + 1);

	  tail->date = normalized_time (msg.newfile_info.time_sent);


	  /* NEXT DO READ_MSG */
	  if (msg.time_read)
	    {
	      new_event_node ();
	      tail->event = READ_MSG;
	      tail->module = MAIL;
	      tail->time = msg.time_read;

	      /* if msg is from this (faculty) user, or if faculty is viewing his own calendar,
	         **  then show some detail
	         ** otherwise, say "(private Post Office  message)")
	       */

	      viewing_own = !strcmp (user->username, username);
	      if (viewing_own
		  || !strcmp (user->username, msg.newfile_info.sender))
		{
		  strftime (timestring, MAX_TIMESTRING, TIME_FORMAT,
			    localtime (&msg.time_read));
		  snprintf (temp, MAX_PATH + 1, "%s (%s %s)",
			    msg.newfile_info.subject,
			    viewing_own ? msg.
			    newfile_info.sender_realname : "", timestring);
		}
	      else
		strncpy (temp, hdf_get_value(hdf, "stats_post_office", "(private Post Office message)"), MAX_PATH + 1);
	      tip_len = strlen (temp);
	      tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
	      if (!tail->tooltip)
		memory_error ();
	      strncpy (tail->tooltip, temp, tip_len + 1);


	      tail->date = normalized_time (msg.time_read);
	    }
	}			/* while fread() */
      fclose (fp);
    }
}



/* is username among the recipients of this msg? */

int
user_is_recipient (char *this_username, OUTBOX_RECORD * msg)
{
  char inf_path[MAX_PATH + 1];
  FILE *fp;
  INFO info_record;
  int i;
  ATTACHMENT attach;
  PERSON pers;

  snprintf (inf_path, MAX_PATH + 1, "%s/%s", discussion_path,
	    msg->info_fname);
  fp = fopen (inf_path, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, "");
  if (fread (&info_record, sizeof (INFO), 1, fp) != 1)
    cs_critical_error (ERR_FREAD_FAILED, "");
  for (i = 1; i <= info_record.no_attachments; i++)
    if (fread (&attach, sizeof (ATTACHMENT), 1, fp) != 1)
      cs_critical_error (ERR_FREAD_FAILED, "");
  while (fread (&pers, sizeof (PERSON), 1, fp) == 1)
    if (!strcmp (pers.username, this_username))
      {
	fclose (fp);
	return 1;
      }

  fclose (fp);
  return 0;
}




void
add_outbox_post_office_events (char *username, SESSION *user)
{
#define TEMP_LEN 256		/*length of temp string : must be at least MAX_SUBJECT + (MAX_TO * MAX_NAME) + len of timestring */

  char outbox_path[MAX_PATH + 1];
  FILE *fp;
  OUTBOX_RECORD msg;
  char timestring[MAX_TIMESTRING + 1];
  char temp[TEMP_LEN];		/* must be at least MAX_SUBJECT + (MAX_TO * MAX_NAME) + len of timestring */
  struct tm;
  int viewing_own;		/* is this person viewing his own record? */
  int tip_len;

  snprintf (outbox_path, MAX_PATH + 1, "%s/%s", discussion_path,
	    OUTBOX_FNAME);
  fp = fopen (outbox_path, "r");
  if (fp)
    {
      while (fread (&msg, sizeof (OUTBOX_RECORD), 1, fp) == 1)
	{
	  new_event_node ();
	  tail->event = SEND_MSG;
	  tail->module = MAIL;
	  tail->time = msg.time_sent;

	  /* if msg is to this (faculty) user, or if faculty is viewing his own info
	     **  then show some detail
	     ** otherwise, say "(private Post Office message)")
	   */

	  viewing_own = !strcmp (user->username, username);
	  if (viewing_own || user_is_recipient (user->username, &msg))
	    {
	      strftime (timestring, MAX_TIMESTRING, TIME_FORMAT,
			localtime (&msg.time_sent));
	      snprintf (temp, TEMP_LEN, "%s (%s %s)", msg.subject,
			viewing_own ? msg.to : "", timestring);
	    }
	  else
	    strncpy (temp, hdf_get_value(hdf, "stats_post_office", "(private Post Office message)"), TEMP_LEN);
	  tip_len = strlen (temp);
	  tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
	  if (!tail->tooltip)
	    memory_error ();
	  strncpy (tail->tooltip, temp, tip_len + 1);
	  tail->date = normalized_time (msg.time_sent);
	}
      fclose (fp);
    }
#undef TEMP_LEN

}





void
add_post_office_events (char *username, CONFIG_STRUCT *conf, SESSION *user)
{

  set_discussion_names (conf->course_path, username, MAIL);	/* shared_news_util.c */

  add_inbox_postoffice_msgs (username, user);
  add_newfile_postoffice_msgs (username, user);

  add_outbox_post_office_events (username, user);
}






static void
add_news_msg(SESSION *target_user, SESSION *user, MSG_TREE_NODE *root, int grp)
{
  int user_sent;		/* did 'target_user->username' send this msg? */
  int tip_len;
  
 if(!root)
   return;
   
 user_sent = !strcmp(root->data.info.sender,target_user->username);

 new_event_node();
 tail->module = grp;
 tail->event = user_sent ? SEND_MSG : NEW_MSG;
 tail->time = root->data.info.time_sent;

 /* don't show details if this is TEAM_DISCUSSION and the teacher running this program
 ** is not on the sender's team
 ** for the Assignments module, if we're looking at the teacher's record, we must show
 ** who the message is 'to' or 'from'
  */

  if (grp == TEAM_DISCUSSION  && (root->data.info.sender_team != user->team))
	            strncpy (temp_tip, hdf_get_value(hdf, "stats_team_discussion", "(private Team Discussion message)"), MAX_PATH + 1);
  else if ((grp == ASSIGNMENTS) && !strcmp (user->username, target_user->username))
	    {
	      strftime (temp_timestring, MAX_TIMESTRING, TIME_FORMAT,
			localtime (&(root->data.info.time_sent)) );
	      snprintf (temp_tip, MAX_PATH + 1, "%s (%s %s)",
			root->data.info.subject,
			root->data.info.sender_realname, temp_timestring);
	    }
  else
	    {
	      strftime (temp_timestring, MAX_TIMESTRING, TIME_FORMAT,
			localtime (&(root->data.info.time_sent)) );
	      snprintf (temp_tip, MAX_PATH + 1, "%s (%s)",
			root->data.info.subject, temp_timestring);
	    }

  tip_len = strlen (temp_tip);
  tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
  if (!tail->tooltip)
	    memory_error ();
  strncpy (tail->tooltip, temp_tip, tip_len + 1);

  tail->date = normalized_time (root->data.info.time_sent);

 /** in addition to the above, each message MAY also be a READ_MSG event
  **  if 'target->username' did not send the message and if the message was read
   */

  if (!user_sent && root->user_data.time_read)
    {
      new_event_node ();

      tail->module = grp;
      tail->event = READ_MSG;
      tail->time = root->user_data.time_read;

      /* don't show details if this is TEAM_DISCUSSION and teacher running this program
         ** is not on the sender's team
       */

      if (grp == TEAM_DISCUSSION
	  && (root->data.info.sender_team != user->team))
	strncpy (temp_tip, hdf_get_value(hdf, "stats_team_discussion", "(private Team Discussion message)"), MAX_PATH + 1);
      else
	{
	  strftime (temp_timestring, MAX_TIMESTRING, TIME_FORMAT,
		    localtime (&(root->user_data.time_read)) );
	  snprintf (temp_tip, MAX_PATH + 1, "%s (%s %s)",
		    root->data.info.subject,
		    root->data.info.sender_realname, temp_timestring);
	}
   
      tip_len = strlen (temp_tip);
      tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
      if (!tail->tooltip)
    	memory_error ();
      strncpy (tail->tooltip, temp_tip, tip_len + 1);

      tail->date = normalized_time (root->user_data.time_read);
  } /* if (!user_sent && root->user_data.time_read)*/
 add_news_msg(target_user,user,root->next,grp);
 add_news_msg(target_user,user,root->reply,grp);
}

   




void
add_news_events (SESSION *target_user, int grp, CONFIG_STRUCT *conf, SESSION *user)
{
 int is_fac_teamteach;
 time_t current_time;

  set_discussion_names (conf->course_path, target_user->username, grp);	/* shared_news_util.c */
  is_fac_teamteach = ((target_user->group == FACULTY) && (grp == TEAM_TEACH));
  
  build_event_list_msg_tree(target_user,grp,is_fac_teamteach,&current_time);   /* shared_news_util.c */
  add_news_msg(target_user,user,tree_head,grp);
  free_msg_tree(tree_head);     /* shared_news_util.c */
}



void
add_login_events (char *username, CONFIG_STRUCT *conf)
{

  char logpath[MAX_PATH + 1];
  FILE *fp;
  time_t login_time;
  char timestring[MAX_TIMESTRING + 1];
  int tip_len;

  snprintf (logpath, MAX_PATH + 1, "%speople/%s/%s", conf->course_path,
	    username,ACCESS_LOG_FNAME);
  fp = fopen (logpath, "r");
  if (!fp)
    return;

  while (fread (&login_time, sizeof (time_t), 1, fp) == 1)
    {
      new_event_node ();

      tail->event = LOG_IN;
      tail->module = NO_MODULE;
      tail->time = login_time;
      strftime (timestring, MAX_TIMESTRING, TIME_FORMAT,
		localtime (&login_time));
      tip_len = strlen (timestring);
      tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
      if (!tail->tooltip)
	memory_error ();
      strncpy (tail->tooltip, timestring, tip_len + 1);

      tail->date = normalized_time (login_time);
    }

  fclose (fp);
}



void
add_chat_events (char *username, CONFIG_STRUCT *conf)
{

  char logpath[MAX_PATH + 1];
  FILE *fp;
  time_t login_time;
  char timestring[MAX_TIMESTRING + 1];
  int tip_len;

  snprintf (logpath, MAX_PATH + 1, "%speople/%s/chat_access.log",
	    conf->course_path, username);
  fp = fopen (logpath, "r");
  if (!fp)
    return;

  while (fread (&login_time, sizeof (time_t), 1, fp) == 1)
    {
      new_event_node ();

      tail->event = ENTER_CHAT;
      tail->module = CHATTER;
      tail->time = login_time;
      strftime (timestring, MAX_TIMESTRING, TIME_FORMAT,
		localtime (&login_time));
      tip_len = strlen (timestring);
      tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
      if (!tail->tooltip)
	memory_error ();
      strncpy (tail->tooltip, timestring, tip_len + 1);

      tail->date = normalized_time (login_time);
    }

  fclose (fp);
}


void
add_selftest_events (const char *username, CONFIG_STRUCT *conf)
{
#define MAX_LONGSTRING 500

  char logpath[MAX_PATH + 1];
  FILE *fp;
  HOTPOTATO_INFO_STRUCT info;
  char timestring1[MAX_TIMESTRING + 1];
  char tempstring[MAX_LONGSTRING + 1];
  int tip_len, hours, minutes, seconds;

  snprintf (logpath, MAX_PATH + 1, "%speople/%s/selftest/%s", 
                     conf->course_path, username, HOTPOT_FNAME);
  fp = fopen (logpath, "r");
  if (!fp)
    return;

  while (fread (&info, sizeof (HOTPOTATO_INFO_STRUCT), 1, fp) == 1)
    {
      new_event_node ();

      tail->event = TOOK_SELFTEST;
      tail->module = SELFTEST;
      tail->time = info.time_submitted;

      strftime (timestring1, MAX_TIMESTRING, TIME_FORMAT,
		localtime (&info.time_submitted));

      /* shared_potato_util.c */
      calculate_elapsed_time(info.elapsed_time, &hours, &minutes, &seconds);
      
      snprintf(tempstring,MAX_LONGSTRING,"%s: %s (%s)  %s %s  %02d:%02d:%02d",
              timestring1, info.exercise, info.orig_fname, 
              hdf_get_value(hdf, "stats_score", "Score:"), info.score, hours, minutes, seconds);
		
      tip_len = strlen (tempstring);
      tail->tooltip = (char *) malloc ((tip_len + 1) * sizeof (char));
      if (!tail->tooltip)
	memory_error ();
      strncpy (tail->tooltip, tempstring, tip_len + 1);

      tail->date = normalized_time (info.time_submitted);
    }

  fclose (fp);

#undef MAX_LONGSTRING
}



void
build_event_list (SESSION *target_user, CONFIG_STRUCT *conf, SESSION *user)
{

  add_post_office_events (target_user->username, conf, user);

  add_news_events (target_user, CLASS_DISCUSSION, conf, user);
  add_news_events (target_user, STUDENT_LOUNGE, conf, user);
  add_news_events (target_user, TEAM_DISCUSSION, conf, user);
  add_news_events (target_user, HANDOUTS_NOTICES, conf, user);
  add_news_events (target_user, INTERNET_RESOURCES, conf, user);
  add_news_events (target_user, ASSIGNMENTS, conf, user);
  add_news_events (target_user, TEAM_TEACH, conf, user);
  add_news_events (target_user, SELFTEST, conf, user);
  add_news_events (target_user, LECTURES, conf, user);
  //add_news_events (target_user, SURVEYS, conf, user);
  add_news_events (target_user, PODCASTS, conf, user);

  add_login_events (target_user->username, conf);
  add_chat_events (target_user->username, conf);
  
  add_selftest_events (target_user->username, conf);

  sort_event_list ();
}


void
free_event_list ()
{
  EVENT_NODE *ptr;

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



void
no_event_error (char *realname)
{
   cs_critical_error (ERR_NO_EVENTS_RECORDED_FOR_X, realname);
}




/* returns 'a' as same calendar date, but with  hour=1 minute=1 second=1  */
time_t normalized (time_t a)
{
  struct tm a_tm;

  a_tm = *localtime (&a);
  a_tm.tm_hour = 1;
  a_tm.tm_min = 1;
  a_tm.tm_sec = 1;

  return mktime (&a_tm);
}


/* returns a 'normalized' time_t value which is the first day of the 
** same month of 'a'
*/
time_t first_monthday (time_t a)
{
  struct tm a_tm;

  a_tm = *localtime (&a);
  a_tm.tm_hour = 1;
  a_tm.tm_min = 1;
  a_tm.tm_sec = 1;
  a_tm.tm_mday = 1;

  return mktime (&a_tm);
}


/* determine what dates our calendar will span
** 'warn' is set to true if the start date of our calendar is preceded by
** an event on the list
*/
void
set_calendar_dates (COURSE_DATES * calendar_date, COURSE_DATES * course_date,
		    time_t today, int *warn)
{



  /* always use the first day of the same month 
     ** as the START date specified by the user 
   */
  calendar_date->start_date = first_monthday (course_date->start_date);

  /* set 'warn' flag if an event happened before the start date */
  *warn = (head->date < calendar_date->start_date);

  /* use the earlier of 'today' or the specified course ending date */
  calendar_date->end_date =
    (today < course_date->end_date) ? today : course_date->end_date;

}










void
get_month_info (time_t current_day, int *lastday, int *month, int *year,
		int *dow)
{

  struct tm time_tm;

  time_tm = *localtime (&current_day);
  *month = time_tm.tm_mon;
  *year = time_tm.tm_year + 1900;
  *lastday = days_in_month (time_tm.tm_mon, time_tm.tm_year + 1900);  /*shared_time.c */
  *dow = time_tm.tm_wday;
}




void
set_day_events (time_t day, int year, int month, int this_day)
{
#define MAX_TMP_NAME 40
  EVENT_NODE *ptr;
  char *cptr;
  int i =0;
  char name[MAX_TMP_NAME];
  char timestring[MAX_TIMESTRING + 1];

  for (ptr = head; ptr; ptr = ptr->next)
    if (ptr->date == day)
      {
        snprintf(name, MAX_TMP_NAME, "event.%d.%d.%d.%d.module", year, month, this_day, i); 
        if(ptr->module == CLASS_DISCUSSION)
	     cs_set_value(name, "class_discussion");
        else if(ptr->module == STUDENT_LOUNGE )
           cs_set_value(name, "student_lounge");
        else if(ptr->module == ANONYMOUS_DISCUSSION)
           cs_set_value(name, "anonymous_discussion");
       else if(ptr->module ==  TEAM_DISCUSSION)
           cs_set_value(name, "team_discussion");
       else if(ptr->module ==  HANDOUTS_NOTICES)
           cs_set_value(name, "handouts_notices");
       else if(ptr->module ==  INTERNET_RESOURCES)
           cs_set_value(name, "internet_resources");
       else if(ptr->module ==  ASSIGNMENTS)
           cs_set_value(name, "assignments");
       else if(ptr->module ==  TEAM_TEACH)
           cs_set_value(name, "team_teach");
       else if(ptr->module == CHATTER)
           cs_set_value(name, "chatter");
       else if(ptr->module == MAIL)
           cs_set_value(name, "mail");
       else if(ptr->module == GRADES)
           cs_set_value(name, "grades");
       else if(ptr->module == SELFTEST)
          cs_set_value(name, "selftest");
       else if(ptr->module == LECTURES)
          cs_set_value(name, "lectures");
       else if(ptr->module == CLIPBOARD)
          cs_set_value(name, "clipboard");
       else if(ptr->module ==  PEOPLE)
           cs_set_value(name, "people");
       else if(ptr->module ==  SURVEYS)
           cs_set_value(name, "surveys");
       else if(ptr->module ==  PODCASTS)
           cs_set_value(name, "podcasts");
       else if(ptr->module ==  99)
           cs_set_value(name, "login");
    /* strip out some HTML chars that will cause trouble */
	for (cptr = ptr->tooltip; *cptr; cptr++)
	  if ((*cptr == 44) ||	/* quote */
	      (*cptr == 39) ||	/* another quote ' */
	      (*cptr == '&') ||
	      (*cptr == '<') || (*cptr == '>') || (*cptr == '"'))
	    *cptr = ' ';
        snprintf(name, MAX_TMP_NAME, "event.%d.%d.%d.%d.detail", year, month, this_day, i); 
        cs_set_value(name, ptr->tooltip);

        snprintf(name, MAX_TMP_NAME, "event.%d.%d.%d.%d.time", year, month, this_day, i); 
        strftime (timestring, MAX_TIMESTRING + 1, DOW_DATE_TIME_FORMAT,localtime (&(ptr->time)));
        cs_set_value(name, timestring);

        snprintf(name, MAX_TMP_NAME, "event.%d.%d.%d.%d.action", year, month, this_day, i); 
       if(ptr->event == NEW_MSG)
          cs_set_value(name, "new_msg");
       else if(ptr->event == READ_MSG)
          cs_set_value(name, "read_msg");
       else if(ptr->event == LOG_IN)
           cs_set_value(name, "log_in");
       else if(ptr->event == SEND_MSG)
          cs_set_value(name, "send_msg");
       else if(ptr->event == ENTER_CHAT)
           cs_set_value(name, "enter_chat");
       else if(ptr->event == TOOK_SELFTEST)
          cs_set_value(name, "took_selftest");

       i++;
      }
#undef MAX_TMP_NAME 
}



void
set_one_month_events (COURSE_DATES * calendar_date, COURSE_DATES * course_date,
		 int *done)
{

#define SECS_PER_DAY (24 * 60 * 60)
#define MAX_TMP_NAME 40 
  static time_t current_day = 0;
  static int i =0;
  char name[MAX_TMP_NAME];

  int day,			/* loop counter */
    lastday,			/* last day of month 28,29,30 or 31 */
    month,			/* 0 - 11 */
    dow,			/* day of week 0-6 */
    year;			/* eg, 1999 */

  if (!current_day)
    current_day = calendar_date->start_date;

  get_month_info (current_day, &lastday, &month, &year, &dow);
  snprintf(name, MAX_TMP_NAME, "event_month.%d.year", i);
  cs_set_int_value(name, year);
  snprintf(name, MAX_TMP_NAME, "event_month.%d.month", i);
  cs_set_int_value(name, month);
  snprintf(name, MAX_TMP_NAME, "event_month.%d.days", i);
  cs_set_int_value(name, lastday);
  snprintf(name, MAX_TMP_NAME, "event_month.%d.start_day", i);
  cs_set_int_value(name, dow);
  i++;
  cs_set_int_value("max_month", i);
  for (day = 1; day <= lastday; day++)
    {
      set_day_events (current_day, year, month, day);
      current_day += SECS_PER_DAY;
      current_day = normalized_time (current_day);
    }
  *done = current_day > calendar_date->end_date;
  

#undef SECS_PER_DAY
#undef MAX_TMP_NAME  
}



void
set_event_data (char *realname, COURSE_DATES * course_date,CONFIG_STRUCT *conf)
{
  time_t today_t;
  COURSE_DATES calendar_date;	/* the dates our calendar will actually span */
  int warn;			/* warn viewer that events precede start date */
  int done = 0;


  /* get current server time */
  today_t = time (NULL);

  set_calendar_dates (&calendar_date, course_date, today_t, &warn);
  if(warn)
     cs_set_int_value("warn", 1);

  cs_set_value("realname", realname);
  while (!done)
    set_one_month_events (&calendar_date, course_date, &done);

}





static
void get_actual_course_dates(COURSE_DATES *dates)
{
 EVENT_NODE *ptr;
 
 if(head)
 {
  dates->start_date = head->date;
  for(ptr=head; ptr->next; ptr=ptr->next);
  dates->end_date = ptr->date;
 }
 else    /* this should never be called w/out a valid list, but just in case */
 { 
  dates->start_date = 0;
  dates->end_date = 0;
 } 
}

void
set_user_info(SESSION *target, CONFIG_STRUCT *conf, const char *course, const char *key)
{
   CONFIG_STRUCT fake_conf;

   fake_conf = *conf;
   fake_conf.people = 1;

   cs_set_bio_link( target->username, course, &fake_conf, key, 0, 0, "user", 30);  /* shared_bio.c */
   cs_set_value("user.0.realname", target->realname);
   cs_set_value("user.0.username", target->username);

}



void
set_all_events (char *username, char *crs, char *key, 
                          CONFIG_STRUCT *conf, SESSION *user)
{

  COURSE_DATES course_date;	/* start/end dates specified by teacher */
  SESSION target_user;
  


  if(!get_user_info(username, &target_user,conf))   /* shared_person_list.c */
       cs_critical_error(ERR_PASSWD_PARSE_ERROR,"");

   get_lang_string(&hdf);
   build_event_list (&target_user, conf, user);
   set_user_info(&target_user, conf, crs, key);

  if (!head)
    no_event_error (target_user.realname);

  get_actual_course_dates(&course_date);

   set_event_data (target_user.realname, &course_date, conf);

  free_event_list ();
  hdf_destroy(&hdf);

}




int
main ()
{

  char course[MAX_PATH + 1];	/* from crs=? command line */
  char key[MAX_KEY + 1];	/* from id=? command line */
  char username[MAX_USERNAME + 1];	/* from username=? command line */
  SESSION user;
  CONFIG_STRUCT conf;		/* the configuration read from config file */

  cs_cgi_init();
  read_parameters (course, key, username);
  read_configuration_file (course, &conf);	/* shared_util.c */
  validate_key (key, &user, &conf);	/* shared_util.c */
  cs_set_course_info(&conf);
  if ( (user.group == FACULTY) || (user.group == ADMIN))
    set_all_events (username, course, key,  &conf, &user);
  else
    cs_critical_error (ERR_REQUEST_DENIED, "");
   #ifdef HIDE_MANHATTAN_USERNAMES
   cs_set_int_value("hide_manhattan_username", 1);
  #endif

  cs_cgi_display("stats_ind_calendar", 1);
  cs_cgi_destroy();
  return 0;			/* exit successfully */

}

