#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>		/* for tolower() */


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

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

#define NOTEAM '!'
#define GATHER_ALL "_all_"
#define GATHER_FLAGGED "_flagged_"
#define GATHER_UNREAD "_unread_"
#define GATHER_LAST   "_last_"
#define GATHER_TOPIC "_topic_"


typedef enum gather_type
{ UNREAD, FLAGGED, ALL_MSG, USERNAME, TOPIC, LAST }
GATHER_TYPE;

typedef struct gather_struct
{
  int topic_id;
  char username[MAX_USERNAME + 1];
  char realname[MAX_NAME + 1];
  GATHER_TYPE target;
  time_t time_read;     /* if GATHER_TYPE is 'UNREAD', then this contains the time the messages were read via this gather program */
}
GATHER_STRUCT;


int global_indent = -1;
int global_msg_count = 0;
int global_topic_count = 0;
int global_total_msgs = 0;

#define NO_INDENT 0
#define INDENT 1





/* these are declared globally to reduce overhead
** for a recursive function in this program
**/

GATHER_STRUCT gather_criteria; 
time_t current_time;


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

char topic_subject[MAX_SUBJECT + 1];
int topic_teacher_hidden;              /* did the teacher hide the topic */
int topic_read_only;                   /* is the topic marked read only */
int global_can_write;                  /* can this user add msgs to this course? */


ATTACHMENT_NODE *attachment_head = (ATTACHMENT_NODE *) 0;



static void
read_parameters (char *course, char *key, char *gather,
		 int *grp, int *del_link, char *topic_prog, char *team,
		           int *printer_friendly, int *fixed_font)
{

  char tmp[MAX_FILENAME + 1];	/* scratch variable */


  cs_get_required_parameter ("crs", course, MAX_FILENAME);
  cs_get_required_parameter ("id", key, MAX_KEY);

  cs_get_required_parameter ("grp", tmp, MAX_FILENAME);
  *grp = atoi (tmp);

  cs_get_optional_parameter ("gather", gather, MAX_PATH);
  if(!strlen(gather))
      strncpy (gather, GATHER_LAST, MAX_PATH + 1);

  snprintf (topic_prog, MAX_PATH + 1, "%s?crs=%s&amp;id=%s&amp;grp=%d",
	    "news_topic", course, key, *grp);

  cs_get_optional_parameter ("team", tmp, 5);
  if(!strlen(tmp) )
    *team = NOTEAM;
  else
    *team = *tmp;
      
  
  
  /* just check if the "del_link" parameter is there */
  cs_get_optional_parameter ("del_link", tmp, 5);
  *del_link = strlen(tmp);
  
    /* provide printer-friendly version? */
  cs_get_optional_parameter ("print", tmp, 5);              /* shared_cs_util.c */
  *printer_friendly = *tmp? (atoi(tmp)? 1 : 0) :  0;


  /* provide preformatted text (instead of proportional) ? */
  cs_get_optional_parameter ("fixed", tmp, 5);              /* shared_cs_util.c */
  *fixed_font = *tmp? (atoi(tmp)? 1 : 0) :  0;


}





/* is this the teacher working with the team/teacher module? */
static void
check_is_fac_team_teach (int grp, int *is_fac_teamteach, char *team, char *topic_prog, SESSION *user)
{

  char teamstring[20];

  if ((user->group == FACULTY) && (grp == TEAM_TEACH))
    {
     /* then the 'team' param must have been passed to this program */
     if(*team == NOTEAM)
        cs_critical_error(ERR_PARAM_MISSING, "team");

      *is_fac_teamteach = 1;
      
      /* append the team parameter to the 
      ** topic_prog string, which will be eventually
      ** used to set a back button url
      */
      snprintf (teamstring, 20, "&team=%c", *team);
      strcat (topic_prog, teamstring);
    }
  else
    {
      *is_fac_teamteach = 0;
      *team = NOTEAM;  /* NOTEAM is #defined on top of this file. */
    }
}





static void
parse_gather_string (char *gather, GATHER_STRUCT * gather_target)
{
  char *ptr;

  gather_target->topic_id = -1;
  *gather_target->username = '\0';
  *gather_target->realname = '\0';
  gather_target->time_read = 0;
  
  if (!strcmp (gather, GATHER_ALL))
    gather_target->target = ALL_MSG;
  else if (!strcmp (gather, GATHER_FLAGGED))
    gather_target->target = FLAGGED;  
  else if (!strcmp (gather, GATHER_UNREAD))
    gather_target->target = UNREAD;
  else if (!strcmp (gather, GATHER_LAST))
    gather_target->target = LAST;
  else
    {
      ptr = strchr (gather, ':');
      if (ptr)
	{
	  *ptr = '\0';
	  strncpy (gather_target->username, gather, MAX_USERNAME);
	  strncpy (gather_target->realname, ptr + 1, MAX_NAME);
	  *ptr = ':';
	  gather_target->target = USERNAME;
	}
      else
	{
	  ptr = strstr (gather, GATHER_TOPIC);
	  if (ptr == gather)
	    {
	      ptr += strlen (GATHER_TOPIC);
	      gather_target->topic_id = atoi (ptr);
	      gather_target->target = TOPIC;
	    }
	  else
	    cs_critical_error (ERR_GATHER_PARSE_ERROR, gather);

	}
    }
}



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



static void
free_attachment_list()
{
 ATTACHMENT_NODE *ptr;

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

}   



static void
build_attachment_list (MSG_TREE_NODE * msg)
{
  FILE *fp;
  int fd;
  char infopath[MAX_PATH + 1];
  int i;
  ATTACHMENT_NODE *aptr = 0;
  NEWS_INFO info;
  int n_attachments;
  
  free_attachment_list();
  
  if(!msg->data.info.attachments)
     return;
  
  n_attachments = abs(msg->data.info.attachments);  /* since a -1 is used to flag an attached website */
  
  snprintf (infopath, MAX_PATH + 1, "%s%s/%s/%s/%s", 
                                    conf.course_path,
		                    PEOPLE_DIR,
		                    msg->data.info.sender,
	    			    discussion_fname, 
	    			    msg->data.info_fname);

  fp = fopen (infopath, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, infopath);
  fd = fileno(fp);  
  get_shared_lock(fd, 1);                      /* shared_lock.c */   
 
  if (fread (&info, sizeof (NEWS_INFO), 1, fp) != 1)
    cs_critical_error (ERR_FREAD_FAILED, infopath);

  /* process attachments, build linked list */

  for (i = 1; i <= n_attachments; i++)
    {
      if (!attachment_head)
	{
	  attachment_head = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!attachment_head)
	          memory_error ();
	  aptr = attachment_head;
	}
      else
	{
	  aptr->next = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!aptr->next)
	    memory_error ();
	  aptr = aptr->next;
	}

      if (fread (&aptr->data, sizeof (ATTACHMENT), 1, fp) != 1)
	cs_critical_error (ERR_FREAD_FAILED, infopath);
      aptr->next = 0;
    }				/* for .. */

release_lock(fd);                             /* shared_lock.c */
fclose(fp);

}






static void
read_last_criteria (GATHER_STRUCT * gather_criteria)
{
  char last_path[MAX_PATH + 1];
  FILE *fp;

  snprintf (last_path, MAX_PATH + 1, "%s%s", discussion_path, GATHER_LAST_FNAME);
  fp = fopen (last_path, "r");
  if (!fp)
    cs_critical_error(ERR_FOPEN_READ_FAILED, last_path);
  if (fread (gather_criteria, sizeof (GATHER_STRUCT), 1, fp) != 1)
    cs_critical_error(ERR_FREAD_FAILED, last_path);

  fclose (fp);
}





static void
write_last_criteria (GATHER_STRUCT * gather_criteria)
{
  char last_path[MAX_PATH + 1];
  FILE *fp;

  if( (gather_criteria->target == UNREAD) && !gather_criteria->time_read )
      gather_criteria->time_read = current_time;
  snprintf (last_path, MAX_PATH + 1, "%s%s", discussion_path, GATHER_LAST_FNAME);
  fp = fopen (last_path, "w");
  if (!fp)
    cs_critical_error(ERR_FOPEN_WRITE_FAILED, last_path);
  if (fwrite (gather_criteria, sizeof (GATHER_STRUCT), 1, fp) != 1)
    cs_critical_error(ERR_FWRITE_FAILED, last_path);

  fclose (fp);
}




static void
set_gather_target (GATHER_STRUCT * gather_criteria)
{

  switch(gather_criteria -> target)
  {
  case UNREAD:   cs_set_value("gather_target", "UNREAD"); 
                 break;
  case FLAGGED:   cs_set_value("gather_target", "FLAGGED"); 
                 break;
  case ALL_MSG:      cs_set_value("gather_target", "ALL"); 
                 break;
  case USERNAME: cs_set_value("gather_target", "USERNAME"); 
                 cs_set_value("gather_target_realname", gather_criteria->realname);
                 cs_set_value("gather_target_username", gather_criteria->username);                 
                 break;

  case TOPIC:    cs_set_value("gather_target", "TOPIC");                 
                 /** additional values will be set later via set_topic_target_details() */
                 break;
                  
  case LAST:       /* LAST should have been replaced via call to read_last_criteria() */
  default:       cs_set_value("gather_target", "");   break;    /* should never happen? */
  }


}



static void
set_topic_target_details(MSG_TREE_NODE *msg_ptr)
{
 char *escaped_string = 0;

 cs_html_escape_string(msg_ptr->data.info.subject, &escaped_string);   /* shared_cs_util.c */
 if(escaped_string)
  {
   cs_set_value("target_escaped_topic_subject", escaped_string);
   free(escaped_string);
  }
  
    cs_set_value( "target_topic_subject", msg_ptr->data.info.subject);
    cs_set_value( "target_topic_sender_realname", msg_ptr->data.info.sender_realname);    


}  




static void
get_supporting_strings (char *fromstring, char *teamstring, long offset)
{
 char escaped_teamstring[10];
  
  /* AMP_SUB, etc. are #defined in shared_translate_url.h */
   
   if (is_fac_teamteach)
    {
      snprintf (teamstring, 20, "&amp;team=%c",  user.team);
      snprintf (escaped_teamstring, 10, "%cteam%c%c", AMP_SUB, EQUAL_SUB, user.team);      
    }
  else
    {
     *teamstring = '\0';
     *escaped_teamstring = '\0';
    }
    
     
  snprintf (fromstring, MAX_PATH + 1,
	    "from=%s%ccrs%c%s%cid%c%s%cgrp%c%d%cgather%c_last_%s%c%ld",
	    "news_gather", QMARK_SUB, EQUAL_SUB, course,
	    AMP_SUB, EQUAL_SUB, key, AMP_SUB, EQUAL_SUB, grp, AMP_SUB,
	    EQUAL_SUB, escaped_teamstring, POUND_SUB, offset);





}




static void
set_indent(const char *name)
{
 char msg_name[MAX_PATH + 1];

 
  snprintf(msg_name, MAX_PATH, "%sindent",name);
  
  if( (gather_criteria.target == TOPIC) ||
      (gather_criteria.target == ALL_MSG) )
      
           cs_set_int_value(msg_name, global_indent);
           
  else
  
          cs_set_int_value(msg_name, 0);
}







static void
set_flag_reply_info_urls ( MSG_TREE_NODE *msg_ptr, char *name)
{

  char from_string[MAX_PATH + 1];	/* how to get back to THIS page... passed
						** to the NEWS_NEW_MEMO program
						 */
  char teamstring[20];	/* eg. "team=A" */

  char msg_name[MAX_PATH];
  char url[MAX_PATH*2];


  get_supporting_strings (from_string, teamstring, msg_ptr->offset);
  
  
  
  /** set flag value and url */
   snprintf(msg_name, MAX_PATH, "%sflagged", name);
   cs_set_int_value(msg_name, msg_ptr->user_data.flagged? 1:0);

   snprintf(url, 
       MAX_PATH * 2, "%s?crs=%s&amp;id=%s&amp;grp=%d&amp;msg_id=%d&amp;%s",
        "news_flag", course, key, grp, msg_ptr->data.info.msg_id, from_string);
   snprintf(msg_name, MAX_PATH, "%sflag_url", name);        
   cs_set_value(msg_name, url);  


  
  
  
  if (group_uses_topic (grp))	/* shared_news_util.c */
   {

    /*** REPLY button */
   if(global_can_write)
    {
       if( (user.group == FACULTY) || !topic_read_only)
       {    
        snprintf(msg_name, MAX_PATH, "%sreply_url", name);
        snprintf (url, MAX_PATH *2, "%s?crs=%s&amp;id=%s&amp;grp=%d&amp;topic=%d&amp;loc=%ld%s&amp;reply_to=%d",
                                "news_new_memo", course, key, grp, msg_ptr->data.info.topic_id, msg_ptr->offset, 
                                                                     teamstring,msg_ptr->data.info.msg_id);
    
        if((grp == ASSIGNMENTS) && (user.group == STUDENT) )
	{
            strcat(url, "&amp;to=");
            strcat(url, user.username);
	}
        strcat(url, "&amp;");
        strcat(url, from_string);
        cs_set_value(msg_name, url); 
   
     }  
    }
    
  }  /* if group uses topic */
  
/**** INFO ***/

  if(grp == SURVEYS)
     snprintf (url, MAX_PATH *2, "%s?crs=%s&amp;id=%s&amp;loc=%ld",
	  "survey_inf", course, key, msg_ptr->offset);
  else
     snprintf (url, MAX_PATH *2, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;grp=%d",
	  "news_info", course, key, msg_ptr->offset, grp);
  snprintf(msg_name, MAX_PATH, "%sinfo_url", name);
  cs_set_value(msg_name, url);
  
}








int gather_msg_should_display(MSG_TREE_NODE *msg_ptr)
{

switch(gather_criteria.target)
 {
  case ALL_MSG: return 1;
            break;

  case TOPIC: return 1;   /* since we've already ensured we are looking at the correct topic */
              break;           
             
  case FLAGGED: return msg_ptr->user_data.flagged ? 1 : 0;
                break;
           
  case UNREAD:  return (gather_criteria.time_read == msg_ptr->user_data.time_read);
                break;

  case USERNAME : return (!strcmp(gather_criteria.username, msg_ptr->data.info.sender) );
                  break;
     
  default: return 0;          /* this should never happen */
           break;
 }
}                                         
                  





static void
set_message_data(MSG_TREE_NODE *msg_ptr)
{
#define MAX_HDF_NAME 40 
char name[MAX_HDF_NAME];
char msg_path[MAX_PATH + 1];
char inet_url[MAX_URL + 1];

 if(gather_msg_should_display(msg_ptr))
  {
   /* Do not mark message as read if:
   ** - the attachments are protected or
   ** - this is the surveys module and the reader is not a teacher.
   */
   if(!msg_ptr->user_data.time_read)
    {
      if(grp == SURVEYS)
        {
          if(user.group != FACULTY)
              mark_msg_as_read_at_time(&(msg_ptr->data), &conf, &user,current_time);  /* shared_news_util.c */
        }
      else
        {   
           if(!IS_PROTECTED(msg_ptr->data.status)) 
                  mark_msg_as_read_at_time(&(msg_ptr->data), &conf, &user,current_time);     /* shared_news_util.c */
        }
    }  /* if msg not read */
    
   
   if(msg_ptr->data.info.reply_to == -1)   /* i.e. if this is a topic */
   {
      global_topic_count++;
      global_msg_count = 0;
   }
   
   
    snprintf(name, MAX_HDF_NAME + 1, "topic.%d.msg.%d.", global_topic_count-1, global_msg_count);
    global_msg_count++;
   
    set_indent (name);

    set_flag_reply_info_urls (msg_ptr, name);
    
    /* shared_news_util.c */
    set_news_msg_header_data (msg_ptr, name, topic_teacher_hidden, topic_read_only, topic_subject); 
    
    
    
    build_attachment_list (msg_ptr);
    
    /* shared_news_util.c */
    set_news_attachment_data (msg_ptr,   
                              topic_subject,
                              course, 
                              key, 
                              grp,
                              msg_ptr->data.info.attachments == -1 ? 1: 0,
       		              "news_gather",
		              user.team, 
		              is_fac_teamteach, 
		              "",                   /* no assign_read_student value */
		              &conf, 
		      
		              /* next is the is_protected param */
		              (msg_ptr->data.info.attachments &&
		              IS_PROTECTED(msg_ptr->data.status) && !msg_ptr->user_data.time_read)? 1: 0,
		       
		              user.username, 
		              attachment_head,
  		              name);


    /* handle body of message */

    snprintf (msg_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
	    conf.course_path, PEOPLE_DIR, msg_ptr->data.info.sender, discussion_fname, msg_ptr->data.info.msg_fname);
	    
    if(grp == INTERNET_RESOURCES)
       snprintf(inet_url, MAX_URL + 1, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;gather=1&amp;url=", 
                                               "link_visit", course, key,  msg_ptr->offset);  
    else
        *inet_url = '\0';        
                                       
     set_msg_body_data (msg_path, name, inet_url); /* shared_msg_line.c */
     global_total_msgs++;
     

  }
  

#undef MAX_HDF_NAME  

}  




static void
gather_subtree (MSG_TREE_NODE *root, int indent)
{

 if(!root)
    return;
 if(indent)
     global_indent++; 
     
  set_message_data(root);
   
 if(root->reply)
    gather_subtree(root->reply, INDENT);
 if(root->next && (root->next->data.info.reply_to != -1))  /* don't do 'next' branches if they lead to a topic */
    gather_subtree(root->next, NO_INDENT);
 
if(indent)
     global_indent--; 

                 
}



static void
handle_one_topic(MSG_TREE_NODE *msg_ptr)
{
 if(msg_ptr)  /* this should always be true */
   {
    /* topic_subject is global */
    strncpy(topic_subject,msg_ptr->data.info.subject, MAX_SUBJECT + 1);

   /* topic_teacher_hidden and current_time are global 
   *  is_teacher_hidden() is in shared_news_util.c 
   */
   topic_teacher_hidden = is_teacher_hidden (msg_ptr->data.status, msg_ptr->data.release_time,  
                                                        msg_ptr->data.unrelease_time, current_time ); 
                                                        
   /* topic_read_only is global at top of this file; 
   ** BASIC_STATUS is macro #defined in global.h 
   */                                             
   topic_read_only = BASIC_STATUS(msg_ptr->data.status) == READ_ONLY? 1 : 0;
                                                        
   gather_subtree(msg_ptr, INDENT);
   }   
}




static void
set_gather_news_values (char *course, char *key, char *gather,
		         int grp, char team, int is_fac_teamteach, char *topic_prog,
		           CONFIG_STRUCT *conf, SESSION *user)
{
  MSG_TREE_NODE *msg_ptr;
  
  /* note that gather_criteria is declared globally */  
  parse_gather_string (gather, &gather_criteria);
  if (gather_criteria.target == LAST)
      read_last_criteria(&gather_criteria);
  set_gather_target(&gather_criteria);
    
    
    
  /* current_time is declared globally */
  build_msg_tree(user, grp, 0, 1, &current_time);   /* shared_news_util.c */
  
  if(gather_criteria.target == TOPIC)  /* then search just that topic */
    {
       /* find the topic */
       for(msg_ptr=tree_head; msg_ptr && msg_ptr->data.info.topic_id != gather_criteria.topic_id; msg_ptr=msg_ptr->next)
        {
          /* do nothing */
        }
       set_topic_target_details(msg_ptr);
       
       handle_one_topic(msg_ptr);
    }
   else 
    {
       for(msg_ptr=tree_head; msg_ptr; msg_ptr=msg_ptr->next)
           handle_one_topic(msg_ptr);
    }


  cs_set_int_value("msg_count", global_total_msgs);
  
  free_msg_tree (tree_head);                     /* shared_news_util.c */
  write_last_criteria(&gather_criteria);

}




int
main (void)
{
  char gather[MAX_PATH + 1];	/* from gather=? command line */

  char team;			/* from team=? command line: used only 
				   for instructors 
				   in the team/teach module  */

  int del_link;			/* from del_link=? command line  do we need to rm a symbolic link
				** to an attached web page?
				 */


  int printer_friendly;         /* from print=? command line  provide "printer friendly" version? */				 
  int fixed_font;               /* from fixed=? command line  print body as fixed font? */				 


  char topic_prog[MAX_PATH + 1];	/* arguments to NEWS_TOPIC program */
  
  char teamstring[2];            
 
 
   /* note that course, key, grp, is_fac_teamteach, user, and conf are declared globally in this program */
 
 
  cs_cgi_init(); 
  read_parameters (course, key, gather, &grp, &del_link, 
                      topic_prog, &team, &printer_friendly, &fixed_font);
  read_configuration_file (course, &conf);	/* shared_util.c */
  validate_key (key, &user, &conf);		/* shared_util.c */

  if(user.group == FACULTY)
       cs_set_value("is_faculty","1");          /* shared_cs_util.h */
       
  if(conf.access == ACCESS_CAPTURE)
      cs_set_value("capture_mode", "1");        /* shared_cs_util.h */

   cs_set_int_value("printer_friendly", printer_friendly);
   cs_set_int_value("fixed_font", fixed_font);


  global_can_write = has_write_permission(&user, &conf);   /* shared_access.c */
  if(global_can_write)
      cs_set_value("can_write", "1");    	/* shared_cs_util.h */
   
   
  cs_set_course_info(&conf);      /* shared_cs_util.c */    
  cs_set_current_time();          /* shared_cs_util.c */
  cs_set_value("crs", course);    /* shared_cs_util.h */
  cs_set_int_value("grp", grp);   /* shared_cs_util.h */

  if(group_uses_topic(grp))       /* shared_news_util.c */
      cs_set_value("group_uses_topic", "1");

      
  cs_set_value("key", key);	  /* shared_cs_util.h */
  cs_set_value("username",user.username);  	  /* shared_cs_util.h */



  set_discussion_names (conf.course_path, user.username, grp);	/* shared_news_util.c */

  check_is_fac_team_teach (grp, &is_fac_teamteach, &team, topic_prog, &user);
  cs_set_value("back_url", topic_prog);
  if(is_fac_teamteach)
   {
     user.team = team;   /* set teacher to this team for the run of this program */
     cs_set_value("is_fac_teamteach", "1");
   }
   
  if(team != NOTEAM)
   {
      *teamstring = team;
      *(teamstring + 1) = '\0';
      cs_set_value("team", teamstring);		/* shared_cs_util.h */
   }



  set_gather_news_values (course, key, gather, grp, team, is_fac_teamteach, topic_prog, &conf, &user);

  
  if (del_link)
    remove_stale_symlinks (user.username, course, conf.access);  /* shared_news_util.c */


  /* NOTE: this program uses the same lang file as news_read.c **/
  cs_load_lang("news_read");
  cs_cgi_display("news_gather", 0);


  cs_cgi_destroy();
  return 0;
}


