#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_grade_book_util.h"
#include "manhat-lib/shared_time.h"

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

     cs_get_required_parameter("crs", course, MAX_PATH);
     cs_get_required_parameter("id", key, MAX_KEY);
     cs_get_required_parameter("save_option", temp, MAX_KEY);
   

     if(strcmp(temp, "save") == 0)
	   *save_option = 1;     /* save new item */
     else if(strcmp(temp, "no") == 0)
	   *save_option = 0;
     else if(strcmp(temp, "update") == 0)  /* update items */
	   *save_option = 2;
     else   /* delete items */
     {
        ptr = strstr(temp, "delete");
	if(!ptr)
	   cs_critical_error(ERR_GRADE_BOOK_UNDEFINED_OPERATION, "grade_book_items_save");
	ptr = strchr(temp, ':');
	if(ptr)
	   *ptr = '\0';
	*save_option = 3;
	ptr++;
	*item_id = atoi(ptr);
	 

     }

}

SCALE_LIST *getLetterGrades(int number, int *error)
{
#define MAX_TMP 40
   SCALE_LIST *list = 0;
   SCALE *ptr;
   char name[MAX_TMP];
   char *letter = 0;
   char *points = 0;
   int error_count = 0;

   int i;


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

   for(i = 0; i<number; i++)
   {
       snprintf(name, MAX_TMP, "Query.letter_%d", i);
       letter = hdf_get_value(global_cgi->hdf, name, ""); 
       snprintf(name, MAX_TMP, "Query.points_%d", i);
       points = hdf_get_value(global_cgi->hdf, name, ""); 
       if(strlen(letter) && strlen(points))
       {
           if(check_spe_char(letter) && check_float_number(points))
	   {
	   
	       ptr = (SCALE*) malloc (sizeof(SCALE));
	       if(!ptr)
                  cs_critical_error(ERR_MALLOC_FAILED, "get_letter_grades loop");
	       ptr->next =0;

	       strncpy(ptr->grade, letter, MAX_GRADE_LEN +1);
	       ptr->start = atof(points);
	       if(!list->head)
		  list->head = ptr;
	       else
		  list->current->next = ptr;
	       list->current = ptr;
	   }
	   else
		error_count++;

       }
   }
   *error = error_count;
   return list;
   
#undef MAX_TMP 
}


SCALE_LIST *
validate_new_item(ITEM *new_item, int *errors)
{
     char temp[MAX_PATH +1];
     int number;
     SCALE_LIST *list = 0;
     int count = 0;
     int error_count = 0;

     cs_get_required_parameter("item_name", temp, MAX_PATH);
     if(!check_spe_char(temp))
	     count++;
     else
        strncpy(new_item->item_name, temp, MAX_ITEM_LEN);


     cs_get_required_parameter("item_category", temp, MAX_PATH);
     if(!strcmp(temp, "-1"))
	     count++;
     else
        new_item->cat_id = atoi(temp);


     cs_get_required_parameter("item_extra_credit", new_item->extra_credit, MAX_EXTRA_CREDIT_LEN);

     cs_get_required_parameter("item_due_date", temp, MAX_PATH);
     new_item->due_date = get_time("item_due_date");

     cs_get_required_parameter("grade_method", temp, MAX_PATH);
     if(!strcmp(temp, "-1"))
	     count++;

     else if(!strcmp(temp, "points"))
     {
        cs_get_required_parameter("item_points", temp, MAX_PATH);
        if(!check_float_number(temp))
	     count++;
	else
          new_item->points = atof(temp);
        strncpy(new_item->is_letter_grade, "no", MAX_BOOLEN_LEN +1); 
     }
     else if(!strcmp(temp, "letter_grades"))
     {
        cs_get_required_parameter("number_letters", temp, MAX_PATH);
        number = atoi(temp);
        list = getLetterGrades(number, &error_count);
	if(!list || !list->head)
	     count++;

	strncpy(new_item->is_letter_grade, "yes", MAX_BOOLEN_LEN +1); 
     }
     strncpy(new_item->grade_entered, "no", MAX_BOOLEN_LEN +1); 
     strncpy(new_item->hide, "no", MAX_BOOLEN_LEN +1); 
    
    
     *errors = count + error_count;
     return list;

}

void insert_item_to_db(CONFIG_STRUCT *conf, ITEM *new_item, SCALE_LIST *list)
{
    char path[MAX_PATH +1];
    int ret = 0;
    sqlite3 *db;
    char *stmt;
    SCALE *ptr;
    int new_id =0;


    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 function");

    ret = sqlite3_exec(db, "Begin transaction insert_new_item", 0, 0, 0);

    if(ret)
    {
         sqlite3_close(db);
         cs_critical_error(ERR_GRADES_DB, "begin insert item function");

     }
    stmt = sqlite3_mprintf("insert into Items values(NULL, '%q', %4.2f, %d, '%q', '%q', '%q', '%q', %d)",
             new_item->item_name, new_item->points, new_item->due_date, new_item->extra_credit, 
	     new_item->is_letter_grade, new_item->grade_entered, new_item->hide, new_item->cat_id);
    if(stmt)
    {
	 ret = sqlite3_exec(db, stmt, 0, 0,0);
	 if(ret)
	 {
	     sqlite3_free(stmt);
	     sqlite3_close(db);
	     cs_critical_error(ERR_GRADES_DB, "ERR insert new item in grade_book_items_save");

	 }
	 sqlite3_free(stmt);
         new_id = sqlite3_last_insert_rowid(db);
    }
    else
	  cs_critical_error(ERR_GRADES_DB, "ERR insert new item in grade_book_items_save");
	
    if(list && list->head && strcmp(new_item->is_letter_grade, "yes") == 0)
    {
          for(ptr = list->head; ptr; ptr = ptr->next)
	  {
	      stmt = sqlite3_mprintf("insert into LetterGrades values(NULL, '%q', %4.2f, %d)",
			     ptr->grade, ptr->start, new_id);
	      if(stmt)
	      {
	         ret = sqlite3_exec(db, stmt, 0, 0,0);
		 if(ret)
		 {
		      ret = sqlite3_exec(db, "rollback transaction insert_new_item", 0, 0, 0);
	              sqlite3_free(stmt);
	              sqlite3_close(db);
	              cs_critical_error(ERR_GRADES_DB, "ERR insert new item in grade_book_items_save");
		        
		 }
	         sqlite3_free(stmt);

	      }      


	  }
    }
    ret = sqlite3_exec(db,"commit transaction insert_new_item", 0,0,0);
    ret = sqlite3_exec(db,"end transaction insert_new_item", 0,0,0);
    sqlite3_close(db);


}

void save_new_item(CONFIG_STRUCT *conf)
{
     ITEM new_item;
     SCALE_LIST *list = 0;
     int errors = 0;

     list = validate_new_item(&new_item, &errors);
     if(errors > 0)
	cs_critical_error(ERR_GRADE_BOOK_NEW_ITEM, "save_new_item int grade_book_items_save");

     insert_item_to_db(conf, &new_item, list);
     if(list)
	 free_scale_list(list);
}

ITEM_LIST *
get_changed_items()
{
  #define MAX_TMP_NAME 60
  char name[MAX_TMP_NAME];
  char temp[MAX_TMP_NAME];
  int number = hdf_get_int_value(global_cgi->hdf, "Query.number_items", 0);
  int i;
  char item_name[MAX_ITEM_LEN +1];
  char item_name_old[MAX_ITEM_LEN +1];
  int item_cat;
  int item_cat_old;
  char extra[MAX_EXTRA_CREDIT_LEN +1];
  char extra_old[MAX_EXTRA_CREDIT_LEN +1];
  int item_points;
  int item_points_old;
  int item_id;
  char hide[MAX_BOOLEN_LEN +1];
  char hide_old[MAX_BOOLEN_LEN +1];
  ITEM_LIST *list;
  ITEM *ptr;


  if(!number)
     cs_critical_error(ERR_PARAM_MISSING, "number_items");

  list = (ITEM_LIST *)malloc(sizeof(ITEM_LIST));
  if(!list)
     cs_critical_error(ERR_MALLOC_FAILED, "get changed items");
      
  list->head =0;
  list->current =0;

  for(i = 1; i<= number; i++)
  {
      snprintf(name, MAX_TMP_NAME, "item_id_%d", i);
      cs_get_required_parameter(name, temp, MAX_TMP_NAME);
      item_id = atoi(temp);

      snprintf(name, MAX_TMP_NAME, "item_name_%d", i);
      cs_get_required_parameter(name, item_name, MAX_ITEM_LEN);
      snprintf(name, MAX_TMP_NAME, "item_name_old_%d", i);
      cs_get_required_parameter(name, item_name_old, MAX_ITEM_LEN);

      snprintf(name, MAX_TMP_NAME, "item_cat_%d", i);
      cs_get_required_parameter(name, temp, MAX_TMP_NAME);
      item_cat = atoi(temp);
      snprintf(name, MAX_TMP_NAME, "item_name_old_%d", i);
      cs_get_required_parameter(name, temp, MAX_TMP_NAME);
      item_cat_old = atoi(temp);

      snprintf(name, MAX_TMP_NAME, "item_extra_%d", i);
      cs_get_required_parameter(name, extra, MAX_EXTRA_CREDIT_LEN);
      snprintf(name, MAX_TMP_NAME, "item_extra_old_%d", i);
      cs_get_required_parameter(name, extra_old, MAX_EXTRA_CREDIT_LEN);

      snprintf(name, MAX_TMP_NAME, "item_points_%d", i);
      cs_get_optional_parameter(name, temp, MAX_TMP_NAME);
      if(strlen(temp))
         item_points = atof(temp);
      else 
	 item_points = -1;

      snprintf(name, MAX_TMP_NAME, "item_points_old_%d", i);
      cs_get_optional_parameter(name, temp, MAX_TMP_NAME);
      if(strlen(temp))
         item_points_old = atof(temp);
      else 
	 item_points_old = -1;


      if(item_points_old != -1 && item_points == -1)
	  cs_critical_error(ERR_PARAM_MISSING, "item_points"); 
      
      snprintf(name, MAX_TMP_NAME, "item_hide_%d", i);
      cs_get_required_parameter(name, hide, MAX_BOOLEN_LEN);
      snprintf(name, MAX_TMP_NAME, "item_hide_old_%d", i);
      cs_get_required_parameter(name, hide_old, MAX_BOOLEN_LEN);
      
      if(strcmp(item_name, item_name_old) || strcmp(extra, extra_old) || item_cat != item_cat_old ||
		   (item_points_old != -1 && item_points != item_points_old) || strcmp(hide, hide_old))
      {
	   ptr = (ITEM*)malloc(sizeof(ITEM));
	   if(!ptr)
               cs_critical_error(ERR_MALLOC_FAILED, "get changed items");
           ptr->item_id = item_id;
           ptr->cat_id = item_cat;
	   strncpy(ptr->item_name, item_name, MAX_ITEM_LEN +1);
	   strncpy(ptr->extra_credit, extra, MAX_EXTRA_CREDIT_LEN +1);
           ptr->points = item_points;
	   strncpy(ptr->hide, hide, MAX_BOOLEN_LEN +1);
	   ptr->next =0;

	   if(!list->head)
		list->head = ptr;
	   else
		list->current->next = ptr;
	   list->current = ptr;

      } 
  }

  return list;

  #undef MAX_TMP_NAME 
}

void update_items(CONFIG_STRUCT *conf)
{
    char path[MAX_PATH +1];
    int ret = 0;
    sqlite3 *db;
    char *stmt;
    ITEM_LIST *list=0;
    ITEM *ptr;

   list = get_changed_items();

   if(!list || !list->head)
      cs_critical_error(ERR_GRADE_BOOK_ITEM_NO_CHANGE, "update items");


    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, "update items");
    
    for(ptr=list->head; ptr; ptr = ptr->next)
    {
       stmt = sqlite3_mprintf("update Items set itemName='%q', points=%4.2f, catId=%d, extraCredit='%q', hide='%q' where itemId=%d",
             ptr->item_name, ptr->points,  ptr->cat_id, ptr->extra_credit, ptr->hide, ptr->item_id);
       if(stmt)
       {
	 ret = sqlite3_exec(db, stmt, 0, 0,0);
	 if(ret)
	 {
	     sqlite3_free(stmt);
	     sqlite3_close(db);
	     cs_critical_error(ERR_GRADES_DB, "ERR update item in grade_book_item_save");

	 }
	 sqlite3_free(stmt);
       }

	
    }
    sqlite3_close(db);
   
}

void  delete_item(CONFIG_STRUCT *conf, int item_id) 
{
    char path[MAX_PATH +1];
    int ret = 0;
    sqlite3 *db;
    char *stmt;

    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, "update items");
    ret = sqlite3_exec(db, "Begin transaction delete_item", 0, 0, 0);
    if(ret)
    {
         sqlite3_close(db);
         cs_critical_error(ERR_GRADES_DB, "begin insert item function");

     }
    stmt = sqlite3_mprintf("delete from Items where itemId=%d", item_id);
    if(stmt)
    {
       ret = sqlite3_exec(db, stmt, 0, 0, 0);
       if(ret)
       {
	     sqlite3_free(stmt);
	     sqlite3_close(db);
	     cs_critical_error(ERR_GRADES_DB, "ERR delete item in grade_book_item_save");
          
       }
      sqlite3_free(stmt);
    }
    stmt = sqlite3_mprintf("delete from letterGrades where itemId=%d", item_id);
    if(stmt)
    {
       ret = sqlite3_exec(db, stmt, 0, 0, 0);
       if(ret)
       {
           sqlite3_free(stmt);
	   ret = sqlite3_exec(db, "rollback transaction delete_item", 0, 0, 0);
	   sqlite3_close(db);
	   cs_critical_error(ERR_GRADES_DB, "ERR delete item in grade_book_item_save");
       }
      sqlite3_free(stmt);
    }
    stmt = sqlite3_mprintf("delete from Grades where itemId=%d", item_id);
    {
       ret = sqlite3_exec(db, stmt, 0, 0, 0);
       if(ret)
       {
           sqlite3_free(stmt);
	   ret = sqlite3_exec(db, "rollback transaction delete_item", 0, 0, 0);
	   sqlite3_close(db);
	   cs_critical_error(ERR_GRADES_DB, "ERR delete item in grade_book_item_save");
       }
      sqlite3_free(stmt);
    }
   ret = sqlite3_exec(db, "commit transaction delete_item", 0, 0, 0);
   ret = sqlite3_exec(db, "end transaction delete_item", 0, 0, 0);
    
  sqlite3_close(db);

    
}



void
do_action(const char *course, const char *id, CONFIG_STRUCT *conf, int save_option, int item_id)
{
  char *next_url;

  next_url = hdf_get_value(global_cgi->hdf, "Query.nextUrl", "");
  if(strlen(next_url) == 0)
      cs_critical_error(ERR_PARAM_MISSING, "grade_book_items_save");

  if(save_option == 0)   // do not save any thing just relocate to another program 
      printf("Location: %s\n\n", next_url);
  else if(save_option == 1)    // save new item 
  {
      save_new_item(conf); 
      printf("Location: %s\n\n", next_url);
  }
  else if(save_option == 2)    // update existing items 
  {
      update_items(conf); 
      printf("Location: %s\n\n", next_url);
  } 
  else if(save_option == 3)    //  delete item 
  {
      delete_item(conf, item_id); 
      printf("Location: %s\n\n", next_url);
  }

}


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

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

  cs_set_course_info(&conf);  

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

  cs_cgi_destroy();      

  return 0;			

}




