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

#include "shared_lock.h"

#include "shared_news_util.h"
#include "shared_lock.h"
#include "shared_cs_util.h"


typedef struct disabled_topic_node
 {
  int topic_id;
  struct disabled_topic_node *next;
 } DISABLED_TOPIC_NODE;
 
 

int
new_files_exist (char *mail_dir)
{
  DIR *dir_p;
  struct dirent *dir_entry_p;
  char *ptr;

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


  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 */
	{
	  closedir (dir_p);
	  return 1;
	}

    }
  closedir (dir_p);
  return 0;
}



/* returns 1 if inbox.dat file contains a msg that was not read and
** was not deleted, else returns 0
*/

int
old_unread_mail (const char *maildir)
{
  char inbox_path[MAX_PATH + 1];
  FILE *fp;
  INBOX_RECORD msg;


  snprintf (inbox_path, MAX_PATH + 1, "%s/%s", maildir, INBOX_FNAME);
  fp = fopen (inbox_path, "r");
  if (!fp)			/* no inbox, no friends */
    return 0;

  get_shared_lock(fileno(fp), 1);    /* shared_lock.c */
  while (fread (&msg, sizeof (INBOX_RECORD), 1, fp) == 1)
    if (!msg.time_read && (msg.newfile_info.how_sent != DELETED))
      {
        release_lock(fileno(fp));    /* shared_lock.c */
	fclose (fp);
	return 1;
      }

  release_lock(fileno(fp));   /* shared_lock.c */
  fclose (fp);
  return 0;
}




/* returns 0 if there is no unread mail/ else returns 1 */

int
unread_mail (const char *username, const CONFIG_STRUCT *conf)
{

  char mail_dir[MAX_PATH + 1];
  int has_new_files = 0;

  snprintf (mail_dir, MAX_PATH + 1, "%s%s/%s/%s", conf->course_path, PEOPLE_DIR,
	    							username, MAIL_DIR);
	    							
/* custom.h - versions prior to 2.4 used *.new files to flag
** the delivery of Post Office messages.
** The custom.h entry controls whether or not we should check
** for these files 
*/
	    							

#ifndef SUPPORT_PRE24_POST_OFFICE_MSGS
     has_new_files = new_files_exist (mail_dir);
#else
  if(SUPPORT_PRE24_POST_OFFICE_MSGS)
     has_new_files = new_files_exist (mail_dir);  
#endif


	    							
	    							
  return (has_new_files || old_unread_mail (mail_dir));
}



int is_unread(CENTRAL_INBOX_RECORD *msg, USER_INBOX_NODE *user_head)
{
 USER_INBOX_NODE *ptr;
 
 for(ptr=user_head; ptr; ptr=ptr->next)
   if (ptr->data.msg_id == msg->info.msg_id)
       return (ptr->data.time_read == 0);
       
 return 1;  /* msg is not listed, so it is unread */
}         
     
     
     
     
     
static
DISABLED_TOPIC_NODE * add_to_disabled_topic_list(DISABLED_TOPIC_NODE *head, int topic_id)
{
 DISABLED_TOPIC_NODE *new_node, *ptr;
 
  new_node = (DISABLED_TOPIC_NODE *) malloc(sizeof(DISABLED_TOPIC_NODE));
  if(!new_node)
     cs_critical_error(ERR_MALLOC_FAILED, "");
  new_node -> topic_id = topic_id;
  new_node ->next = 0;
  
  if(!head)
     head = new_node;
  else
   {
    for(ptr = head; ptr->next; ptr=ptr->next);
    ptr->next = new_node;
   }
 return head;
}


   
static int
on_disabled_topic_list(DISABLED_TOPIC_NODE *head, int topic_id)
{
 DISABLED_TOPIC_NODE *ptr;
 
   for(ptr = head; ptr; ptr = ptr->next)
      if (ptr->topic_id == topic_id)
           return 1;
           
   return 0;
}



static
DISABLED_TOPIC_NODE *free_disabled_topic_list(DISABLED_TOPIC_NODE *head)
{
  DISABLED_TOPIC_NODE *ptr;
  
  while(head)
   {
    ptr = head;
    head = head->next;
    free(ptr);
   }
  return head;
}

   
     


     

int unread_news (SESSION *user, int grp, const CONFIG_STRUCT *conf, time_t current_time)
{
  int show_all_teamteach;
  int show_teacher_hidden;
  USER_INBOX_NODE   *user_head = 0;
  FILE *fp;
  int fd;
  char inbox_fname[MAX_PATH + 1];  /* full path to central inbox */
  CENTRAL_INBOX_RECORD data;
  int unread_found = 0;
  int check_for_hidden_topics;
  int should_display;
  DISABLED_TOPIC_NODE *head = 0;
    
  /* When a topic is marked as hidden by the teacher, each message that belongs to that topic is NOT
  ** also marked as hidden.  The tree structure used within the programs that list the indexes of messages
  ** handle the display correctly.  However here we might  have to check each msg to make sure the topic 
  ** that msg belongs to has not been hidden by the teacher
  ** Do we need to check each message to make sure its topic has not been hidden by the teaher?
  */
  
  /* Another thing... the next used to do the check only for students, but on rare occasions it turns out
  ** that teachers became the victim of red stars on the main menu that won't go away.
  **/
  check_for_hidden_topics =  group_uses_topic(grp);   /* shared_news_util.c */     
  
  show_all_teamteach = (user->group == FACULTY) && (grp == TEAM_TEACH);
  show_teacher_hidden = (user->group == FACULTY)? 1 : 0;
  
  
  set_discussion_names (conf->course_path, user->username, grp);	/* shared_news_util.c */
  user_head = build_user_inbox_list();                                 /* shared_news_util.c */
  
  snprintf(inbox_fname,MAX_PATH + 1,"%s%s",central_discussion_path,INBOX_FNAME);
  fp = fopen(inbox_fname,"r");
  if(!fp)                              /* inbox.dat doesn't exist */
  {
    free_user_inbox_list();	  
    return 0;  /* so there are no unread msgs */
  } 
 
  fd = fileno(fp);
  get_shared_lock(fd, 1);                      /* shared_lock.c */

  /*Just a guess.  An unread msg often will be often the last record in the inbox file
  **Let's test that first and see if we get lucky.  We can only try this if we don't need to
  ** check for hidden topics.
  */
  
  if(!check_for_hidden_topics)
  {
   if(!fseek(fp,-sizeof(CENTRAL_INBOX_RECORD), SEEK_END))  /* if we can move back one record from the end */
    {
     if(fread(&data,sizeof(CENTRAL_INBOX_RECORD),1,fp) == 1) /* if the read is successful */
       unread_found = (msg_should_display(&data, user, grp, current_time, show_all_teamteach, show_teacher_hidden) &&
                           is_unread(&data, user_head) );
    } 
  }
  
  
 if(!unread_found)   /* our trick didn't work, so we need to deal with the whole file */
 {           
  if(fseek(fp, 0, SEEK_SET))   /* move to the start of the file */
   {
    release_lock(fd);
    fclose(fp);
    cs_critical_error(ERR_FSEEK_FAILED, inbox_fname);
   } 
  
   do
   {
    if(fread(&data,sizeof(CENTRAL_INBOX_RECORD),1,fp) != 1)
       break;    
       
    /* msg_should_display() is in shared_news_util.c */   
    should_display = msg_should_display(&data, user, grp, current_time, show_all_teamteach, show_teacher_hidden);
    
    /** If we need to check each message to see if it belongs to a hidden topic,
    *** and if this message is a topic, and if it should NOT be shown to this user,
    *** we add it to a linked list of 'bad' topics
    */
    if(check_for_hidden_topics && (data.info.reply_to == -1) && !should_display)
        head = add_to_disabled_topic_list(head, data.info.msg_id);

    unread_found = (should_display && is_unread(&data, user_head) );
    
    /** if the message seems to be unread, and if we need to check the msg to see
    *** if it belongs to a hidden topic, and if this message is not a topic,
    ***  the message is only really unread if
    *** the topic it belongs to is not on the bad topic list
    **/
    if(check_for_hidden_topics && unread_found && (data.info.reply_to != -1))
        unread_found = !on_disabled_topic_list(head, data.info.topic_id);
    
   } while (!unread_found);
   
  }  /* our trick didn't work */    
    
  release_lock(fd);
  fclose(fp);
  free_user_inbox_list();
  head = free_disabled_topic_list(head);
  
  return unread_found;
}  
  




int
unread_grades (const char *username, const 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;
}



int
non_empty_chatroom ()
{
  struct stat buf;

  return (!stat (CHAT_ACTIVE_FILE, &buf));
}




