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

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

}






int
get_item_id()
{
   int seed, i;
   seed = time(NULL);
   srand(seed);
   i = 10000+(int)(90000.0 *rand()/(RAND_MAX + 10000.0));
    return i;
}

int
get_category_id()
{
   int seed, i;
   seed = time(NULL);
   srand(seed);
   i = 1000+(int)(9000.0 *rand()/(RAND_MAX + 1000.0));
    return i;
}



int
create_db_tables(CONFIG_STRUCT *conf)
{
    char name[MAX_PATH +1];
    int item_id, cat_id;
    int ret;
    sqlite3 *db;
    char *statement =0;


    /* begin creating tables */
    snprintf(name, MAX_PATH +1, "%s%s/%s", conf->course_path, GRADE_BOOK_DIR, GRADE_BOOK_DB);
    ret = sqlite3_open(name, &db);
    if(ret)
       return 0;

   ret = sqlite3_exec(db, 
		   "create table Categories(catId integer primary key, catName varchar(40), weight float, dropLowest integer)", 0, 0,0);
   if(ret)
   {
      sqlite3_close(db);
      return 0;
   }

           
    ret = sqlite3_exec( db, 
        "create table Items( itemId integer primary key, itemName varchar(40), points float,  dueDate real, extraCredit varchar(10), isLetterGrade varchar(5), gradesEntered varchar(5), hide varchar(5), catId integer)", 0, 0, 0);
   if(ret)
   {
      sqlite3_close(db);
      return 0;
   }
   ret = sqlite3_exec(db, 
	"create table LetterGrades(letterId integer primary key, letter varchar(10), points float, itemId integer)", 0, 0, 0);
   if(ret)
   {
      sqlite3_close(db);
      return 0;
   }

   ret = sqlite3_exec(db, 
       "create table Grades(userName varchar(20), itemId integer, earnPoints float, excuse varchar(5), isPending varchar(5), comments text, letterId integer, timestamp real)", 0, 0, 0);
   if(ret)
   {
      sqlite3_close(db);
      return 0;
   }

    
   ret = sqlite3_exec(db, "create table Scales(scaleId integer primary key, grade varchar(10), lowEnd float, highEnd float)", 0, 0, 0);
   if(ret)
   {
      sqlite3_close(db);
      return 0;
   }


    /* end of creating tables */

    /* insert dummy rows into Items,Categories,Scales,LetterGrades table to force primary key to be certain length of integer */

    item_id = get_item_id();   /* get first item id number */
    cat_id = get_category_id();     /* get first group id number */

    statement = sqlite3_mprintf(  
      "insert into Items values(%d, '%q', 0, 0, '%q', '%q', '%q', '%q', 0)",
       item_id, "XXXXXXXXXXXX", "No", "No", "No", "No");

    if(statement)   
    {
        ret = sqlite3_exec(db, statement, 0,0,0); 
       if(ret)
       {
          sqlite3_free(statement);
          sqlite3_close(db);
	  return 0;
       }
       sqlite3_free(statement);
    }

    statement = sqlite3_mprintf(  
      "insert into Categories values(%d, '%q', -1, %d)",
			       cat_id, "XXXXXXXXXXXX", 0);
    if(statement)
    {
        ret = sqlite3_exec(db, statement, 0,0,0);
       if(ret)
       {
          sqlite3_free(statement);
          sqlite3_close(db);
	  return 0;
       }
       sqlite3_free(statement);
    }

    cat_id = get_category_id();     /* get first Scale id number */

    statement = sqlite3_mprintf(  
      "insert into Scales values(%d, '%q', 0, 0)",
       cat_id, "XXXXX");
    if(statement)
    {
       ret = sqlite3_exec(db, statement,0,0,0); 
       if(ret)
       {
          sqlite3_free(statement);
          sqlite3_close(db);
	  return 0;
      }
       sqlite3_free(statement);
    }

    statement = sqlite3_mprintf(  
      "insert into LetterGrades values(%d, '%q', 0, 0)",
       cat_id, "XXXXX");
    if(statement)
    {
       ret = sqlite3_exec(db, statement,0,0,0); 
       if(ret)
       {
          sqlite3_free(statement);
          sqlite3_close(db);
	  return 0;
      }
       sqlite3_free(statement);
    }
    sqlite3_close(db);
    return 1;
}



int create_database(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);

    snprintf(name, MAX_PATH +1, "%s%s/%s", conf->course_path, GRADE_BOOK_DIR, GRADE_BOOK_DB);
    if(!file_exists(name))
        return create_db_tables(conf);
    return 1;
}

void get_all_grades(CONFIG_STRUCT *conf)	
{
    if(global_item_list && global_item_list->head)
    {
	for(global_item_list->current = global_item_list->head; global_item_list->current; global_item_list->current = global_item_list->current->next)
	{
	   if(strcmp(global_item_list->current->grade_entered, "yes") ==0)
	   {
	
	       get_one_item_grade_list(conf, global_item_list->current->item_id, GRADE_BOOK_DB); /* shared_grade_book_db.h */
	       if(global_grade_temp_list && global_grade_temp_list->head)
	       {
		  
		   if(strcmp(global_item_list->current->is_letter_grade, "yes") == 0)
			 update_letter_log_list(conf,  global_item_list->current->item_id, GRADE_BOOK_DB, global_grade_temp_list);  /* shared_grade_book_db.h */

	               global_item_list->current->grades = global_grade_temp_list;
	       }

	   }
	}
    }
}

void set_all_item_grades()
{
#define MAX_TMP 50
    GRADE *ptr;
    ITEM *item_ptr;
    char name[MAX_TMP];
    char temp[MAX_TMP];


    for(item_ptr = global_item_list->head; item_ptr; item_ptr = item_ptr->next)
    {
	 if(item_ptr->grades)
	 {
            for(ptr = item_ptr->grades->head; ptr; ptr = ptr->next)
	    {
	       if(strcmp(item_ptr->is_letter_grade, "no") == 0)
	       {
	          snprintf(name, MAX_TMP, "score.%s.%d.score", ptr->username, ptr->item_id);
	          snprintf(temp, MAX_TMP, "%4.*f", test_precision(ptr->score), ptr->score);
                  cs_set_value(name, temp);
	       } 
	       else
	       {
	          snprintf(name, MAX_TMP, "score.%s.%d.letter", ptr->username, ptr->item_id);
                  cs_set_value(name, ptr->letter);
	       }
	      snprintf(name, MAX_TMP, "score.%s.%d.excuse", ptr->username, ptr->item_id);
              cs_set_value(name, ptr->excuse);
	      snprintf(name, MAX_TMP, "score.%s.%d.pending", ptr->username, ptr->item_id);
              cs_set_value(name, ptr->pending);
	      snprintf(name, MAX_TMP, "score.%s.%d.item_id", ptr->username, ptr->item_id);
              cs_set_int_value(name, ptr->item_id);
	      snprintf(name, MAX_TMP, "score.%s.%d.drop", ptr->username, ptr->item_id);
              cs_set_value(name, ptr->drop);
               
            }
	     
	 }
    }

 #undef MAX_TMP 
}

void set_std_total_grade(TOTAL_GRADE *std_grade)
{
   #define MAX_TMP 50
   TOTAL_GRADE *ptr;
   char name[MAX_TMP];
   char temp[MAX_TMP];
   double total =0;
   double max =-1;
   double min =999999;
   double avg =0;
   char letter[MAX_GRADE_LEN +1];
   int count =0;

    for(ptr = std_grade; ptr; ptr = ptr->next)
    {
       
	if(ptr->total >=0)
	{

          snprintf(name, MAX_TMP, "score.%s.total", ptr->username);
          snprintf(temp, MAX_TMP, "%4.*f", test_precision(ptr->total), ptr->total);
	  cs_set_value(name, temp);
          snprintf(name, MAX_TMP, "score.%s.grade", ptr->username);
	  cs_set_value(name, ptr->grade);
	  total += ptr->total;
	  if(max < ptr->total)
              max = ptr->total;
	  if(min > ptr->total)
	     min = ptr->total;
        count++;
	}
	
    }

  if(count >0 && total >= 0)
  {
   avg = total/(float)count; 

   snprintf(temp, MAX_TMP, "%4.2f", global_grade_conf.rounding_method == WHOLE_NUMBER ? whole_number(avg): nearest_tenth(avg));
   cs_set_value("avg", temp);

   snprintf(temp, MAX_TMP, "%4.2f", global_grade_conf.rounding_method == WHOLE_NUMBER ? whole_number(min): nearest_tenth(min));
   cs_set_value("min", temp);
   
   snprintf(temp, MAX_TMP, "%4.2f", global_grade_conf.rounding_method == WHOLE_NUMBER ? whole_number(max): nearest_tenth(max));
   cs_set_value("max", temp);
   
   get_one_student_grade(global_grade_conf.rounding_method == WHOLE_NUMBER ? whole_number(min): nearest_tenth(min), letter);
   cs_set_value("min_grade", letter);

   get_one_student_grade(global_grade_conf.rounding_method == WHOLE_NUMBER ? whole_number(max): nearest_tenth(max), letter);
   cs_set_value("max_grade", letter);

   get_one_student_grade(global_grade_conf.rounding_method == WHOLE_NUMBER ? whole_number(avg): nearest_tenth(avg), letter);
   cs_set_value("avg_grade", letter);
  } 
 #undef MAX_TMP 
}



void  do_action(CONFIG_STRUCT *conf, SESSION *user)
{
    P_NODE *head;
    TOTAL_GRADE *std_grade =0;

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

    get_categories(conf, GRADE_BOOK_DB);  /* shared_grade_book_db.h */



    if(global_cat_list && global_cat_list->head)
    {
	set_cat_list_values();               /* shared_grade_book_util.h */
        get_items(conf, GRADE_BOOK_DB, 0);     /* shared_grade_book_db.h */
	if(global_item_list && global_item_list->head)
	{
           get_all_grades(conf);	

	   get_items_min_avg_max(conf, GRADE_BOOK_DB);   /* shared_grade_book_calculation_util.h */

           set_all_item_grades();
	   set_item_list_values(1, 0);        /* shared_grade_book_util.h */
	   std_grade = calculate_total_grades(head, conf, GRADE_BOOK_DB); /* shared_grade_book_calculation_util.h */
	   if(std_grade)
	   {
	        set_std_total_grade(std_grade);
		free_total_grade_list(std_grade);
                cs_set_int_value("max_min_avg", 1);
                cs_set_int_value("view_bar", 1);
                cs_set_int_value("view_total", 1);
	   }
         

	}

    }


    if(head)
	free_option_person_list(head);    /* shared_person_list.c */
    if(global_cat_list)
	free_category_list(global_cat_list);
    if(global_item_list)
	free_item_list(global_item_list);
    if(global_scale_list)
       free_scale_list(global_scale_list);

}




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

  SESSION user;
  CONFIG_STRUCT conf;
  
  cs_cgi_init();   /* shared_cs_util.h */
  read_parameters ( course, key);
  read_configuration_file (course, &conf);	
  validate_key (key, &user, &conf);	
  cs_set_course_info(&conf);  
  set_grade_book_menu("grade_book", key, course); /* shared_grade_book_menu.h */
  cs_load_lang("grade_book_menu_util");

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

  if(!create_database(&conf))
	 cs_critical_error(ERR_GRADES_DB, "grade_book");
  
  do_action(&conf, &user);

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

  return 0;			

}


