#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <locale.h>
#include <monetary.h>
              
#include "global.h"

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_file_util.h"
#include "manhat-lib/shared_person_list.h"
#include "manhat-lib/shared_super_key.h"
#include "manhat-lib/shared_course_search.h"


typedef enum {DISK_USAGE, MSG_COUNT, WEBSITE_COUNT, FILE_COUNT, STUDENT_COUNT, CHAT_MSG_COUNT} SORTS;


typedef struct group_summary {
  int classroom_count;

  double disk_usage;
  double average_disk_usage;
  double median_disk_usage;

  double msg_disk_usage;
  double file_disk_usage;
  double website_disk_usage;
  double misc_disk_usage;
  
  int msg_count;
  double median_msg_count;
  
  
  int website_count;
  double median_website_count;
  
  int file_count;
  double median_file_count;
  
  
  int chat_msg_count;
  double median_chat_msg_count;
  
  
  int student_count;
  double median_student_count;
  
  
  CONFIG_STRUCT conf;
 } GROUP_SUMMARY;
 

typedef struct module_node {
  char module_name[MAX_FILENAME + 1];
  double disk_usage;
  double msg_disk_usage;
  double file_disk_usage;
  double website_disk_usage;
  double misc_disk_usage;
  int msg_count;
  int website_count;
  int file_count;
  struct module_node *next;
 } MODULE_NODE; 





typedef struct course_node
  {
    char dirname[MAX_FILENAME + 1];
    CONFIG_STRUCT conf;
    int teacher_count;
    int student_count;
    double disk_usage;
    double msg_disk_usage;
    double file_disk_usage;
    double website_disk_usage;
    double misc_disk_usage;
    int msg_count;
    int chat_msg_count;
    int file_count;
    int website_count;
    MODULE_NODE *module_head;
    struct course_node *next;
  } COURSE_NODE; 
    


COURSE_NODE *course_head = (COURSE_NODE *) 0;


static void
read_parameters (char *key, char *group)
{
  cs_get_required_parameter("id", key, MAX_KEY);
  cs_get_required_parameter("subdir", group, MAX_FILENAME);
  
}


static void
my_crash(const char *msg)
{
 puts(msg);
 puts("</pre>\n</body>\n</html>");
 exit(1);
} 




void *my_malloc(size_t size)
{
 void *ptr;
 
 ptr = malloc(size);
 if(!ptr)
    my_crash("malloc() failed");
 return ptr;
}


static
void my_strncpy(char *dest, char *src, int len)
{
 strncpy(dest, src, len);
 *(dest + len - 1) = '\0';
}






   
static COURSE_NODE *
new_course_node(char *group_dir, char *course_dir)
{
 COURSE_NODE *ptr;
 char course_name[MAX_PATH + 1];
 CONFIG_STRUCT conf;
 P_NODE *person_head;
 
 snprintf(course_name, MAX_PATH + 1, "%s%s/%s", COURSE_PATH, group_dir, course_dir);
 if(!read_configuration_file_ok(course_name, &conf))    /* shared_util.c */
     return (COURSE_NODE *) 0;
 
 if(!course_head)
   {
     course_head = (COURSE_NODE *) my_malloc(sizeof(COURSE_NODE));
     ptr = course_head;
    }
  else
   {
    for(ptr = course_head; ptr->next; ptr=ptr->next);
    ptr->next = my_malloc(sizeof(COURSE_NODE));
    ptr = ptr->next;
   }
   
   
   my_strncpy(ptr->dirname, course_dir, MAX_FILENAME + 1);
   ptr->conf = conf;
   
   person_head = build_option_person_list(&conf);       /* shared_person_list.c */
   ptr -> teacher_count = count_faculty(person_head);    /* shared_person_list.c */
   ptr -> student_count = count_students(person_head);   /* shared_person_list.c */
   free_option_person_list(person_head);   /* shared_person_list.c */   
   
   ptr->disk_usage = 0;
   ptr->msg_disk_usage = 0;
   ptr->file_disk_usage = 0;
   ptr->website_disk_usage = 0;
   ptr->misc_disk_usage = 0;
   
   ptr->msg_count = 0;
   ptr->chat_msg_count = 0;
   ptr->file_count = 0;
   ptr->website_count = 0;
   ptr->module_head = (MODULE_NODE *) 0;
   ptr -> next = (COURSE_NODE *) 0;
   
      
 return ptr;
}




 







static MODULE_NODE *
find_module_node (COURSE_NODE *course_ptr, char *module_name)
{
 MODULE_NODE *module_ptr;
 int found;
 
 if(!course_ptr->module_head)
  {
   course_ptr->module_head = (MODULE_NODE *) my_malloc(sizeof(MODULE_NODE));
   module_ptr = course_ptr->module_head;
   found = 0;
  }
 else
  {
   for(found = 0, module_ptr = course_ptr->module_head; module_ptr->next; module_ptr=module_ptr->next)
    {
      found = !strcmp(module_name, module_ptr->module_name);  
      if(found)
         break;
    }
   if(!found)
    {
      module_ptr->next = (MODULE_NODE *) my_malloc(sizeof(MODULE_NODE));     
      module_ptr = module_ptr -> next;
    }
   }

  if(!found)
   {
     my_strncpy(module_ptr->module_name, module_name, MAX_FILENAME + 1);
     module_ptr -> disk_usage = 0;
     module_ptr -> msg_disk_usage = 0;
     module_ptr -> file_disk_usage = 0;
     module_ptr -> website_disk_usage = 0;
     module_ptr -> misc_disk_usage = 0;
     module_ptr -> msg_count = 0;
     module_ptr -> website_count = 0;
     module_ptr -> file_count = 0;
     module_ptr -> next = (MODULE_NODE *) 0;
   }

 return module_ptr;
}

  
  
static         
long file_size(char *dir, char *fname)
{
 char path[MAX_PATH + 1];
 struct stat buf;
 
 snprintf(path, MAX_PATH + 1, "%s/%s", dir, fname);
 if(!stat(path, &buf)) // && !(S_ISDIR (buf.st_mode)) )
    return (long) buf.st_size;
 else
    return (long) 0;
}

    
    



static 
long dir_size(char *dir, char *fname)
{
 char *path, *filepath;
 DIR *dir_p;
 struct dirent *dir_entry_p;
 long filesize = 0;
 struct stat buf;
 int malloc_size;
 
 
 malloc_size = strlen(dir) + strlen(fname) + 10;
 
 path = (char *) my_malloc(malloc_size);
 filepath = (char *) my_malloc(malloc_size + MAX_PATH);
 sprintf(path, "%s/%s", dir, fname);
 
 dir_p = opendir (path);
 if (dir_p)
  { 
   filesize += file_size(path, ".");    /* count size of the directory entry itself */
    while (NULL != (dir_entry_p = readdir (dir_p)))
       {  
        if (strcmp (dir_entry_p->d_name, ".")
	      && strcmp (dir_entry_p->d_name, ".."))
        {
          snprintf(filepath, malloc_size + MAX_PATH, "%s/%s", path, dir_entry_p->d_name);
          if(!stat(filepath, &buf))
            {
              if(S_ISDIR (buf.st_mode))
                 filesize += dir_size(path, dir_entry_p->d_name);
              else
                 filesize += (long) buf.st_size;
 
            }     
        }
       }
    closedir(dir_p);   
   }         
                 
 
 free(path);
 free(filepath);
 return filesize;
}
 


/** Need to find filenames of the form "msg_xxxxxx" and "xxxxxx.msg"
*** and "fil_xxxxxx" and "xxxxxxx.fil" and so on.
***
*** Using 'msg' as an example, ptr points to substring 'msg' in larger 
*** null-terminated string 'string'.  'len' is the string length of 'msg'.
***
*** does string either start with msg_ or end with .msg ?
**/

static 
int starts_ends_with(char *target, char *string)
{
int len = strlen(target);
char *ptr;

ptr = strstr(string, target);

if(ptr)
 return ((ptr == string) &&
          (*(ptr + len) == '_') ) || 
         ( (*(ptr - 1) == '.')  &&
           (*(ptr + len) == '\0') );
           
else
   return 0;             
}




static void
count_user_module_usage(COURSE_NODE *course_ptr,  char *module_dir, char *user_path)
{
 char module_path[MAX_PATH + 1];
 MODULE_NODE *module_ptr;
 DIR *dir_p;
 struct dirent *dir_entry_p;
 long filesize;

 
 snprintf(module_path, MAX_PATH + 1, "%s/%s", user_path, module_dir);
 
 dir_p = opendir (module_path);
 if (dir_p)
  { 
    module_ptr = find_module_node(course_ptr, module_dir);
    course_ptr->misc_disk_usage += file_size(module_path, ".");     /* count size of this module directory */
    while (NULL != (dir_entry_p = readdir (dir_p)))
       {  
        if (strcmp (dir_entry_p->d_name, ".")
	      && strcmp (dir_entry_p->d_name, ".."))
        {
         if(starts_ends_with("msg", dir_entry_p->d_name))
          {
           module_ptr -> msg_count++;
           filesize = file_size(module_path, dir_entry_p->d_name);
           module_ptr -> msg_disk_usage += filesize;
          } 
         else if(starts_ends_with("fil",dir_entry_p->d_name))
          {
           module_ptr -> file_count++;
           filesize = file_size(module_path, dir_entry_p->d_name);
           module_ptr -> file_disk_usage += filesize;
          }
         else if(starts_ends_with("dir", dir_entry_p->d_name)   ||   /* attached website */
                 starts_ends_with("hot", dir_entry_p->d_name) )     /* attached hot exam in assign module */
          {
           module_ptr -> website_count++;
           filesize = dir_size(module_path, dir_entry_p->d_name);
           module_ptr->website_disk_usage += filesize;
          }
         else    /* misc. files, e.g. 'inf' files, filter.dat, inbox.dat, last_gather.dat, etc ... */
          {
            filesize = file_size(module_path, dir_entry_p->d_name);
            module_ptr->misc_disk_usage += filesize;
          }
        
          module_ptr -> disk_usage += filesize;    /* update total */
        }
       }
    closedir (dir_p);
  }
 else   /* might have found a regular file in this user's directory - such as access_log.dat */
     course_ptr->misc_disk_usage += file_size(user_path, module_dir);
} 








static void 
analyze_user(char *people_dir, char *user_dir, COURSE_NODE *course_ptr)
{
 char path[MAX_PATH + 1];
 DIR *dir_p;
 struct dirent *dir_entry_p;


 snprintf(path, MAX_PATH + 1, "%s/%s", people_dir, user_dir);
 
 
 
 dir_p = opendir (path);
 if (dir_p)
   { 
    course_ptr->misc_disk_usage += file_size(path, ".");    /* count size of this user's directory as 'misc_disk_usage */
    while (NULL != (dir_entry_p = readdir (dir_p)))
       {  
        if (strcmp (dir_entry_p->d_name, ".")
	      && strcmp (dir_entry_p->d_name, ".."))
        {
          count_user_module_usage(course_ptr,dir_entry_p->d_name, path);
        }

      }
    closedir(dir_p);                 
  }                      

}


static 
void update_module_totals(COURSE_NODE *course_ptr)
{
 MODULE_NODE *module_ptr;
 
 for(module_ptr = course_ptr->module_head; module_ptr; module_ptr = module_ptr->next)
  {
     course_ptr->disk_usage += module_ptr->disk_usage;
     course_ptr->msg_disk_usage += module_ptr->msg_disk_usage;
     course_ptr->file_disk_usage += module_ptr -> file_disk_usage;
     course_ptr->website_disk_usage += module_ptr -> website_disk_usage;
     course_ptr->misc_disk_usage += module_ptr->misc_disk_usage;
     course_ptr->msg_count += module_ptr->msg_count;
     course_ptr->website_count += module_ptr->website_count;
     course_ptr->file_count += module_ptr->file_count;
  }
}  
  
    
    

/* This code taken from chat_transcript.c */
    
static void    
count_chat_msgs(COURSE_NODE *course_ptr, char *group_dir, char *course_dir)
{
  FILE *fp;
  char path[MAX_PATH + 1];

  time_t msg_time;		/* the CHAT_MSG_LOG has these data items in this order */
  int deleted;
  char sender[40];
  int msg_len;
  char *msg = (char *) 0;
  
  course_ptr->chat_msg_count = 0;

  snprintf (path, MAX_PATH + 1, "%s%s/%s/%s", COURSE_PATH, group_dir, course_dir, CHAT_MSG_LOG);
  fp = fopen (path, "r");
  if (!fp)
    return;

  while (fread (&msg_time, sizeof (time_t), 1, fp) == 1)
    {
      /* was this message deleted by the teacher ? */
      if (fread (&deleted, sizeof (int), 1, fp) != 1)
          break;

      /* who typed the message (realname) **/
      if (fread (sender, 40 * sizeof (char), 1, fp) != 1)
          break;


      /* how many chars were typed - what is the length of the message? */ 
      if (fread (&msg_len, sizeof (int), 1, fp) != 1)
          break;

     /** allocate space for the 'raw' message **/
      msg = (char *) my_malloc ((msg_len + 1) * sizeof (char));

      /* read the raw message */
      if ((int)fread (msg, sizeof (char), msg_len, fp) != msg_len)
                 break;
      msg[msg_len] = '\0';

      /* need to escape html chars */

      if(msg)
       {
         free (msg);
         msg = (char *) 0;
       }
    
    course_ptr->chat_msg_count++;
    }

  fclose (fp);

}




static void
count_misc_course_data(COURSE_NODE *course_ptr,  char *group_dir, char *course_dir)
{
 char path[MAX_PATH + 1];
 char filepath[MAX_PATH + 1];
 DIR *dir_p;
 struct dirent *dir_entry_p;
 struct stat buf;

 snprintf(path, MAX_PATH + 1, "%s%s/%s", COURSE_PATH, group_dir, course_dir);
 dir_p = opendir (path);
 if (dir_p)
  { 
      course_ptr->misc_disk_usage += file_size(path, ".");    /* count size of THIS course directory */
      while (NULL != (dir_entry_p = readdir (dir_p)))
       {  
        if (strcmp (dir_entry_p->d_name, ".")
	      && strcmp (dir_entry_p->d_name, "..") 
	      && strcmp (dir_entry_p->d_name, "people") )   /* since the people directory has already been dealt with */
         {
           snprintf(filepath, MAX_PATH + 1, "%s/%s", path, dir_entry_p->d_name);
           if(!stat(filepath, &buf))
            {
              if (S_ISDIR(buf.st_mode))
               {
                course_ptr->misc_disk_usage += dir_size(path, dir_entry_p->d_name);    
               }
              else
               {
                course_ptr->misc_disk_usage += buf.st_size;
               }
               
            }
          }
        }
       closedir(dir_p);
   }
}   



static
void update_total_disk_usage(COURSE_NODE *course_ptr)
{

 course_ptr->disk_usage = course_ptr->msg_disk_usage + 
                               course_ptr->file_disk_usage + 
                                   course_ptr->website_disk_usage + 
                                              course_ptr->misc_disk_usage;
}



static
void analyze_course(char *group_dir, char *course_dir)
{
 char path[MAX_PATH + 1];
 DIR *dir_p;
 struct dirent *dir_entry_p;
 COURSE_NODE *course_ptr;
 static int count = 0;
 
 snprintf(path, MAX_PATH + 1, "%s%s/%s/%s", COURSE_PATH, group_dir, course_dir, PEOPLE_DIR);
 dir_p = opendir (path);
 if (dir_p)
  { 
   course_ptr = new_course_node(group_dir, course_dir);
   if(course_ptr)
    {
       count++;
       printf("%s ", course_dir);
       if(!(count % 10))
         printf("\n");
 
       fflush(stdout);


      course_ptr -> misc_disk_usage += file_size(path, ".");   /* count size of the 'people' directory */
      while (NULL != (dir_entry_p = readdir (dir_p)))
       {  
        if (strcmp (dir_entry_p->d_name, ".")
	      && strcmp (dir_entry_p->d_name, ".."))
         {
           analyze_user(path, dir_entry_p->d_name, course_ptr);
         }       
       }
      update_module_totals(course_ptr);
      count_chat_msgs(course_ptr, group_dir, course_dir);
      count_misc_course_data(course_ptr,group_dir, course_dir);   
      update_total_disk_usage(course_ptr);
      
    }    
    closedir (dir_p);
  }
}   



static void clear_summary(GROUP_SUMMARY *summary)
{
 summary->classroom_count = 0;
 
 summary->disk_usage = 0;
 summary->average_disk_usage = 0.0;
 summary->median_disk_usage = 0.0;
 
 summary->msg_disk_usage = 0;
 summary->file_disk_usage = 0;
 summary->website_disk_usage = 0;
 summary->misc_disk_usage = 0;
 
 summary->msg_count = 0;
 summary->median_msg_count = 0;
 
 
 summary->website_count = 0;
 summary->median_website_count = 0;
 
 
 summary->file_count = 0;
 summary->median_file_count = 0;
 
 summary->chat_msg_count = 0;
 summary->median_chat_msg_count = 0;
 
 
 summary->student_count = 0;
 summary->median_student_count = 0;
 
 summary->conf.assignments = 0;
 summary->conf.syllabus = 0;
 summary->conf.discussion = 0;
 summary->conf.postoffice = 0;
 summary->conf.internet = 0;
 summary->conf.lounge = 0;
 summary->conf.chat = 0;
 summary->conf.anonymous = 0;
 summary->conf.team = 0;
 summary->conf.team_teach = 0;
 summary->conf.grades = 0;
 summary->conf.selftest = 0;
 summary->conf.lectures = 0;
 summary->conf.people = 0;
 summary->conf.surveys = 0;
 summary->conf.podcasts = 0;
 
}



static void
add_to_summary(COURSE_NODE *course_ptr, GROUP_SUMMARY *summary)
{

 summary->classroom_count++;
 summary->disk_usage += course_ptr->disk_usage;;
 summary->msg_disk_usage += course_ptr->msg_disk_usage;
 summary->file_disk_usage += course_ptr->file_disk_usage;
 summary->website_disk_usage += course_ptr->website_disk_usage;
 summary->misc_disk_usage += course_ptr->misc_disk_usage;
 
 summary->msg_count += course_ptr->msg_count;
 summary->website_count += course_ptr->website_count;
 summary->file_count += course_ptr -> file_count;
 
 summary->chat_msg_count += course_ptr->chat_msg_count;
 summary->student_count += course_ptr->student_count;
 
 if(course_ptr->conf.assignments)
      summary->conf.assignments++;

 if(course_ptr->conf.syllabus)
     summary->conf.syllabus++;

 if(course_ptr->conf.discussion)
     summary->conf.discussion++;

 if(course_ptr->conf.postoffice)
     summary->conf.postoffice++;

 if(course_ptr->conf.internet)
     summary->conf.internet++;

 if(course_ptr->conf.lounge)
     summary->conf.lounge++;

 if(course_ptr->conf.chat)
     summary->conf.chat++;

 if(course_ptr->conf.anonymous)
    summary->conf.anonymous++;

 if(course_ptr->conf.team)
    summary->conf.team++;

 if(course_ptr->conf.team_teach)
    summary->conf.team_teach++;
    
 if(course_ptr->conf.grades)
     summary->conf.grades++;

 if(course_ptr->conf.selftest)
     summary->conf.selftest++;

 if(course_ptr->conf.lectures)
     summary->conf.lectures++;

 if(course_ptr->conf.people)
     summary->conf.people++;

 if(course_ptr->conf.surveys)
      summary->conf.surveys++;
      
 if(course_ptr->conf.podcasts)
      summary->conf.podcasts++;

}      
      


static
int course_node_cmp( COURSE_NODE *a, COURSE_NODE *b, SORTS sort)
{

 if(sort == MSG_COUNT)
  {
    if(a->msg_count > b->msg_count)
      return -1;
    else if(a->msg_count < b->msg_count)
      return +1;
    else
      return 0;
  }
 else if(sort == WEBSITE_COUNT)
  {
    if(a->website_count > b->website_count)
      return -1;
    else if(a->website_count < b->website_count)
      return +1;
    else
      return 0;
  }
 else if(sort == FILE_COUNT)
  {
    if(a->file_count > b->file_count)
      return -1;
    else if(a->file_count < b->file_count)
      return +1;
    else
      return 0;
  } 
 else if(sort == STUDENT_COUNT)
  {
    if(a->student_count > b->student_count)
      return -1;
    else if(a->student_count < b->student_count)
      return +1;
    else
      return 0;
  } 
 else if(sort == CHAT_MSG_COUNT)
  {
    if(a->chat_msg_count > b->chat_msg_count)
      return -1;
    else if(a->chat_msg_count < b->chat_msg_count)
      return +1;
    else
      return 0;
  } 
 else     /* sort == DISK_USAGE */
  {
   if(a->disk_usage > b->disk_usage)
      return -1;
   else if(a->disk_usage < b->disk_usage)
      return +1;
   else
      return 0;
  }     
}
     
     
     
     
     
static
COURSE_NODE *
sort_course_list (COURSE_NODE *course_head, SORTS sort)
{
  COURSE_NODE *q, *tail, *p, *r;
  if (course_head != NULL)
    {
      tail = course_head;
      while (tail->next)
	{
	  q = tail->next;
	  if (course_node_cmp (q, course_head, sort) <  0)
	    {
	      tail->next = q->next;
	      q->next = course_head;
	      course_head = q;
	    }
	  else
	    {
	      r = course_head;
	      p = r->next;
	      while (course_node_cmp (q,p, sort) > 0)
		{
		  r = p;
		  p = r->next;
		}
	      if (q == p)
		tail = q;
	      else
		{
		  tail->next = q->next;
		  q->next = p;
		  r->next = q;
		}
	    }
	}
    }
    return  course_head;
}
     





static void
get_median_disk_usage(GROUP_SUMMARY *summary, int middle, int median_requires_average)
{
 COURSE_NODE *ptr;
 int i;
 double result = 0.0;
 
 course_head = sort_course_list(course_head, DISK_USAGE);
 
 for(i=1, ptr=course_head; (i < middle) && ptr; ptr=ptr->next, i++);
 
 if(ptr)
   {
     result = ptr -> disk_usage;
     if(median_requires_average && (ptr->next))
         result = (result + ptr->next->disk_usage) / 2.0;
         
   }
 summary->median_disk_usage = result;
}



static void
 get_median_msg_count(GROUP_SUMMARY *summary, int middle, int median_requires_average)
{
 COURSE_NODE *ptr;
 int i;
 double result = 0.0;
 
 course_head = sort_course_list(course_head, MSG_COUNT);
 
 for(i=1, ptr=course_head; (i < middle) && ptr; ptr=ptr->next, i++);
 
 if(ptr)
   {
     result = ptr -> msg_count;
     if(median_requires_average && (ptr->next))
         result = (result + ptr->next->msg_count) / 2.0;
         
   }
 summary->median_msg_count = result;
}





static void
 get_median_website_count(GROUP_SUMMARY *summary, int middle, int median_requires_average)
{
 COURSE_NODE *ptr;
 int i;
 double result = 0.0;
 
 course_head = sort_course_list(course_head, WEBSITE_COUNT);
 
 for(i=1, ptr=course_head; (i < middle) && ptr; ptr=ptr->next, i++);
 
 if(ptr)
   {
     result = ptr -> website_count;
     if(median_requires_average && (ptr->next))
         result = (result + ptr->next->website_count) / 2.0;
         
   }
 summary->median_website_count = result;
}
 
      
static void
 get_median_file_count(GROUP_SUMMARY *summary, int middle, int median_requires_average)
{
 COURSE_NODE *ptr;
 int i;
 double result = 0.0;
 
 course_head = sort_course_list(course_head, FILE_COUNT);
 
 for(i=1, ptr=course_head; (i < middle) && ptr; ptr=ptr->next, i++);
 
 if(ptr)
   {
     result = ptr -> file_count;
     if(median_requires_average && (ptr->next))
         result = (result + ptr->next->file_count) / 2.0;
         
   }
 summary->median_file_count = result;
}




static void
 get_median_student_count(GROUP_SUMMARY *summary, int middle, int median_requires_average)
{
 COURSE_NODE *ptr;
 int i;
 double result = 0.0;
 
 course_head = sort_course_list(course_head, STUDENT_COUNT);
 
 for(i=1, ptr=course_head; (i < middle) && ptr; ptr=ptr->next, i++);
 
 if(ptr)
   {
     result = ptr -> student_count;
     if(median_requires_average && (ptr->next))
         result = (result + ptr->next->student_count) / 2.0;
         
   }
 summary->median_student_count = result;
}




static void
get_median_chat_msg_count(GROUP_SUMMARY *summary, int middle, int median_requires_average)
{
 COURSE_NODE *ptr;
 int i;
 double result = 0.0;
 
 course_head = sort_course_list(course_head, CHAT_MSG_COUNT);
 
 for(i=1, ptr=course_head; (i < middle) && ptr; ptr=ptr->next, i++);
 
 if(ptr)
   {
     result = ptr -> chat_msg_count;
     if(median_requires_average && (ptr->next))
         result = (result + ptr->next->chat_msg_count) / 2.0;
         
   }
 summary->median_chat_msg_count = result;
}



   


static
void find_middle(int count, int *middle, int *median_requires_average)
{

if(count <= 1)
 {
  *middle = count;
  *median_requires_average = 0;
 }
else
  {
   *middle = count / 2;
   if( count % 2)   /* if odd */
     {
      *middle++;
      *median_requires_average = 0;
     }
   else
    {
     *median_requires_average = 1;
    }
  } 
}  
  
 


static void
summarize_group(GROUP_SUMMARY *summary)
{
 COURSE_NODE *ptr;
 int middle;
 int median_requires_average;
 
 
 clear_summary(summary);
 for(ptr=course_head; ptr; ptr=ptr->next)
     add_to_summary(ptr, summary);

 summary->average_disk_usage = summary->disk_usage / (double) summary->classroom_count;
 
 find_middle(summary->classroom_count, &middle, &median_requires_average);
 get_median_disk_usage(summary, middle, median_requires_average);
 get_median_msg_count(summary, middle, median_requires_average);
 get_median_website_count(summary, middle, median_requires_average);
 get_median_file_count(summary, middle, median_requires_average); 
 get_median_student_count(summary, middle, median_requires_average);  
 get_median_chat_msg_count(summary, middle, median_requires_average);
 

}


     
     
     
     



static void
analyze_course_group(char *group, GROUP_SUMMARY *summary)
{
 char path[MAX_PATH + 1];
 DIR *dir_p;
 struct dirent *dir_entry_p;

 
 printf("Analyzing course group: %s ...\n", group);
 
 snprintf(path, MAX_PATH + 1,"%s%s", COURSE_PATH, group);
 
 dir_p = opendir (path);
 if (!dir_p)
  {
   perror("Unable to open group directory, so quitting");
   my_crash("Unable to open group directory, so quitting. Bye!");
  }
 
 while (NULL != (dir_entry_p = readdir (dir_p)))
  {
   if (strcmp (dir_entry_p->d_name, ".")
	  && strcmp (dir_entry_p->d_name, ".."))
     {
       analyze_course(group, dir_entry_p->d_name);
     }       
  }

 closedir (dir_p);
 
 if(!course_head)
   {
   my_crash("No courses found.\n. Bye!");
   }
 
 printf("\n\nSummarizing... "); 
 fflush(stdout);
 summarize_group(summary);
 course_head = sort_course_list(course_head, DISK_USAGE);
 
} 




static void
write_one_course_hdf (FILE *fp, COURSE_NODE *course_ptr)
{
 static int course_no = 0;
 char number_string[25];
 MODULE_NODE *mptr;
 
 fprintf(fp, "class.%d.dir = %s\n", course_no, course_ptr->dirname);
 fprintf(fp, "class.%d.course_no = %s\n", course_no, course_ptr->conf.course_no); 
 fprintf(fp, "class.%d.title = %s\n", course_no, course_ptr->conf.title); 
 fprintf(fp, "class.%d.semester = %s\n", course_no, course_ptr->conf.semester);       
 fprintf(fp, "class.%d.instructor = %s\n", course_no, course_ptr->conf.instructor); 
 
 fprintf(fp, "class.%d.teachers = %d\n", course_no, course_ptr->teacher_count);  
 fprintf(fp, "class.%d.students = %d\n", course_no, course_ptr->student_count); 
 
 
 strfmon(number_string, 24, "%!.0n", (double)course_ptr->disk_usage);
 fprintf(fp, "class.%d.disk_usage = %s\n", course_no, number_string);


 strfmon(number_string, 24, "%!.0n", (double)course_ptr->msg_disk_usage);
 fprintf(fp, "class.%d.msg_disk_usage = %s\n", course_no, number_string);

 strfmon(number_string, 24, "%!.0n", (double)course_ptr->file_disk_usage);
 fprintf(fp, "class.%d.file_disk_usage = %s\n", course_no, number_string);

 strfmon(number_string, 24, "%!.0n", (double)course_ptr->website_disk_usage);
 fprintf(fp, "class.%d.website_disk_usage = %s\n", course_no, number_string);  
 
 strfmon(number_string, 24, "%!.0n", (double)course_ptr->misc_disk_usage);
 fprintf(fp, "class.%d.misc_disk_usage = %s\n", course_no, number_string);  
 
 fprintf(fp, "class.%d.msg_count = %d\n", course_no, course_ptr->msg_count);  
 fprintf(fp, "class.%d.chat_msg_count = %d\n", course_no, course_ptr->chat_msg_count);  
 fprintf(fp, "class.%d.file_count = %d\n", course_no, course_ptr->file_count);  
 fprintf(fp, "class.%d.website_count = %d\n", course_no, course_ptr->website_count);  
 
 
 for(mptr=course_ptr->module_head; mptr; mptr=mptr->next)
  {
   strfmon(number_string, 24, "%!.0n", (double)mptr->disk_usage);
   fprintf(fp, "class.%d.module.%s.disk_usage = %s\n", course_no, mptr->module_name, number_string);  


   strfmon(number_string, 24, "%!.0n", (double)mptr->msg_disk_usage);
   fprintf(fp, "class.%d.module.%s.msg_disk_usage = %s\n", course_no, mptr->module_name, number_string);  


   strfmon(number_string, 24, "%!.0n", (double)mptr->file_disk_usage);
   fprintf(fp, "class.%d.module.%s.file_disk_usage = %s\n", course_no, mptr->module_name, number_string);  


   strfmon(number_string, 24, "%!.0n", (double)mptr->website_disk_usage);
   fprintf(fp, "class.%d.module.%s.website_disk_usage = %s\n", course_no, mptr->module_name, number_string);  
 

   strfmon(number_string, 24, "%!.0n", (double)mptr->misc_disk_usage);
   fprintf(fp, "class.%d.module.%s.misc_disk_usage = %s\n", course_no, mptr->module_name, number_string);  
   
   fprintf(fp, "class.%d.module.%s.msg_count = %d\n", course_no, mptr->module_name, mptr->msg_count);  
   fprintf(fp, "class.%d.module.%s.website_count = %d\n", course_no, mptr->module_name, mptr->website_count);     
   fprintf(fp, "class.%d.module.%s.file_count = %d\n", course_no, mptr->module_name, mptr->file_count);  
   
  }   
   
 
 
 
 
 
 
 
 
 
 course_no++;

}




static void
write_summary_hdf(FILE *fp, char *group, GROUP_SUMMARY *summary)
{
 char group_description[MAX_FILENAME + 1];
 char timestring[MAX_TIMESTRING + 1];
 time_t now;
 char number_string[25];
 now = time(NULL);
 
 
 strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime (&now));
 fprintf(fp,"analysis_timestamp = %s\n", timestring); 
    
 
 fprintf(fp,"group_internal_name = %s\n", group);

 get_short_group_description(group, group_description);    /* shared_course_search.c */
 fprintf(fp, "group_description = %s\n", group_description);
 
 
 fprintf(fp, "classroom_count = %d\n", summary->classroom_count);





 fprintf(fp,"\n");
 strfmon(number_string, 24, "%!.0n", (double)summary->disk_usage);
 fprintf(fp, "disk_usage = %s\n",  number_string);

 strfmon(number_string, 24, "%!.0n", summary->average_disk_usage);
 fprintf(fp, "avg_disk_usage = %s\n",  number_string);


 strfmon(number_string, 24, "%!.0n", summary->median_disk_usage);
 fprintf(fp, "median_disk_usage = %s\n",  number_string);






 fprintf(fp,"\n");
 strfmon(number_string, 24, "%!.0n", (double)summary->msg_disk_usage);
 fprintf(fp, "msg_disk_usage = %s (%3.2f%%)\n",  number_string, ((double) summary->msg_disk_usage/ (double)summary->disk_usage) * 100.0);

 strfmon(number_string, 24, "%!.0n", (double)summary->file_disk_usage);
 fprintf(fp, "file_disk_usage = %s (%3.2f%%)\n",  number_string, ((double) summary->file_disk_usage/ (double)summary->disk_usage) * 100.0);

 strfmon(number_string, 24, "%!.0n", (double)summary->website_disk_usage);
 fprintf(fp, "website_disk_usage = %s (%3.2f%%)\n",  number_string, ((double) summary->website_disk_usage/ (double)summary->disk_usage) * 100.0);  
 
 strfmon(number_string, 24, "%!.0n", (double)summary->misc_disk_usage);
 fprintf(fp, "misc_disk_usage = %s (%3.2f%%)\n",  number_string, ((double) summary->misc_disk_usage/ (double)summary->disk_usage) * 100.0);  
 


 fprintf(fp,"\n");
 fprintf(fp,"msg_count = %d\n", summary->msg_count);
 fprintf(fp,"avg_msg_count = %.2f\n", (double) summary->msg_count/ summary->classroom_count);
 fprintf(fp,"median_msg_count = %.2f\n", (double) summary->median_msg_count);
 
 
 
 fprintf(fp,"\n"); 
 fprintf(fp, "website_count = %d\n", summary->website_count);
 fprintf(fp,"avg_website_count = %.2f\n", (double) summary->website_count/ summary->classroom_count);
 fprintf(fp,"median_website_count = %.2f\n", (double) summary->median_website_count);
 

 fprintf(fp,"\n"); 
 fprintf(fp,"file_count = %d\n", summary->file_count); 
 fprintf(fp,"avg_file_count = %.2f\n", (double) summary->file_count/ summary->classroom_count);
 fprintf(fp,"median_file_count = %.2f\n", (double) summary->median_file_count);
 
 
 
 fprintf(fp,"\n");
 fprintf(fp,"chat_msg_count = %d\n", summary->chat_msg_count);
 fprintf(fp,"avg_chat_msg_count = %.2f\n", (double) summary->chat_msg_count / summary->classroom_count);
 fprintf(fp,"median_chat_msg_count = %.2f\n", (double) summary->median_chat_msg_count);

 fprintf(fp,"\n");
 fprintf(fp,"student_count = %d\n",  summary->student_count);
 fprintf(fp, "avg_student_count = %.2f\n", (double) summary->student_count/ summary->classroom_count);
 fprintf(fp,"median_student_count = %.2f\n", (double) summary->median_student_count); 
 
 
 fprintf(fp,"\n"); 
 fprintf(fp,"assignments = %d (%3.1f%%)\n", summary->conf.assignments, (double) (summary->conf.assignments/(double) summary->classroom_count)*100.0);
 fprintf(fp,"handouts_notices = %d (%3.1f%%)\n", summary->conf.syllabus, (double) (summary->conf.syllabus/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"class_discussion = %d (%3.1f%%)\n", summary->conf.discussion, (double) (summary->conf.discussion/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"post_office = %d (%3.1f%%)\n", summary->conf.postoffice, (double) (summary->conf.postoffice/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"internet = %d (%3.1f%%)\n", summary->conf.internet, (double) (summary->conf.internet/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"lounge = %d (%3.1f%%)\n", summary->conf.lounge, (double) (summary->conf.lounge/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"chat = %d (%3.1f%%)\n", summary->conf.chat, (double) (summary->conf.chat/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"anonymous = %d (%3.1f%%)\n", summary->conf.anonymous, (double) (summary->conf.anonymous/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"team = %d (%3.1f%%)\n", summary->conf.team, (double) (summary->conf.team/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"team_teach = %d (%3.1f%%)\n", summary->conf.team_teach, (double) (summary->conf.team_teach/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"grades = %d (%3.1f%%)\n", summary->conf.grades, (double) (summary->conf.grades/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"selftest = %d (%3.1f%%)\n", summary->conf.selftest, (double) (summary->conf.selftest/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"lectures = %d (%3.1f%%)\n", summary->conf.lectures, (double) (summary->conf.lectures/(double) summary->classroom_count)*100.0);
 fprintf(fp,"people = %d (%3.1f%%)\n", summary->conf.people, (double) (summary->conf.people/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"surveys = %d (%3.1f%%)\n", summary->conf.surveys, (double) (summary->conf.surveys/(double) summary->classroom_count)*100.0); 
 fprintf(fp,"podcasts = %d (%3.1f%%)\n", summary->conf.podcasts, (double) (summary->conf.podcasts/(double) summary->classroom_count)*100.0); 
 
 fprintf(fp,"\n\n");
 
}






static void
write_hdf(char *group, GROUP_SUMMARY *summary)
{
 FILE *fp;
 COURSE_NODE *ptr;
 char path[MAX_PATH + 1];
 
 printf("Writing data... ");
 fflush(stdout);

 setlocale (LC_ALL, "en_US");
 
 snprintf(path, MAX_PATH + 1, "%s%s/%s", COURSE_PATH, group, USAGE_REPORT_FNAME);
 
 fp = fopen(path, "w");
 if(!fp)
  { 
    perror(path);
    my_crash("Can't open file for writing.");
  }

 write_summary_hdf(fp, group, summary);
 
 for(ptr=course_head; ptr; ptr=ptr->next)
   write_one_course_hdf(fp, ptr);
   
 fclose(fp);
 puts(" Done!");
  
}

   
   
static void
free_course_list()
{   
 COURSE_NODE *ptr;
 MODULE_NODE *mptr;

 while(course_head)
  {
   ptr = course_head;
   course_head = course_head->next;
   
   while(ptr->module_head)
    {
      mptr = ptr->module_head;
      ptr->module_head = ptr->module_head->next;
      free(mptr);
    }  
   free(ptr);
  }
}

  



int
main()
{
   char key[MAX_KEY + 1];
   char group[MAX_FILENAME + 1];
   SESSION user;
   GROUP_SUMMARY summary;

   cs_cgi_init();   
   read_parameters(key, group);
   cs_cgi_destroy();

   validate_super_key(key, &user);

   puts("Content-type: text/html\n");
   printf("<html>\n<head><title>Analyzing course group</title></head><body>\n");
   puts("<h3 align=\"center\">Working ... Don't Interrupt!<p />Click the 'View Report' link, which will appear AT THE BOTTOM OF THIS PAGE, when the analysis is complete.</h3>");
   puts("<pre>");

   analyze_course_group(group, &summary);
   write_hdf(group, &summary);
   puts("</pre>");
   
   printf("<p align=\"center\"><a href=\"super_view_usage_report?id=%s&amp;subdir=%s\" title=\"View the newly generated usage report.\">View Report</a></p>",
                         key, group);
                         

   puts("</body>\n</html>");  

   free_course_list();

    return 0;

}

