#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_grade_book_db.h"

void
read_parameters ( char *course, char *key, int *save_option, int *cat_id)
{
     char temp[MAX_KEY];
     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;
     else if(strcmp(temp, "no") == 0)
	     *save_option = 0;
     else
     {
        ptr = strstr(temp, "delete");
        if(!ptr)
	   cs_critical_error(ERR_PARAM_MISSING, "delete");
	ptr = strchr(temp, ':');
	if(!ptr)
	   cs_critical_error(ERR_PARAM_MISSING, "delete");
	*ptr = '\0';
        ptr++; 
        *save_option = 2;
	*cat_id = atoi(ptr);
	
     }
     

}

int check_name(char *cat_name, CATEGORY *dest)
{
    if(!strlen(cat_name))
    {
	 *(dest->cat_name) = '\0';
	 return 0;
    }
    strncpy(dest->cat_name, cat_name, MAX_CAT_LEN +1);

    if(!check_spe_char(cat_name))
        return 0;
    return 1;

}

int check_weight(const char *weight, char *ungraded, CATEGORY *ptr)
{
   double value;

   if(!strlen(weight))   
   {
        ptr->weight = 0;
	return 0;
   }
   if(!check_float_number(weight))
   {
       ptr->weight = 0;
       return 0;
   }	   
   value = atof(weight);
   if(!strcmp(ungraded, "yes"))
   {
       if(value > 0)
       {
	  ptr->weight = value;
	  return 0;
       }
   }
   ptr->weight = value;
   return 1;
}


	
CATEGORY_LIST *
check_all_categories(int *error, double *total, int *no_id)
{
 #define MAX_TMP 40
   CATEGORY_LIST *cat_list;
   CATEGORY *ptr;
   char name[MAX_TMP];
   int number_categories =0, i =0;
   char *weight = 0, *cat_name = 0, *cat_id =0, *ungraded =0, *drop =0;
   int error_count = 0;
   double total_weight = 0;
   int no_id_count =0;

   number_categories = hdf_get_int_value(global_cgi->hdf, "Query.number_categories", 0);
   if(number_categories == 0)
     cs_critical_error(ERR_PARAM_MISSING, "check_all_categories");

   cs_set_int_value("number_categories", number_categories);

   cat_list = ( CATEGORY_LIST *)malloc(sizeof(CATEGORY_LIST));
   if(!cat_list)
	cs_critical_error(ERR_MALLOC_FAILED, "check_all_categories");
   cat_list->head =0;
   cat_list->current =0;
   
   for (i = 1; i<=number_categories; i++)
   {
      snprintf(name, MAX_TMP, "Query.cat_name_%d", i);
      cat_name = hdf_get_value(global_cgi->hdf, name, "");
      snprintf(name, MAX_TMP, "categories.%d.cat_name", i);
      cs_set_value(name, cat_name);

      snprintf(name, MAX_TMP, "Query.cat_id_%d", i);
      cat_id = hdf_get_value(global_cgi->hdf, name, "");
      snprintf(name, MAX_TMP, "categories.%d.cat_id", i);
      cs_set_value(name, cat_id);

      snprintf(name, MAX_TMP, "Query.cat_weight_%d", i);
      weight = hdf_get_value(global_cgi->hdf, name, "");
      snprintf(name, MAX_TMP, "categories.%d.cat_weight", i);
      cs_set_value(name, weight);


      snprintf(name, MAX_TMP, "Query.cat_ungraded_%d", i);
      ungraded = hdf_get_value(global_cgi->hdf, name, "");
      snprintf(name, MAX_TMP, "categories.%d.cat_ungraded", i);
      cs_set_value(name, ungraded);

      snprintf(name, MAX_TMP, "Query.cat_drop_%d", i);
      drop = hdf_get_value(global_cgi->hdf, name, "");
      snprintf(name, MAX_TMP, "categories.%d.cat_drop", i);
      cs_set_value(name, drop);
      

      if( strlen(cat_name) || strlen(weight) || strlen(ungraded))
      {
	  ptr = (CATEGORY *)malloc(sizeof(CATEGORY));
          if(!ptr)
	     cs_critical_error(ERR_MALLOC_FAILED, "check_all_categories");
          ptr->next = 0;

	  if(!check_name(cat_name, ptr))
		error_count++;
	  if(!check_weight(weight, ungraded, ptr))
		error_count++;
	  else
	       total_weight = total_weight + ptr->weight;
          
	   if(strlen(cat_id))
	     ptr->cat_id = atoi(cat_id);
	   else
	   {
	     ptr->cat_id = -1;
	     no_id_count++;
	   }

	   ptr->drop_lowest = atoi(drop);
           if(!cat_list->head)	
	     cat_list->head = ptr;
	   else
	     cat_list->current->next = ptr;
	   cat_list->current = ptr; 
	      
      } 
   } 
   *error = error_count;
   *total = total_weight;
   *no_id = no_id_count;

   return cat_list; 

   
   
 #undef MAX_TMP 
}



int get_count(CATEGORY_LIST *cat_list)
{
   int count = 0;
   CATEGORY *ptr;
   for( ptr = cat_list->head; ptr; ptr = ptr->next)
   {
	if(ptr->cat_id != -1)
	    count++;
   }
   return count;
}

int check_conflict(CATEGORY_LIST *category_list, int no_id, CONFIG_STRUCT *conf)
{
    int g_count =0;
    int new_count =0; 

    if(!no_id)
	return 1;

    get_categories(conf, GRADE_BOOK_DB);   /* shared_grade_book_db.h */
    
   if(global_cat_list && global_cat_list->head)
   {
	g_count = get_count(global_cat_list);
	new_count = get_count(category_list);
	if(g_count == new_count)
	   return 1;
	else
	  return 0;
   }
   else
     return 1;
}


void
save_categories( CATEGORY_LIST *category_list, CONFIG_STRUCT *conf, int no_id)
{
    char path[MAX_PATH +1];
    int ret = 0;
    sqlite3 *db;
    char *stmt;
    CATEGORY *ptr;

  if(check_conflict(category_list, no_id, conf))
  {
    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, "save categories function");

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

    }

    for(ptr = category_list->head; ptr; ptr = ptr->next)
    {
        if(ptr->cat_id == -1)
	   stmt = sqlite3_mprintf("insert into Categories  values(NULL, '%q', %4.2f, %d)", ptr->cat_name, ptr->weight, ptr->drop_lowest);
        else
	   stmt = sqlite3_mprintf("update Categories set catName='%q', weight=%4.2f, dropLowest=%d where catId=%d",  ptr->cat_name, ptr->weight, ptr->drop_lowest, ptr->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, "error insert/update categories function");
	     }
	     sqlite3_free(stmt);

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

void delete_category(int cat_id, CONFIG_STRUCT *conf)
{
    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, "delete category function");
   stmt = sqlite3_mprintf("delete from Categories where catId=%d ", 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, "error delete category function");
	 }
	 sqlite3_free(stmt);

   }
  sqlite3_close(db);
    

}


void
do_action(const char *course, const char *key, int save_opt, CONFIG_STRUCT *conf, int cat_id)
{
  char *next_url =0;
  int error = 0;
  double total = 0;
  int no_id = 0;


  CATEGORY_LIST *category_list;

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

  if(save_opt == 0)    /* do not save anything */
	printf("Location: %s\n\n", next_url);
  else if(save_opt == 1)   /* save changes or new categories */
  {
     category_list = check_all_categories(&error, &total, &no_id);
     if(error || total > 100 || total < 100)
     {
	cs_critical_error(ERR_GENERAL_ERROR,"There is a problem with one or more values on this page. Does the sum of your weights equal 100?  Did you use anything but A-Z, a-z, 0-9, in category names?");

     }
     else
     {
        if(category_list->head)	
            save_categories(category_list, conf, no_id);
        free_category_list(category_list);
	printf("Location: %s\n\n", next_url);
     } 

  }
  else if(save_opt == 2)
  {
      delete_category(cat_id, conf);
      printf("Location: %s\n\n", next_url);
  }

}


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

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

  do_action(course, key, save_option, &conf, cat_id);
  cs_cgi_destroy();      

  return 0;			

}




