#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "ClearSilver.h"
#include "sqlite3.h"
#include "global.h"
#include "custom.h"		/* for date FORMAT strings */
#include "grade.h"		/* for date FORMAT strings */
#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_grade_book_menu.h"
#include "manhat-lib/shared_file_util.h"
#include "manhat-lib/shared_grade_book_util.h"
#include "manhat-lib/shared_grade_book_db.h"
#include "manhat-lib/shared_person_list.h"

void
read_parameters ( char *course, char *key, int *item_id)
{
     char temp[MAX_KEY +1];

     cs_get_required_parameter("crs", course, MAX_PATH);
     cs_get_required_parameter("id", key, MAX_KEY);
     cs_get_required_parameter("item_id", temp, MAX_KEY);
     *item_id = atoi(temp);

}


GRADE_LIST *get_input_grades(int number, int item_id)
{
#define MAX_TMP 40
    char name[MAX_TMP];
    char temp[MAX_TMP];
    int i;
    GRADE_LIST *list =0;
    GRADE *ptr;
    char *comment;

    list = (GRADE_LIST*) malloc (sizeof(GRADE_LIST));
    if(!list)
	cs_critical_error(ERR_MALLOC_FAILED, "grade_book_enter_grades");
    list->head = 0;
    list->current = 0;

    for(i = 0; i<number; i++)
    {
        ptr = (GRADE*) malloc (sizeof(GRADE));
        if(!ptr)
	   cs_critical_error(ERR_MALLOC_FAILED, "grade_book_enter_grades");
	ptr->next =0;
	ptr->item_id =item_id;

	strncpy(ptr->letter, "chg", MAX_GRADE_LEN +1);

        snprintf(name, MAX_TMP, "username_%d", i);
	cs_get_required_parameter(name, ptr->username, MAX_USERNAME);

        snprintf(name, MAX_TMP, "excuse_%d", i);
	cs_get_optional_parameter(name, temp, MAX_TMP-1);
	if(strlen(temp))
	  strncpy(ptr->excuse, "yes", MAX_BOOLEN_LEN +1);
	else
	  strncpy(ptr->excuse, "no", MAX_BOOLEN_LEN +1);
        
	snprintf(name, MAX_TMP, "pending_%d", i);
	cs_get_optional_parameter(name, temp, MAX_TMP-1);
	if(strlen(temp))
	  strncpy(ptr->pending, "yes", MAX_BOOLEN_LEN +1);
	else
	  strncpy(ptr->pending, "no", MAX_BOOLEN_LEN +1);

        if(!strcmp(ptr->pending, "no"))
	{	
	   if(strcmp(global_item_list->head->is_letter_grade, "no") == 0)
	   {
              snprintf(name, MAX_TMP, "score_%d", i);
	      cs_get_required_parameter(name, temp, MAX_POINT_LEN);
	      ptr->score = atof(temp);
	      ptr->letter_id = -1;
	   }
	   else
	  {
             snprintf(name, MAX_TMP, "letter_%d", i);
	     cs_get_required_parameter(name, temp, MAX_GRADE_LEN);
	     ptr->letter_id = atoi(temp);
	     ptr->score = -999999;

	  }
        }
	else
        {
	    ptr->letter_id = -1;
	    ptr->score = -999999;
	}


        snprintf(name, MAX_TMP, "Query.comments_%d", i);
        comment = hdf_get_value(global_cgi->hdf, name, "");
        if(strlen(comment))
	   ptr->comment = malloc_str(comment);
        else
	   ptr->comment = malloc_str(" ");
        
       if(!list->head)	
	    list->head = ptr;
       else
	    list->current->next = ptr;

       list->current = ptr;
         
    }
    return list;

#undef MAX_TMP	
}

int is_changed(GRADE *ptr)
{
   int find = 0;

   if(!global_grade_list || !global_grade_list->head)
	return 1;

    for(global_grade_list->current = global_grade_list->head; global_grade_list->current; global_grade_list->current = global_grade_list->current->next)
    {
	 if(!strcmp(ptr->username, global_grade_list->current->username))
	 {
	     find = 1;   
             if(ptr->score != global_grade_list->current->score ||
		 strcmp(ptr->excuse, global_grade_list->current->excuse) ||
		 strcmp(ptr->pending, global_grade_list->current->pending) ||
		 ptr->letter_id != global_grade_list->current->letter_id ||
		 strcmp(ptr->comment, global_grade_list->current->comment))
		     return 1;

	 }

    }
    if(find == 0)
	 return 1;
    return 0;
}


void update_input_list(GRADE_LIST *list, int *changes)
{
     GRADE *ptr;
     int changed = 0;

     for(ptr = list->head; ptr; ptr = ptr->next)
     {
         changed = is_changed(ptr);
	 if(!changed)
	     strncpy(ptr->letter, "no", MAX_GRADE_LEN +1);
	 else
	    (*changes)++;
	     
     }
}

void  insert_grades_into_table(GRADE_LIST *list, CONFIG_STRUCT *conf)
{
    GRADE *ptr;
    int ret =0;
    sqlite3 *db;
    char *stmt;
    time_t time_stamp = time(NULL);
    char path[MAX_PATH +1];

    snprintf(path, MAX_PATH +1, "%s%s/%s", conf->course_path, GRADE_BOOK_DIR, GRADE_BOOK_DB);
    ret = sqlite3_open(path, &db);
    if(ret)
        cs_critical_error(ERR_GRADES_DB, "insert grades ");

    ret = sqlite3_exec(db, "Begin transaction insert_grades", 0, 0, 0);
    if(ret)
    {
        sqlite3_close(db);
	cs_critical_error(ERR_GRADES_DB, "begin insert grades function");

    }
    for(ptr = list->head; ptr; ptr = ptr->next)
    {
	 stmt = sqlite3_mprintf("insert into Grades values('%q', %d, %4.2f, '%q', '%q', '%q', %d, %d)",
			 ptr->username, ptr->item_id, ptr->score, 
			 ptr->excuse, ptr->pending, ptr->comment ? ptr->comment:"", ptr->letter_id, time_stamp);
	 if(stmt)
	 {
            ret = sqlite3_exec(db, stmt, 0, 0, 0);
	    if(ret)
	    {
                sqlite3_exec(db, "rollback transaction insert_grades", 0, 0, 0);
		sqlite3_free(stmt);
                sqlite3_close(db);
	        cs_critical_error(ERR_GRADES_DB, "insert grades failed");

	    }
	    sqlite3_free(stmt);
	       
        }
	
    }
    if(!strcmp(global_item_list->head->grade_entered,"no"))
    {
        stmt = sqlite3_mprintf("update Items set gradesEntered='yes' where itemId=%d", global_item_list->head->item_id);
	if(stmt)
	{
            ret = sqlite3_exec(db, stmt, 0, 0, 0);
	    if(ret)
	    {
                sqlite3_exec(db, "rollback transaction insert_grades", 0, 0, 0);
		sqlite3_free(stmt);
                sqlite3_close(db);
	        cs_critical_error(ERR_GRADES_DB, "update items failed");
	    }
	   sqlite3_free(stmt);
	}
    }
   sqlite3_exec(db, "commit transaction insert_grades", 0, 0, 0);
   sqlite3_exec(db, "end transaction insert_grades", 0, 0, 0);
   sqlite3_close(db);

}


void  do_action(CONFIG_STRUCT *conf, int item_id, const char *course, const char *key, SESSION *user)
{
    P_NODE *head;
    int number_students =0;
    GRADE_LIST *list = 0;
    int changes = 0;

    get_one_item(conf, item_id, GRADE_BOOK_DB);
    if(!global_item_list || !global_item_list->head)
       cs_critical_error(ERR_GRADES_DB, "There is no item in db");
    
    number_students = hdf_get_int_value(global_cgi->hdf, "Query.number_students", 0);
    if(number_students >0)
    {
       list = get_input_grades(number_students, item_id);
       if(!strcmp(global_item_list->head->grade_entered, "yes"))
       {
	  get_current_grade_list(conf, item_id, GRADE_BOOK_DB);  /* shared_grade_book_db.c */
	  update_input_list(list, &changes);
	  if(global_grade_list)
              free_grade_list(global_grade_list);
       }
       else
	   changes = number_students;
       if(changes >0)
           insert_grades_into_table(list, conf);
       free_grade_list(list);
       printf("Location: %s?crs=%s&id=%s&item_id=%d&save=yes\n\n", "grade_book_enter_grades", course, key, item_id); 
    }
    else
    { 

        set_item_list_values(1, 0);                  /* shared_grade_book_util.c */

       if(global_item_list->head && strcmp(global_item_list->head->is_letter_grade, "yes") ==0)
       {
           get_letter_grades(conf, item_id,GRADE_BOOK_DB);
	   if(!global_letter_grades || !global_letter_grades->head) 
	      cs_critical_error(ERR_GRADES_DB, "grade_book_enter");
	   set_letter_grades_value();
	   if(global_letter_grades)
	      free_letter_grade_list(global_letter_grades);

       }

		    
       head = build_option_person_list (conf);  /* shared_person_list.c */
       set_person_list_values(head, user);            /* shared_grade_book_util.c */

       get_current_grade_list(conf, item_id, GRADE_BOOK_DB);  /* shared_grade_book_db.c */
		        
       set_current_grade_list_values();        /* shared_grade_book_util.c */
       
       cs_set_int_value("max_point_len", MAX_POINT_LEN);  
       if(head)
	free_option_person_list(head);    /* shared_person_list.c */
  }
  if(global_item_list)
	free_item_list(global_item_list);    /* shared_grade_book_util.c */

}




int
main ()
{
  char course[MAX_PATH +1];
  char key[MAX_KEY +1];
  int item_id;
  SESSION user;
  CONFIG_STRUCT conf;
  
  cs_cgi_init();   /* shared_cs_util.h */
  read_parameters ( course, key, &item_id);
  read_configuration_file (course, &conf);	
  validate_key (key, &user, &conf);	


  if(user.group != FACULTY)
      cs_critical_error(ERR_REQUEST_DENIED, "grade_book");

  do_action(&conf, item_id, course, key, &user);

  cs_cgi_display ("grade_book_enter_grades", 1);
  cs_cgi_destroy();      

  return 0;			

}


