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

#include "shared_person_list.h"
#include "shared_access.h"
#include "shared_cs_util.h"

GROUP_TYPE
get_group_type(const char *group)
{
      if (!strcmp (group, "faculty"))
	return FACULTY;
      else if (!strcmp (group, "student"))
	return  STUDENT;
      else if (!strcmp (group, "audit"))
	return  AUDIT;
      else
	return UNKNOWN_GROUP;
}



char *
get_group_type_str(GROUP_TYPE group)
{
    char *group_str;
    group_str= (char*)malloc(sizeof(char)*20);
    if(group == FACULTY)
      strncpy(group_str, "faculty", 20);
    else if(group == STUDENT)
      strncpy(group_str, "student", 20);
    else if(group == AUDIT)
      strncpy(group_str, "audit", 20);
     return group_str;
}
   



P_NODE *
sort_person_list (P_NODE *pers_head)
{
  P_NODE *q, *tail, *p, *r;
  if (pers_head != NULL)
    {
      tail = pers_head;
      while (tail->next)
	{
	  q = tail->next;
	  if (username_cmp (&(q->data), &(pers_head->data)) < 0) /* shared_authenticate.c */
	    {
	      tail->next = q->next;
	      q->next = pers_head;
	      pers_head = q;
	    }
	  else
	    {
	      r = pers_head;
	      p = r->next;
	      while (username_cmp (&(q->data), &(p->data)) > 0) /* shared_authenticate.c */
		{
		  r = p;
		  p = r->next;
		}
	      if (q == p)
		tail = q;
	      else
		{
		  tail->next = q->next;
		  q->next = p;
		  r->next = q;
		}
	    }
	}
    }
    return  pers_head;
}




void
get_passwd_line_data(char *line, USER_DATA *data)
{
      char *ptr, *ptr2, *lastname;

      ptr = strchr (line, ':');
      if (!ptr)
	cs_critical_error (ERR_PASSWD_PARSE_ERROR, "");
      *ptr = '\0';
      strncpy (data->username, line, MAX_USERNAME + 1);
      ptr++;
      
      ptr2 = strchr (ptr, ':');
      if (!ptr2)
	cs_critical_error (ERR_PASSWD_PARSE_ERROR, "");
      *ptr2 = '\0';
      strncpy (data->password, ptr, MAX_PASSWORD + 1);
      ptr = ptr2 + 1;

      ptr2 = strchr (ptr, ':');
      if (!ptr2)
	cs_critical_error (ERR_PASSWD_PARSE_ERROR, "");
      *ptr2 = '\0';
      strncpy (data->realname, ptr, MAX_NAME + 1);
      ptr = ptr2 + 1;

      lastname = get_lastname(data->realname);
      strncpy (data->lastname, lastname, MAX_NAME + 1);
      free(lastname);

      ptr2 = strchr (ptr, ':');
      if (!ptr2)
	cs_critical_error (ERR_PASSWD_PARSE_ERROR, "");
      *ptr2 = '\0';
      data->group =get_group_type(ptr);
      ptr = ptr2 + 1;

      ptr2 = strchr (ptr, ':');
      if (!ptr2)
          cs_critical_error (ERR_PASSWD_PARSE_ERROR, "");
      *ptr2 = '\0';
      strncpy (data->alias, ptr, MAX_NAME + 1);

      ptr = ptr2 + 1;
      ptr2 = strchr (ptr, ':');
      if(!ptr2)
         cs_critical_error (ERR_PASSWD_PARSE_ERROR, "");
      *ptr2 = '\0';
       data->team = *ptr;

       ptr = ptr2 + 1;
       ptr[strlen(ptr)-1] = '\0';
       strncpy (data->id, ptr, MAX_ID +1);
}





P_NODE *
build_option_person_list (const CONFIG_STRUCT *conf)
{
  char passwd_fname[MAX_PATH + 1];
  FILE *fp;
  int fd;
  char line[200];
  P_NODE *person, *pptr = 0;
  P_NODE *pers_head = 0;

  snprintf (passwd_fname, MAX_PATH + 1, "%s%s", conf->course_path,PASSWORD_FNAME);
  fp = fopen (passwd_fname, "r");
  if (!fp)
    cs_critical_error (ERR_PASSWD_OPEN_FAILED, passwd_fname);
  
  fd = fileno(fp);  
  get_shared_lock(fd, 1);                      /* shared_lock.c */   
  
  while (fgets (line, 200, fp))
    {
      if (*line == '?')		/* password lines starting with a '?' are ignored */
	continue;
      person = (P_NODE *) malloc (sizeof (P_NODE));
      if (!person)
	cs_critical_error (ERR_MALLOC_FAILED, "");
      get_passwd_line_data(line, &(person->data));
           /* now add it to the linked list */
      person->next = 0;
      if (!pers_head)
	pers_head = person;
      else
	pptr->next = person;

      pptr = person;
    }

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

  pers_head = sort_person_list (pers_head);
  return pers_head;

}





/* removes first node in list that represents 'username'
*/
P_NODE *option_list_remove_user(P_NODE *head, const char *username)
{
 P_NODE *ptr, *ptr2;
 int found; 
 
 for(ptr= head,found=0; ptr && !found; ptr=ptr->next)
  {
   if(!strcmp(ptr->data.username,username))
    {
     found = 1;
     if(ptr == head)
      {
        head = head->next;
        free(ptr);
     }
     else
      {
       for(ptr2=head; ptr2 && (ptr2->next != ptr); ptr2=ptr2->next);
       if(ptr2)
        {
          ptr2->next = ptr->next;
          free(ptr);
        }
     }
   }  /* if username was found */
  } /* for */

  return head;
}      





/** removes the first FACULTY node found in the linked list
**/
P_NODE *option_list_remove_first_faculty(P_NODE *head)
{
 P_NODE *ptr, *ptr2;
 int found;
 
 for(ptr= head,found=0; ptr && !found; ptr=ptr->next)
  {
   if(ptr->data.group == FACULTY)
    {
     found = 1;
     if(ptr == head)
      {
        head = head->next;
        free(ptr);
     }
     else
      {
       for(ptr2=head; ptr2 && (ptr2->next != ptr); ptr2=ptr2->next);
       if(ptr2)
        {
          ptr2->next = ptr->next;
          free(ptr);
        }
     }
   }  /* if faculty was found */
  } /* for */
  return head;
}      





/** This fcn is called to adjust the person list when the list
*** is being used to pick a user to delete from the course.
*** That program can be run by FACULTY or ADMIN
*** We need to make sure that 
*** 1) a FACULTY using the program doesn't delete himself from
***    the classroom
*** 2) an ADMIN doesn't delete the last FACULTY member in the course
**/

P_NODE *faculty_adjusted_option_list(P_NODE *head, const SESSION *user)
{
 int fac_count;
 P_NODE *ptr;
 
 if(user->group == FACULTY)  /* this user could also be ADMIN */
    head =option_list_remove_user(head, user->username);  /* remove username from list */
 
 if(user->group == ADMIN)
  {
   /* how many faculty are on the list? */
   for(ptr = head, fac_count = 0; ptr; ptr=ptr->next)
     if(ptr->data.group == FACULTY)
         fac_count++;

    /* if there's only one FACULTY, remove him */
    if(fac_count == 1)
        head = option_list_remove_first_faculty(head);
  } 
 return head;
}         
 






void
free_option_person_list (P_NODE *pers_head)
{
  P_NODE *ptr;

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






/* sets values for each user in the list,
** that might be used in an HTML selection list 
*/

void
set_option_userlist (P_NODE *pers_head)
{
#define MAX_TMP_NAME 30

  P_NODE *ptr;
  char name[MAX_TMP_NAME];
  char value[MAX_PATH];
  int i;

  for (ptr = pers_head, i = 0; ptr; ptr = ptr->next, i++)
  {
    snprintf(name, MAX_TMP_NAME, "user.%d.value", i);
    snprintf (value, MAX_PATH ,"%s:%s",  ptr->data.username, ptr->data.realname);
    cs_set_value(name, value);

    snprintf(name, MAX_TMP_NAME, "user.%d.realname", i);
    snprintf (value, MAX_PATH ,"%s%s",  ptr->data.group == FACULTY ? "*": "",  ptr->data.realname);
    cs_set_value(name, value);
  }

#undef MAX_TMP_NAME 
}






void 
set_post_recipient(RECEIVE_TYPE kind, const char *username, const char *realname)
{ 
#define MAX_STR 100
 char name[MAX_STR + 1];
 char value[MAX_STR + 1];

 snprintf(name, MAX_STR, "%s.%s", kind == TO? "to": kind== CC? "cc": "bcc",  username);
 snprintf(value,MAX_STR, "%s:%s", username, realname);
 cs_set_value(name,value);   /* shared_cs_util.c */
#undef MAX_STR
}



void
set_post_group_reply_recipients(const char *sender, const char *sender_realname, 
                                                 const char *thisuser, PERSON_NODE *recipient_head)
{
 PERSON_NODE *rptr;
 
 /* include the sender of the message on the TO: list */
 set_post_recipient(TO,  sender, sender_realname);
 
 for(rptr = recipient_head; rptr; rptr = rptr->next)
  {
   if(strcmp(thisuser, rptr->data.username))   /* don't include the person running this program */
    {
         set_post_recipient(rptr->data.how_received,  rptr->data.username, rptr->data.realname);
    }   
  }    

}




/* faculty are excluded from this list */
void
print_student_option_namelist (P_NODE *pers_head)
{
  P_NODE *ptr;

    for (ptr = pers_head; ptr; ptr = ptr->next)
        if (ptr->data.group != FACULTY)
         printf ("<option value=\"%s:%s\">%s\n", ptr->data.username, ptr->data.realname,
						                 ptr->data.realname);
}



/* students are excluded from this list */
void
print_faculty_option_userlist (P_NODE *pers_head, const SESSION *user)
{
  P_NODE *ptr;

  for (ptr = pers_head; ptr; ptr = ptr->next)
    if (ptr->data.group == FACULTY && strcmp(user->username, ptr->data.username) != 0)
      printf ("<option value=\"%s:%s\">%s\n", ptr->data.username, ptr->data.realname,
              ptr->data.realname);
}



 /* count how many teachers are there in the classroom */
int
count_faculty(P_NODE *head)
{
   int count;
   P_NODE *ptr;

   count = 0;
   for(ptr = head; ptr; ptr = ptr->next)
   {
     if(ptr->data.group == FACULTY)
        count++;
   }
   return count;
}


 /* How many students are there in the list ?*/
int
count_students(P_NODE *head)
{
   int count;
   P_NODE *ptr;

   count = 0;
   for(ptr = head; ptr; ptr = ptr->next)
   {
     if(ptr->data.group == STUDENT)
        count++;
   }
   return count;
}


/** How many students are there in the course? **/
int current_student_count(CONFIG_STRUCT *conf)
{
 P_NODE *head;
 int number_students;
 
 head = build_option_person_list(conf);
 number_students = count_students(head);
 free_option_person_list(head);
 return number_students;
}
 








void
copy_data_to_user(SESSION *user, USER_DATA *data)
{
    if(!user_capturing(user)) /* shared_access.c */
       strncpy(user->alias, data->alias, MAX_NAME+1);
    strncpy(user->username, data->username, MAX_USERNAME +1);
    strncpy(user->realname, data->realname, MAX_NAME +1);
    strncpy(user->id, data->id, MAX_ID +1);
    
    user->team = data->team;
    user->group = data->group;
}





/* reads password file for course identified by conf, looking for username.
** fills in 'user' with appropriate data
** returns 0 if username is not found in password file, else 1
*/

int
get_user_info (const char *username,  SESSION *user, CONFIG_STRUCT *conf )
{
  char *ptr ;
  char passwd_fname[MAX_PATH + 1];
  FILE *fp;
  int fd;
  char line[200];
  int found = 0;
  int userlen;
  USER_DATA data;

  snprintf (passwd_fname, MAX_PATH + 1, "%s%s", 
  			conf->course_path,PASSWD_FNAME);
  fp = fopen (passwd_fname, "r");
  if (!fp)
    cs_critical_error (ERR_PASSWD_OPEN_FAILED, "");
  fd = fileno(fp);
  get_shared_lock(fd, 1);   /* shared_lock.c */
  
  userlen = strlen (username);
  while (!found)
    {
      if (!fgets (line, 200, fp))
	{
          release_lock(fd);
	  fclose (fp);
	  return 0;
	}
      if (*line == '?')		/* lines starting with ? are ignored */
	continue;
      ptr = strstr (line, username);
      found = ((ptr == line) && (*(ptr + userlen) == ':'));
    }
  release_lock(fd);    
  fclose (fp);
  get_passwd_line_data(line, &data);
  copy_data_to_user(user, &data);
  return 1;
}





void
get_user_data (char *name, USER_DATA * data, CONFIG_STRUCT *conf)
{
  char passwd_fname[MAX_PATH + 1];
  FILE *fp;
  char line[200];
  int found = 0;
  char *ptr;
  int userlen;
  char username[MAX_USERNAME +1];

  /* get the username from the 'name' var passed to us in the form */
  ptr = strchr (name, ':');
  if(ptr)
  {
    *ptr = '\0';
    strncpy (username, name, MAX_USERNAME + 1);
  }
  else
    strncpy (username, name, MAX_USERNAME + 1);

  snprintf (passwd_fname, MAX_PATH + 1, "%s%s", conf->course_path, PASSWD_FNAME);
  fp = fopen (passwd_fname, "r");
  if (!fp)
    cs_critical_error (ERR_PASSWD_OPEN_FAILED, "");

  userlen = strlen (username);
  while (!found)
    {
      if (!fgets (line, 200, fp))
	{
          release_lock(fileno(fp));
	  fclose (fp);
	  cs_critical_error (ERR_NOT_IN_PASSWD,"");
	}
      if (*line == '?')		/* lines starting with ? are ignored */
	continue;
      ptr = strstr (line, username);
      found = ((ptr == line) && (*(ptr + userlen) == ':'));
    }
  release_lock(fileno(fp));    
  fclose (fp);
  if(found)
     get_passwd_line_data(line, data);
}





int 
get_total_users(P_NODE *head)
{
   int count;
   P_NODE *ptr;

   count = 0;
   for(ptr = head; ptr; ptr = ptr->next)
        count++;
   return count;
}




/** returns non-zero if both current_user and target_user are in the classroom
*** represented by conf.
**/

int 
are_classmates(const char *current_user, const char *target_user, CONFIG_STRUCT *conf)
{
  P_NODE *person_head, *ptr;
  int current_found;
  int target_found;
  
  
  person_head = build_option_person_list(conf);   /* shared_person_list.c */
  for(ptr=person_head, current_found = 0, target_found= 0; 
                       ptr && (!current_found || !target_found); 
                                                         ptr=ptr->next)
   {
    if(!strcmp(ptr->data.username, current_user))
        current_found = 1;
    if(!strcmp(ptr->data.username, target_user))
        target_found = 1;
   }
   
  free_option_person_list(person_head);    /* shared_person_list.c */
  return current_found && target_found;
}  
