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

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

#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"

#define MAX_LOGINS 10		/* how many logins do we want to list? */

typedef struct user_log_node
{
  SESSION user;
  int unread_mail;		/* number of unread messages */
  int unread_discussion;
  int unread_lounge;
  int unread_team;
  int unread_handouts;
  int unread_inet;
  int unread_assignments;
  int unread_teamteach;
  int unread_grades;
  int unread_selftest;
  int unread_lectures;
  int unread_podcasts;
  int logins;			        /* total number of logins */
  time_t login_time[MAX_LOGINS];	/* list of most recent logins */

  struct user_log_node *next;

}
USER_LOG_NODE;


static void
read_parameters (char *course, char *key)
{

  cs_get_required_parameter ("crs", course, MAX_PATH);
  cs_get_required_parameter ("id", key, MAX_ID);

}



USER_LOG_NODE *
build_password_list( CONFIG_STRUCT *conf)
{
    USER_LOG_NODE *ptr, *current_ptr = 0;
    USER_LOG_NODE *head = 0;
    P_NODE *pptr;
    P_NODE *p_head = build_option_person_list (conf);
    for(pptr = p_head; pptr; pptr=pptr->next)
    {
             ptr = ( USER_LOG_NODE *)malloc(sizeof(USER_LOG_NODE));
             strncpy(ptr->user.username, pptr->data.username,MAX_USERNAME + 1);
             strncpy(ptr->user.realname, pptr->data.realname, MAX_NAME + 1);
             strncpy(ptr->user.id, pptr->data.id, MAX_ID + 1);
             ptr->user.group = pptr->data.group;
             ptr->user.team = pptr->data.team;
             ptr->next = 0;
             if(!head)
               head = ptr;
             else
               current_ptr->next = ptr;
             current_ptr = ptr;
    }
    free_option_person_list (p_head);
    return head;
}



/* returns the number of files ending in *.new in mail_dir */
int
new_files (char *mail_dir)
{
  DIR *dir_p;
  struct dirent *dir_entry_p;
  char *ptr;
  int count = 0;

  dir_p = opendir (mail_dir);
  if (!dir_p)
    cs_critical_error (ERR_OPENDIR_FAILED, "");


  while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      ptr = strstr (dir_entry_p->d_name, ".new");
      if (ptr && *(ptr + 4) == '\0')	/* i.e. if file ends in .new */
	count++;
    }
  closedir (dir_p);
  return count;
}



/* returns number of unread Post Office messages recorded in the inbox
 */
int
old_unread_mail (char *maildir)
{
  char inbox_path[MAX_PATH + 1];
  FILE *fp;
  INBOX_RECORD msg;
  int count = 0;

  snprintf (inbox_path, MAX_PATH + 1, "%s/%s", maildir, INBOX_FNAME);
  fp = fopen (inbox_path, "r");
  if (fp)
    {
      while (fread (&msg, sizeof (INBOX_RECORD), 1, fp) == 1)
	if ( (msg.newfile_info.how_sent != DELETED) && !msg.time_read)
	  count++;

      fclose (fp);
    }
  return count;
}



static int
unread_grades (const char *username, CONFIG_STRUCT *conf)
{

  FILE *fp;
  char fname[MAX_PATH + 1];

  snprintf (fname, MAX_PATH + 1, "%speople/%s/grades/%s", conf->course_path,
	    username, GRADES_NEW_FNAME);
  fp = fopen (fname, "r");
  if (fp)
    {
      fclose (fp);
      return 1;
    }
  return 0;
}






/* returns number of unread news messages 'user' has in 'grp' */
int
unread_news (SESSION *user, int grp, CONFIG_STRUCT *conf)

{
  int unread_msgs = 0;
  int total_msgs = 0;
  int flagged_msgs = 0;   /* needed for call to count_all_msgs() */
  int show_all_teamteach;
  time_t current_time;   /* needed for call to build_msg_tree */
  
  show_all_teamteach = (user->group == FACULTY) && (grp == TEAM_TEACH);
  
  set_discussion_names (conf->course_path, user->username, grp);	/* shared_news_util.c */
  build_msg_tree(user,grp,show_all_teamteach,0,&current_time); 		                /*shared_news_util.c */

  count_all_msgs(tree_head, &total_msgs, &unread_msgs, &flagged_msgs);                 /* shared_news_util.c */

  free_msg_tree(tree_head);                                             /* shared_news_util.c */

  return unread_msgs;
}



/*** NOTE: Anonymous discussion is deliberately left out in this
**** function because when someone posts a message it is 
**** automatically marked as read by them.  If I post 10 
**** Anonymous messages and I'm the only one who has 0 unread 
**** msgs, then teacher knows who posted the information...
**/
void
count_unread_msgs (CONFIG_STRUCT *conf, USER_LOG_NODE *head)
{
  USER_LOG_NODE *person_ptr;


  for (person_ptr = head; person_ptr; person_ptr = person_ptr->next)
    {

      /* POST OFFICE */
      if (!conf->postoffice)	/* if module is not enabled, don't blame student for not reading posts! */
	person_ptr->unread_mail = 0;
      else
	{
	  set_discussion_names (conf->course_path, person_ptr->user.username, MAIL);	/* shared_news_util.c */
	  person_ptr->unread_mail = old_unread_mail (discussion_path);
	  person_ptr->unread_mail += new_files (discussion_path);
	}

      /* GRADES */
      person_ptr->unread_grades =
	conf->grades ? unread_grades (person_ptr->user.username, conf) : 0;


      person_ptr->unread_discussion =
	conf->discussion ? unread_news (&(person_ptr->user),
				       CLASS_DISCUSSION, conf) : 0;
      person_ptr->unread_lounge =
	conf->lounge ? unread_news (&(person_ptr->user), STUDENT_LOUNGE, conf) : 0;
      person_ptr->unread_team =
	conf->team ? unread_news (&(person_ptr->user), TEAM_DISCUSSION, conf) : 0;
      person_ptr->unread_handouts =
	conf->syllabus ? unread_news (&(person_ptr->user),
				     HANDOUTS_NOTICES, conf) : 0;
      person_ptr->unread_inet =
	conf->internet ? unread_news (&(person_ptr->user),
				     INTERNET_RESOURCES, conf) : 0;
      person_ptr->unread_assignments =
	conf->assignments ? unread_news (&(person_ptr->user),
					ASSIGNMENTS, conf) : 0;
      person_ptr->unread_teamteach =
	conf->team_teach ? unread_news (&(person_ptr->user), TEAM_TEACH, conf) : 0;
      person_ptr->unread_selftest =
	conf->selftest ? unread_news (&(person_ptr->user), SELFTEST, conf) : 0;
      person_ptr->unread_lectures =
	conf->lectures ? unread_news (&(person_ptr->user), LECTURES, conf) : 0;
      person_ptr->unread_podcasts =
	conf->podcasts ? unread_news (&(person_ptr->user), PODCASTS, conf) : 0;
    }
}


/* we'll just take the size of the access log file 
   ** and divide it by the size of each record 
 */
void
count_total_logins (CONFIG_STRUCT *conf, USER_LOG_NODE *head)
{
  USER_LOG_NODE *person_ptr;
  char logpath[MAX_PATH + 1];
  FILE *fp;

  for (person_ptr = head; person_ptr; person_ptr = person_ptr->next)
    {

      snprintf (logpath, MAX_PATH + 1, "%speople/%s/%s",
		conf->course_path, person_ptr->user.username,ACCESS_LOG_FNAME);
      fp = fopen (logpath, "r");
      if (!fp)
	person_ptr->logins = 0;
      else
	{
	  fseek (fp, 0, SEEK_END);
	  person_ptr->logins = ftell (fp) / sizeof (time_t);
	  fclose (fp);
	}
    }
}




void
record_recent_logins (CONFIG_STRUCT *conf, USER_LOG_NODE *head)
{
  USER_LOG_NODE *person_ptr;
  char logpath[MAX_PATH + 1];
  FILE *fp;
  int i;

  for (person_ptr = head; person_ptr; person_ptr = person_ptr->next)
    {

      for (i = 0; i < MAX_LOGINS; i++)
	person_ptr->login_time[i] = (time_t) 0;

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

      if (fp)
	{
	  for (i = 0; i < MAX_LOGINS; i++)
	    {
	      if (fseek (fp, -((i + 1) * sizeof (time_t)), SEEK_END) == -1)
		break;
	      if (fread (&(person_ptr->login_time[i]), sizeof (time_t), 1, fp)
		  != 1)
		cs_critical_error (ERR_FREAD_FAILED, "");
	    }
	  fclose (fp);
	}
    }
}



void
free_person_list (USER_LOG_NODE *head)
{
  USER_LOG_NODE *person_ptr;

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


void
list_recent_logins (char *crs, char *id, CONFIG_STRUCT *conf)
{
 #define MAX_TMP_NAME 30
  USER_LOG_NODE *person_ptr;
  int i =0;
  char timestring[MAX_TIMESTRING + 1];
  time_t now;
  int row;
  USER_LOG_NODE *head;
  char url[MAX_PATH + 1];
  char name[MAX_TMP_NAME];
  int count, j=0;
  
  
  /* get current server time */
  now = time (NULL);
  strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT,
	    localtime (&now));


  head = build_password_list (conf);
  count_unread_msgs (conf, head);
  count_total_logins (conf, head);
  record_recent_logins (conf, head);

  cs_set_course_info(conf);
  cs_set_current_time();
  snprintf(url, MAX_PATH +1, "%s?id=%s&amp;crs=%s", "stats_menu", id, crs);
  cs_set_value("back_url", url);

  cs_set_int_value("max_logins", MAX_LOGINS); 
  	  
  for (person_ptr = head, row = 1; person_ptr;
       person_ptr = person_ptr->next, row++)
    {
      snprintf(url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;username=%s&amp;prog=%s","stats_ind_calendar",crs, id, person_ptr->user.username, "stats_recent_logins");
      snprintf(name, MAX_TMP_NAME, "logins.%d.url", j);
      cs_set_value(name, url);
      
      snprintf(name, MAX_TMP_NAME, "logins.%d.id", j);
      cs_set_value(name, person_ptr->user.id);

      snprintf(name, MAX_TMP_NAME, "logins.%d.realname", j);
      cs_set_value(name, person_ptr->user.realname);

      snprintf(name, MAX_TMP_NAME, "logins.%d.unread_msg_count", j);
	 count =person_ptr->unread_mail + person_ptr->unread_discussion + person_ptr->unread_lounge +
	 person_ptr->unread_team + person_ptr->unread_handouts +person_ptr->unread_inet + person_ptr->unread_assignments +
	 person_ptr->unread_teamteach + person_ptr->unread_grades +person_ptr->unread_selftest + person_ptr->unread_lectures + person_ptr->unread_podcasts;
      cs_set_int_value(name,count); 

      snprintf(name, MAX_TMP_NAME, "logins.%d.logincount", j);
      cs_set_int_value(name,person_ptr->logins);

      for (i = 0; i < MAX_LOGINS; i++)
	{
	  if (person_ptr->login_time[i])
	    {
	      strftime (timestring, MAX_TIMESTRING,
			SPLIT_DOW_DATE_TIME_FORMAT,
			localtime (&person_ptr->login_time[i]));

              snprintf(name, MAX_TMP_NAME, "logins.%d.times.%d", j, i);
              cs_set_value(name, timestring);
	    }
	}
      j++;
    }

  free_person_list (head);
 #undef MAX_TMP_NAME 
}



int
main ()
{

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

  cs_cgi_init();

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

  if ((user.group == FACULTY) || (user.group == ADMIN))
    list_recent_logins (course, key, &conf);
  else
    cs_critical_error (ERR_REQUEST_DENIED, "");
    
  cs_cgi_display("stats_recent_logins", 1);
  cs_cgi_destroy();
  return 0;			/* exit successfully */

}

