#include <stdio.h>
#include <stdlib.h>             /* for rand() */
#include <string.h>
#include <dirent.h>             /* Directory information.       */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


#include "global.h"


#include <ClearSilver.h>
#include "grade.h"	


#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_strtrm.h"
#include "manhat-lib/shared_news_util.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_file_util.h"
#include "manhat-lib/shared_person_list.h"
#include "manhat-lib/shared_grade_book_release.h"


	
void
read_parameters (char *course, char *key)
{
    cs_get_required_parameter("crs", course, MAX_PATH);
    cs_get_required_parameter("id", key, MAX_KEY);

}

void find_html_file(const char *path, char *filename)
{
   DIR *dir_p;
   struct dirent *dir_entry_p;
   int found =0;

   dir_p = opendir(path);
   if(!dir_p)
      cs_critical_error(ERR_OPENDIR_FAILED, "find html file");
  
   while (NULL != (dir_entry_p = readdir(dir_p)))
   {
            if(strstr(dir_entry_p->d_name, ".html"))
	    {
		strncpy(filename, dir_entry_p->d_name, MAX_PATH +1);
	        found = 1;	
		break;
	    }
   }  
   if(!found)
     cs_critical_error(ERR_GENERAL_ERROR, "no html file was found");
}






char *add_send_file(char *msg, const char *course)
{
    int len;
    char search_str[] = "src=";
    char add_str[MAX_PATH + 1];

    char *new_str;
    char *ptr, *ptr2;


    len = strlen(msg) * 4;
    ptr2 = msg;
 
    snprintf(add_str, MAX_PATH +1, "send_img?crs=%s&file=", course);  
    
    new_str = (char *) malloc (sizeof(char) * len +1);
    if(!new_str)
	cs_critical_error(ERR_MALLOC_FAILED, "add send file");
    *new_str = '\0'; 

    do
    {
      ptr = strstr(ptr2, search_str);
      if(ptr)
      {
	ptr = ptr + strlen(search_str) + 1;
	strncat(new_str, ptr2, ptr-ptr2);  
	strncat(new_str, add_str, strlen(add_str));
        ptr2 = strchr(ptr, '\"');
        strncat(new_str, ptr, ptr2 - ptr +1);
        ptr2++;	
      } 
      else
	 break; 
    }while(ptr2);
    if(ptr2)
       strncat(new_str, ptr2, strlen(ptr2));
    
    return new_str;
}




/*** When the GradeQuick file is printed to a PDF file on a MACintosh, the 
**** ID number winds up in a different place, on the line above the line 
**** that contains the "ID:" string.  This function is called when the usual
**** routine can't find the ID number.  returns 1 if successful, else 0
***/

static void
extract_mac_id(char *end, char *start, char *id)
{
 char *ptr;
 char *id_start;
 int eol_count;
 
 *id = '\0';
 
 for(ptr=end, eol_count = 0; (ptr > start) && eol_count != 2; ptr--)
      if(*ptr == '\n')
          eol_count++;
          
 if(eol_count == 2)
  {
   ptr = strstr(ptr, "</span>");
   if(ptr && (ptr < end))
    {
     for(id_start = ptr - 1; (*id_start != '>') && (id_start > start); id_start--);
     if(*id_start == '>')
      {
       id_start++;
       *ptr = '\0';
       strncpy(id, id_start, MAX_NAME);
       *ptr = '<';
       strtrm(id);
      }
    } 
  }
  
 if(!strlen(id))
   cs_critical_error(ERR_GENERAL_ERROR, "Cannot extract Student ID.  Did you enable the Student ID option for the GradeQuick report?");   
} 


     
   




char *add_id_tags(char *send_buffer)
{

#define START_PAGE_COMMENT  "<!-- Page "
#define END_PAGE_COMMENT  "-->\n"
#define MARKER_STRING  ">ID:"
#define ID_START "ID:"
#define ID_END  "</"
#define CLOSE_BODY_TAG "</BODY>"


   char *new_str;
   char pre_name[MAX_NAME +1];
   char new_name[MAX_NAME +1];
   char *ptr, *unprocessed_ptr, *ptr3, *ptr4, *pre_ptr =0;

   unprocessed_ptr = send_buffer;
   memset(pre_name, 0, MAX_NAME + 1);
   memset(new_name, 0, MAX_NAME + 1);

   new_str = (char *)malloc(sizeof(char) * ( (2 * strlen(send_buffer)) + 1));   /* allocate plenty of extra space */
   if(!new_str)
	cs_critical_error(ERR_MALLOC_FAILED, "add_id_tags()");
   *new_str = '\0';
   
   
   do
   {
      ptr = strstr(unprocessed_ptr, START_PAGE_COMMENT);   /* find the start of a page eg. <!-- Page   */
      if(ptr)
      {
	ptr3 = strstr(ptr, END_PAGE_COMMENT);
	if(ptr3)
	{
          ptr = ptr3 + strlen(END_PAGE_COMMENT);
          strncat(new_str, unprocessed_ptr, ptr - unprocessed_ptr);   /* copy everything up to the end of the <!-- Page xx --> comment */
        
          unprocessed_ptr = ptr;
          do
	  {
               ptr3 = strstr(ptr,MARKER_STRING);
	       if(ptr3)
	       {
	          ptr4 = strstr(ptr3, ID_START);
	          // if(ptr4... )  why no test to see if last was successful? */
                  ptr4 = ptr4 + strlen(ID_START);
	          ptr = strstr(ptr4, ID_END);
		  if(!ptr)
	              cs_critical_error(ERR_GENERAL_ERROR, "Cannot extract Student ID.  Did you enable the Student ID option for the GradeQuick report?"); 


	          strncpy(new_name, ptr4, ptr - ptr4);
                  strtrm(new_name);

                  if(!strlen(new_name))
                     extract_mac_id(ptr4, unprocessed_ptr, new_name); 
		  
	          if(!strlen(pre_name))
		  {
		   strncpy(pre_name, new_name, MAX_NAME + 1);
                   pre_ptr = ptr4; 
		  }

		  if(!strcmp(new_name, pre_name))
                    pre_ptr = ptr4; 
	       }

          }while(ptr3 && !strcmp(pre_name, new_name)); 
      
	  if(strlen(pre_name))
	  {
	    strcat(new_str, "<");
	    strcat(new_str, pre_name);
	    strcat(new_str, ">\n");

	    ptr4 = strstr(pre_ptr, START_PAGE_COMMENT);
	    if(ptr4)
	    {
	      strncat(new_str, unprocessed_ptr, ptr4-unprocessed_ptr);
	      unprocessed_ptr = ptr4 -1;
	      strcat(new_str, "</");
	      strcat(new_str, pre_name);
	      strcat(new_str, ">\n");

	    }
	    else
	    {
		ptr4 = strstr(pre_ptr, CLOSE_BODY_TAG);
	        strncat(new_str, unprocessed_ptr, ptr4-unprocessed_ptr);
	        strcat(new_str, "</");
	        strcat(new_str, pre_name);
	        strcat(new_str, ">\n");
		unprocessed_ptr = ptr4;
	        break;
	    }
	    memset(pre_name, 0, MAX_NAME +1);
	    memset(new_name, 0, MAX_NAME +1);
	  }
	}
      }
      else   /* if <!-- Page was not found */
	break;
   }while(unprocessed_ptr);

   if(unprocessed_ptr)
      strncat(new_str, unprocessed_ptr, strlen(unprocessed_ptr));
  
   return new_str; 

}

void write_data_new_file(const char *tmpdir, char *new_buffer, CONFIG_STRUCT *conf)
{
    FILE *fp;
    char *ptr, *ptr2, *ptr3, *t_title, *te_title;
    char start_title[] = "<TITLE>";
    char end_title[] = "</TITLE>";
    char start_style[] = "<STYLE "; 
    char end_style[] = "</STYLE>"; 
    char end_head[] = "</HEAD>"; 
    char path[MAX_PATH +1];

    t_title = strstr(new_buffer, start_title);
    te_title = strstr(t_title, end_title);
    te_title = te_title + strlen(end_title);


    ptr = strstr(new_buffer, start_style);
    ptr2 = strstr(ptr, end_style);
    ptr2 = ptr2 + strlen(end_style);

    ptr3 =strstr(te_title, end_head);

    snprintf(path, MAX_PATH +1, "%s/%s", tmpdir, "gradequick.html");

     
    fp = fopen(path, "w");
    if(!fp)
       cs_critical_error(ERR_FOPEN_WRITE_FAILED, path);
   
    fwrite(new_buffer, t_title - new_buffer, 1, fp);
    fprintf(fp, "<TITLE>%s %s</TITLE>\n", conf->course_no, conf->title);

    fwrite(te_title, ptr3 - te_title, 1, fp);
    fprintf(fp, "%s", "<HEAD>"); 
    fwrite(ptr, ptr2 - ptr, 1, fp);
    fprintf(fp, "%s", ptr3); 
    fclose(fp); 
}


void send_grade_file(CONFIG_STRUCT *conf)
{
    char path[MAX_PATH +1];
    struct stat statbuffer;
    char *buffer;
    FILE *fp;



    snprintf(path, MAX_PATH +1, "%s%s/%s/%s", conf->course_path, GRADE_BOOK_DIR, "gradequick", "gradequick.html");
    if(stat (path, &statbuffer))
	 cs_critical_error(ERR_STAT_FAILED, path);
    buffer = (char *) malloc(statbuffer.st_size * sizeof(char));
    if(!buffer)
	 cs_critical_error(ERR_MALLOC_FAILED, "send grade file");

    fp = fopen(path, "r");
    if(!fp)
	 cs_critical_error(ERR_FOPEN_READ_FAILED, path);

    if(fread(buffer, statbuffer.st_size, 1, fp) != 1)
	 cs_critical_error(ERR_FREAD_FAILED, path);
    fclose(fp);

    send_content_type();
    fwrite(buffer, statbuffer.st_size, 1, stdout);
    free(buffer); 
}



void parse_html(const char *course, const char *tmpdir, CONFIG_STRUCT *conf)
{
    char htmlfile[MAX_PATH +1];
    char full_path[MAX_PATH +1];
    FILE *fp;
    struct stat stat_buf;
    char *buffer, *new_buffer;
    char *ptr;
    char *send_buffer; 


    find_html_file(tmpdir, htmlfile);
    snprintf(full_path, MAX_PATH + 1, "%s/%s", tmpdir, htmlfile);

    if(stat(full_path, &stat_buf))
	 cs_critical_error(ERR_STAT_FAILED, full_path);
    fp = fopen(full_path, "r");
    if(!fp)
	 cs_critical_error(ERR_FOPEN_READ_FAILED, full_path);
     
    buffer = (char *) malloc((stat_buf.st_size + 2) * sizeof(char));
    if(!buffer)
	 cs_critical_error(ERR_MALLOC_FAILED, "parse_html()");

    if(fread(buffer, stat_buf.st_size, 1, fp)!=1)
	 cs_critical_error(ERR_FREAD_FAILED, full_path);
    fclose(fp);

    ptr = buffer + stat_buf.st_size - 1;
    if(*ptr != '\n')
    {
        ptr++;
	*ptr = '\n';

    }
    
    *(ptr + 1) = '\0';

    send_buffer = add_send_file(buffer, course);
    free(buffer);
    
    new_buffer = add_id_tags(send_buffer);
    free(send_buffer);
    
    write_data_new_file(tmpdir, new_buffer, conf);
    free(new_buffer);
}


void
get_tmp_name(char *tempdir)
{
   int fd;

   snprintf (tempdir, MAX_PATH + 1, "%sCGI_DATA_XXXXXX", TMP_PATH);
   fd = mkstemp (tempdir);
   if(fd == -1)
        cs_critical_error(ERR_MKSTEMP_FAILED, "");
   close(fd);
   change_mkstemp_permission(tempdir);   /*shared_file_util.c */
}

void make_work_space(const char *tempdir)
{
    unlink(tempdir);
    if(mkdir(tempdir, 0770))
	 cs_critical_error(ERR_MKDIR_FAILED, tempdir);
}




void write_uploaded_file(const char *tempdir, FILE *fp)
{
#define CHUNKSIZE 2048

    char path[MAX_PATH +1];
    FILE *w_fp;
    int bytes_read;
    int bytes_left;
    char buffer[CHUNKSIZE];
    struct stat file_stat;

   
    if(fstat(fileno(fp), &file_stat))
	 cs_critical_error(ERR_NO_FILE, "write_uploaded_file()");
    if(file_stat.st_size ==0)
	 cs_critical_error(ERR_NO_FILE, "write_uploaded_file()");


    snprintf(path, MAX_PATH +1, "%s/report.pdf", tempdir);
    w_fp = fopen(path, "w");
    if(!w_fp)
	cs_critical_error(ERR_FOPEN_WRITE_FAILED, path);

    bytes_left = file_stat.st_size;
    bytes_read = CHUNKSIZE;
    while (bytes_left)
    {
       if (bytes_left < CHUNKSIZE)
          bytes_read = bytes_left;
       if (fread (buffer, bytes_read, 1, fp) != 1)
          cs_critical_error (ERR_FREAD_FAILED, "in write_uploaded_file()");
       bytes_left -= bytes_read;
       if (fwrite (buffer, bytes_read, 1, w_fp) != 1)
          cs_critical_error(ERR_FWRITE_FAILED, path);

    }
  fclose (w_fp);

#undef CHUNKSIZE 
}


void convert_pdf_to_html(const char *tempdir)
{
   char command[MAX_PATH*2 +1];

   snprintf(command,MAX_PATH *2 +1, "cd %s ; pdftohtml -c -q -noframes report.pdf ; cd ../../bin", tempdir);
   if(system(command) == -1)
	cs_critical_error(ERR_GENERAL_ERROR, "Error running pdftohtml utility. Is it installed on your server?");

}

void kill_old_gradequick(CONFIG_STRUCT *conf) 
{
   char path[MAX_PATH +1];

   snprintf(path, MAX_PATH +1, "%s%s/%s", conf->course_path, GRADE_BOOK_DIR, "gradequick");
   if(file_exists(path))
	kill_directory(path);   
}


void  move_tmp_to_course(CONFIG_STRUCT *conf, const char *tempdir)
{
   char command[MAX_PATH *2 +1];
   char path[MAX_PATH +1];
   char *ptr;

   strncpy(path, tempdir, MAX_PATH +1);
   ptr = strrchr(path, '/');
   *ptr = '\0';
   ptr++;

   kill_old_gradequick(conf); 
   snprintf(command, MAX_PATH *2 +1, "cd ../tmp ; mv %s %s%s/gradequick ; cd ../bin", ptr, conf->course_path, GRADE_BOOK_DIR);

   if(system(command) == -1)
	cs_critical_error(ERR_GENERAL_ERROR, "move tmp dir to course failed");

}

void
deliver_new_file_to_students(CONFIG_STRUCT *conf, P_NODE *head)
{
   P_NODE *ptr;
   for(ptr = head; ptr; ptr = ptr->next)
   {
     if(ptr->data.group != FACULTY && *(ptr->data.realname) != '*')
       write_student_newfile (ptr->data.username, conf);
   }
}




void create_gradebook_dir(CONFIG_STRUCT *conf)
{

    char name[MAX_PATH + 1];

    snprintf(name, MAX_PATH +1, "%s%s", conf->course_path, GRADE_BOOK_DIR);
    if(!file_exists(name))
        mkdir(name, 0775);

}


void  do_action(FILE *fp, const char *course, CONFIG_STRUCT *conf)
{
    char extension[MAX_FILENAME + 1];
    char *filename;
    char tempdir[MAX_PATH +1];
    P_NODE *head;



    filename = hdf_get_value(global_cgi->hdf, "Query.grades_file", "");

    if(strlen(filename) ==0)
	cs_critical_error(ERR_GENERAL_ERROR, "No PDF file specified.");

    get_extension(filename, extension);   /*shared_news_util.c */
    if(strcmp(extension, "pdf"))
	cs_critical_error(ERR_GENERAL_ERROR, "The uploaded file must end in .pdf");

    create_gradebook_dir(conf);
    
    get_tmp_name(tempdir);
    make_work_space(tempdir);
    write_uploaded_file(tempdir, fp);

    convert_pdf_to_html(tempdir);
    
    parse_html(course, tempdir, conf);
    move_tmp_to_course(conf, tempdir);

    head = build_option_person_list(conf);
    delete_redfiles(head, conf);
    deliver_new_file_to_students(conf, head);
    free_option_person_list(head);
    send_grade_file(conf);
}





int
main ()
{
  char course[MAX_PATH +1];
  char key[MAX_KEY +1];
  FILE *fp;

  SESSION user;
  CONFIG_STRUCT conf;


  cs_cgi_init();    /*shared_cs_util.c */

  read_parameters (course, key);
  read_configuration_file (course, &conf);	
  validate_key (key, &user, &conf);	

  fp = cgi_filehandle(global_cgi, "grades_file");
  if(!fp)
     cs_critical_error(ERR_UPLOAD_FILE, "");

  do_action(fp, course, &conf);
  
  cs_cgi_destroy();           
                              
                              

  return 0;		

}






