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

#include "global.h"

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_person_list.h"
#include "manhat-lib/shared_strtrm.h"
#include "manhat-lib/shared_file_util.h"

#define SUBDIR_NAME "orig"


typedef enum {STANDALONE, CENTRAL, TEMPLATE} COURSE_TYPE;


#define MAX_COURSE_DIRNAME 100

typedef struct xml_course_info
{
   char course_code[MAX_COURSE_NO +1];
   char title[MAX_COURSE_TITLE +1];
   char semester[MAX_SEMESTER +1];
   char subdir[MAX_COURSE_SUBDIR + 1];
   char dir_id[MAX_COURSE_DIRNAME +1];
   COURSE_TYPE course_type;

   P_NODE *user_head;
   P_NODE *user_current;
   struct xml_course_info *next;
}XML_COURSE_INFO;


typedef struct xml_course_info_list
{
	    XML_COURSE_INFO *head;
	    XML_COURSE_INFO *current;
}XML_COURSE_INFO_LIST;


typedef struct user_course_list
{
     char course_path[MAX_PATH +1];
     struct user_course_list *next;
}USER_COURSE_LIST;

typedef struct username_courses
{
    char username[MAX_USERNAME +1];
    USER_COURSE_LIST *course_head;
    USER_COURSE_LIST *course_current;
    struct username_courses *next;
}USERNAME_COURSES;


int normal_count = 0;
int standalone_count = 0;
int template_count = 0;
int user_count = 0;



USERNAME_COURSES *
find_username(USERNAME_COURSES *head, const char *username)
{
   USERNAME_COURSES *ptr;
   for(ptr = head; ptr; ptr = ptr->next)
   {
       if(strcmp(ptr->username, username) ==0)
	       return ptr;
   }
   return 0;
}
   
   
   
USERNAME_COURSES *
find_last(USERNAME_COURSES *head)
{
   USERNAME_COURSES *ptr;
   USERNAME_COURSES *last =0;
   for(ptr = head; ptr; ptr = ptr->next)
   {
      last = ptr;
   }
   return last;
}




void
free_user_course_list(USER_COURSE_LIST *head)
{
    USER_COURSE_LIST *ptr;
    while(head)
    {
	 ptr = head->next;
	 free(head);
	 head = ptr;
    }
}



void
free_username_courses(USERNAME_COURSES *head)
{
    USERNAME_COURSES *ptr;
    while(head)
    {
	ptr = head->next;
	free_user_course_list(head->course_head);
	free(head);
	head = ptr;
    }
}
    
    
    
/** Updates the USERNAME_COURSES list, which holds for each user a list
*** of courses they belong to.  Each call for this function acts only on
*** one user - username
***/

USERNAME_COURSES *
insert_username_data(USERNAME_COURSES *head, const char *username, const char *course_path)
{
  USERNAME_COURSES *ptr;
  USERNAME_COURSES *last;

  /* does username already have an entry on the list? */
  ptr = find_username(head, username);
  
  /* yes - add this course to the end of the list ***/
  if(ptr)
  {
	 ptr->course_current->next = (USER_COURSE_LIST*)malloc(sizeof(USER_COURSE_LIST));
         if(!ptr->course_current->next)
           {
            perror("malloc failed - Not enough memory?");
            exit(1);
           } 
	 ptr->course_current = ptr->course_current->next;
         strncpy(ptr->course_current->course_path, course_path,	MAX_PATH +1);
	 ptr->course_current->next = 0;
  }
 
  else  /* no add this user, then add the course to the end of the list */
  {
      user_count++;
      ptr = (USERNAME_COURSES*)malloc(sizeof(USERNAME_COURSES));
      if(!ptr)
       {
         perror("malloc failed - Not enough memory?");
         exit(1);
       }
         
      
      strncpy(ptr->username, username, MAX_USERNAME +1);
      ptr->course_head = (USER_COURSE_LIST*)malloc(sizeof(USER_COURSE_LIST));
      if(!ptr->course_head)
        {
          perror("malloc() failed - Not enough memory?");
          exit(1);
        }
          
      ptr->course_current = ptr->course_head;
      strncpy(ptr->course_current->course_path, course_path,	MAX_PATH +1);
      ptr->course_current->next = 0;
      ptr->next =0;
      if(!head)
	   head = ptr;
      else
      {
        last = find_last(head);
	last->next = ptr;
      }
  }
  return head;
}

      
      
      
/** Shows simplified instructions, and forces user to type
*** a phrase to continue
***/      
void
show_instructions()
{
    char line[200 +1];

      printf("\n\n\n\n\n");
      puts("This upgrade program will move all of your existing course directories");
      printf("into a new SUBDIRECTORY called '%s' (for 'original')  The new release\n", SUBDIR_NAME);
      puts("of Manhattan calls this a 'course group'.  See the Manhattan 2.2 ");
      puts("Administrator's Reference for details.");
      puts("\nBefore you continue, you must get everyone off of Manhattan and ");
      puts("BACK UP your current installation.\n");
      
      printf("Type I BACKED UP to do the upgrade: ");
      fgets(line, 200,stdin);
      *(line + 200) = '\0';
      strtrm(line);
      if(strcmp(line,"I BACKED UP"))
        {
         puts("You need to type I BACKED UP (in uppercase)");
         puts("to do the upgrade.  You didn't so I quit!\n\n");
         exit(1);
        }
        
}




/** builds a list of the courses found in course_base directory 
*** course_base can be either ../courses or ../templates
**/

XML_COURSE_INFO_LIST *
build_course_list(const char *course_base, const char *subdir, XML_COURSE_INFO_LIST *list)
{
   XML_COURSE_INFO *ptr;
   DIR *dir_ptr;
   struct dirent *dir_entry;
   CONFIG_STRUCT conf;
   
   int doing_templates;
   
   doing_templates = (int)strstr(course_base,"templates");
   
   
   if(!doing_templates)
      printf("\nBuilding a list of normal and standalone courses in the 'courses' directory.\n");
    else
      printf("\nBuilding a list of course templates in the 'templates' directory.\n");
         
      
   dir_ptr = opendir(course_base); 
   if(!dir_ptr)
   {
      perror(course_base);
      exit(1);
   }

   while(NULL != (dir_entry = readdir(dir_ptr)))
     {
      if(*(dir_entry->d_name) != '.')
      {
         printf(".");
         fflush(stdout);
         
	 ptr = (XML_COURSE_INFO*)malloc(sizeof(XML_COURSE_INFO));
	 if(!ptr)
	   {
	    perror("malloc() failed - Not enough memory?");
	    exit(1);
	   }
	 
	 /* store the name of the subdir (which is really SUBDIR_NAME 
	 ** This will be changed later for course templates
	 */
	 strncpy(ptr->subdir, subdir, MAX_COURSE_SUBDIR +1);
	 *(ptr->subdir + MAX_COURSE_SUBDIR) = '\0';
	 
	 /*** store the internal name of the course
	 ***/
	 strncpy(ptr->dir_id, dir_entry->d_name, MAX_COURSE_DIRNAME + 1);
	 *(ptr->dir_id + MAX_COURSE_DIRNAME) = '\0';

         /** Try to read the config.dat file
         *** If we can't there's a problem, so stop
         **/
	 if(!read_configuration_file_ok(dir_entry->d_name, &conf))
	  {
	   printf("\n\nThe directory named %s/%s is not a valid course directory.\n",
	                                    course_base, dir_entry->d_name);
	   printf("\n\nAre you SURE you are upgrading from version 2.0 or 2.0.1???\n\n");                                 
	   printf("If so, move this directory out of the way and try again.\n\n");
	   exit(1);
	  } 

	 ptr->course_type = (conf.standalone)? STANDALONE : (conf.template)? TEMPLATE:CENTRAL;
	 switch(ptr->course_type)
	  {
	   case STANDALONE: standalone_count++; break;
	   case TEMPLATE : template_count++; break;
	   default: normal_count++; break;
	  }    
	  
         strncpy(ptr->course_code, conf.course_no, MAX_COURSE_NO +1);
	 strncpy(ptr->title, conf.title, MAX_COURSE_TITLE +1);
	 strncpy(ptr->semester, conf.semester, MAX_SEMESTER +1);

         /* get a list of users in this course */
	 ptr->user_head = build_option_person_list(&conf);
	 ptr->user_current = ptr->user_head;
	 ptr->next =0;
	 if(!list->head)
             list->head = ptr;
	 else
             list->current->next = ptr;
	 list->current = ptr;
      }
   }
   
   if(doing_templates)
     printf("\nFound %d course_templates.\n", template_count);
   else
     printf("\nFound %d normal and %d standalone courses.\n",
     					normal_count, standalone_count);  
   closedir(dir_ptr);
   return list;
}



/*** copies the username of the first teacher found
**** in list to 'username'.  Returns 1 if a teacher
**** was found, else returns 0
***/

int
find_temp_course_teacher(char *username, P_NODE *head)
{
   P_NODE *ptr;
   for(ptr = head; ptr; ptr = ptr->next)
   {
      if(ptr->data.group == FACULTY)
      {
         strncpy(username, ptr->data.username, MAX_USERNAME +1);
	 return 1;
      }
   }
   return 0;
}



/*** For course templates, the subdirectory is actually
**** derived as 'T' followed by the teacher's username.
**** This function changes the value of 'subdir' to the 
**** new correct value
***/

void
update_temp_course_list(XML_COURSE_INFO_LIST *list)
{
   XML_COURSE_INFO *ptr;
   char username[MAX_USERNAME +1];

   printf("\nDetermining subdirectory name for the course templates.");
   fflush(stdout);
   for(ptr = list->head; ptr; ptr = ptr->next)
   {
      printf(".");
      fflush(stdout);
      
      if(find_temp_course_teacher(username, ptr->user_head))
      {
	snprintf(ptr->subdir, MAX_COURSE_SUBDIR +1, "T%s", username);
	*(ptr->subdir + MAX_COURSE_SUBDIR) = '\0';
      }
     else
      {
        printf("\nCould not find a teacher in the course template: %s\n", ptr -> dir_id);
        puts("Aborting.\n\n");
        exit(1);
      }
   }
   puts("done\n");
}





/** For each course in list, add the user to the USERNAME_COURSES list
*** (if not already there) and add the course to their list of courses
*** Do this only for courses that are not standalone courses!
**/

USERNAME_COURSES *
get_all_users(XML_COURSE_INFO_LIST *list, USERNAME_COURSES *head)
{
    char course_path[MAX_PATH +1];

    
    for(list->current = list->head; list->current; list->current = list->current->next)
    {
      if(list->current->course_type != STANDALONE)
      {
       snprintf(course_path, MAX_PATH +1, "%s/%s", list->current->subdir, list->current->dir_id);
       for(list->current->user_current = list->current->user_head;
	        list->current->user_current;
	        list->current->user_current=list->current->user_current->next)
          {
             printf(".");
             fflush(stdout);
             head = insert_username_data(head, list->current->user_current->data.username, course_path);
          }
      }
    }
    return head;
}





void
print_username_course_list(USERNAME_COURSES *list)
{
     USERNAME_COURSES *ptr;
     for(ptr = list; ptr; ptr = ptr->next)
     {
	printf("****\nUsername: %s\n", ptr->username);
	for(ptr->course_current = ptr->course_head; ptr->course_current; ptr->course_current=ptr->course_current->next)
          printf("course_path: %s\n", ptr->course_current->course_path);
     }
}




void
update_user_course_list(USERNAME_COURSES *head)
{
    USERNAME_COURSES *ptr;
    char course_list[MAX_PATH +1];
    FILE *fp;

    for(ptr = head; ptr; ptr = ptr->next)
    {
      printf(".");
      fflush(stdout);
      snprintf(course_list, MAX_PATH + 1, "../%s/%c/%s/%s",
	      USERS_DIR, sub_dir_name(ptr->username), ptr->username, COURSE_LIST_FNAME);
      fp = fopen(course_list, "w");
      if(fp)
      {
	  for(ptr->course_current = ptr->course_head; ptr->course_current; 
			  ptr->course_current=ptr->course_current->next)
		  fprintf(fp, "%s\n", ptr->course_current->course_path);
      }
      fclose(fp);
    }
}





void
move_courses_to_subdir(XML_COURSE_INFO_LIST *list)
{
    char dir_path[MAX_PATH +1];
    char cmd[MAX_PATH *2 +1];

    for(list->current = list->head; list->current; list->current = list->current->next)
    {
       if(list->current->course_type == TEMPLATE)
	      snprintf(dir_path, MAX_PATH +1, "%s%s", TEMPLATE_PATH, list->current->subdir);
       else 
	      snprintf(dir_path, MAX_PATH +1, "%s%s", COURSE_PATH, list->current->subdir);
       mkdir(dir_path, 0777);
       snprintf(cmd, MAX_PATH*2+1, "cd %s; mv %s %s; cd ../src",
            list->current->course_type == TEMPLATE ? TEMPLATE_PATH : COURSE_PATH,
	    list->current->dir_id, list->current->subdir);
       system(cmd);
       printf(".");
       fflush(stdout);
    }
}




void
print_list(XML_COURSE_INFO_LIST *list)
{
   for(list->current = list->head; list->current; list->current = list->current->next)
   {
	printf("*****\n");
	printf("subdir = %s\n", list->current->subdir);
	printf("dir_id = %s\n", list->current->dir_id);
	for(list->current->user_current = list->current->user_head;
			list->current->user_current; 
			list->current->user_current =list->current->user_current->next) 
	{
	  printf("Username: %s\n", list->current->user_current->data.username);
	  printf("ID: %s\n", list->current->user_current->data.id);
	  printf("Realname: %s\n", list->current->user_current->data.realname);
	}
   }
}
void free_xml_data_list( XML_COURSE_INFO_LIST *list)
{
  XML_COURSE_INFO *ptr;
  list->current = list->head;

   while(list->current)
   {
     ptr = list->current->next;
          if(list->current->user_head)
      free_option_person_list(list->current->user_head);
       free(list->current);
      list->current = ptr;
  }
     free(list);
}
void
print_course_info(XML_COURSE_INFO_LIST *list)
{
     for(list->current = list->head;
		  list->current; list->current = list->current->next)
     {
	 printf("*********\nCourse No: %s\n", list->current->course_code);
	 printf("Course Title: %s\n", list->current->title);
	 printf("Course Semester: %s\n", list->current->semester);
	 printf("Course subdir: %s\n", list->current->subdir);
	 printf("Course id: %s\n", list->current->dir_id);
	 printf("Course Type: %s\n", list->current->course_type == NORMAL ? "Normal" : list->current->course_type == TEMPLATE ?
			 "template" : "standalone");
     }
}

void
print_msg(const char *subdir, XML_COURSE_INFO_LIST *list, XML_COURSE_INFO_LIST *temp_list)
{
     if(list->head)
     {
        printf("\n\n#############\nCreated subdir: %s in courses directory\n", subdir);
        printf("The following normal and standalone courses have been moved to directory: %s\n", subdir);
        print_course_info(list);
     }
     if(temp_list->head)
     {
       printf("\n\n############\nTemplate courses are organized by teacher's username. \nThat means each teacher has their own subdir for all his/her template courses\n");

        print_course_info(temp_list);
     }

}



void
move_logs()
{
 system("mv ../users/logs ../users/old_logs");
 system("mkdir ../users/logs ; chgrp www ../users/logs ;  chmod 770 ../users/logs");
}




/*** driver for the upgrade process ***/
void
do_upgrade(const char *subdir)
{
   XML_COURSE_INFO_LIST *list;         /* a list of normal and standalone courses */
   XML_COURSE_INFO_LIST *temp_list;    /* a list of course templates */
   USERNAME_COURSES *head =0;          /* a list of usernames and the courses they belong to */
   
   /* create the empty normal and standalone list structure */
   list = (XML_COURSE_INFO_LIST *)malloc(sizeof(XML_COURSE_INFO_LIST));
   if(!list)
     {
      perror("malloc() failed - Not enough memory?");
      exit(1);
     }
      
   list->head = 0;
   list->current = 0;
   
   /* create the empty course template list structure */
   temp_list = (XML_COURSE_INFO_LIST *)malloc(sizeof(XML_COURSE_INFO_LIST));
   if(!temp_list)
     {
      perror("malloc() failed - Not enough memory?");
      exit(1);
     }
      
   temp_list->head = 0;
   temp_list->current = 0;

   list = build_course_list("../courses", subdir, list);    /* build a list of normal and standalone courses */
   temp_list =build_course_list("../templates", subdir, temp_list); /* build a list of course templates */
    
   /** If there are any course templates, we need to figure out which 
   *** subdirectory within the templates directory each template needs to 
   *** be stored in.  
   **/ 
   if(temp_list->head)
       update_temp_course_list(temp_list); 

   /* If there were any normal or standalone courses, add the users to
   ** the username list (which also records which courses they belong to)
   */
   if(list->head)
     {
      printf("Building a list of users in normal courses.");
      fflush(stdout);
      head = get_all_users(list, head);
      puts("done\n");
     } 
     
   /** If there were any course templates, add the users to the username list */   
   if(temp_list->head)
    {
      printf("Building a list of users in course templates.");
      fflush(stdout);
      head = get_all_users(temp_list, head);
      puts("done\n");
    }
    
   /** If there were any normal/standalone courses, move them to the subdirectory */
   if(list->head)
    {
      printf("Moving normal and standalone course directories.");
      fflush(stdout);
      move_courses_to_subdir(list);
      puts("done\n");
    }  

   /*** move the course templates to the appropriate subdir within the templates dir ***/
   if(temp_list->head)
    {
      printf("Moving course template directories.");
      fflush(stdout);
      move_courses_to_subdir(temp_list);
      puts("done\n");
    }
      
    /*** rewrite all of the individual user's course list files ****/
    if(head)
     {
      printf("Updating user info to reflect new course locations.");
      fflush(stdout);
      update_user_course_list(head);  
      puts("done\n");
     }
      
    /* report success ***/
    // print_msg( subdir, list, temp_list);
    free_xml_data_list( list);
    free_xml_data_list( temp_list); 
    free_username_courses(head); 
    
    /* the old log files need to be moved away, since they are incompatible
    ** with the new version
    **/
    
    puts("Moving obsolete login logs to ../users/old_logs.\n");
    move_logs();
    
    /** need to delete this index file so the super-user can re-create
    *** his account
    **/
    puts("Deleting administrative user accounts.\n");
    unlink("../users/admin/index.txt");
    
    
}



void
show_super_user_instructions()
{
 printf("\n\nYou must now create a new super-user account.\n");
 printf("Visit: http(s)://YOURSERVER.edu/%s/super_conf\n\n",  SBIN_ALIAS);
} 
 



/** checks to see if a file named SUBDIR_NAME
*** already exists in the courses directory.
*** If it does, we stop, since it (probably) means
*** this program was already run once
**/
void
check_validity()
{
 char path[MAX_PATH + 1];
 
 snprintf(path, MAX_PATH + 1, "../courses/%s", SUBDIR_NAME);
 if(file_exists(path))
 {
    printf("A directory (or file) named %s already exists.\n", path);
    printf("Did you already run this program?\n");
    printf("Aborting\n\n.");
    exit(1);
 }   
   
}




int main()
{ 

    show_instructions();
    check_validity();
    do_upgrade(SUBDIR_NAME);
    show_super_user_instructions();
    return 0; 
} 
