package gradebook;

/**
 * Derived from Section 2.5.2 of the requirements.
 */
import java.util.Collection;
import curve.GradeCurve;
import java.util.List;

/**
 * The core class of the Grader. Stores the class data, which in turn
 * contains the list of students and thier grades. Also stores the list
 * of assignments and the curent grade curve data.
 *
 * Includes functions for editing grades and assignments as well as
 * sorting columns.
 */
public abstract class ClassGradebook {
   /**
    * Contains the student list and thier grades.
    */
   ClassData classData;
   /**
    * The list of assignments in the gradebook.
    */
   Collection<GradedItem> assignments;
   /**
    * The current curve settings associated with the gradebook.
    */
   public GradeCurve gradeCurve;

   /**
   * A list of the categories in the spreadsheet.
   */
   Collection<Category> categories;
   
   /**
    * Creates a new Grade with the given points for the given assignment in the given student.
    */
   /*@
    requires
       //
       // grade is not null and  doesnt exists in student.grades
       //
       (grade != null) 
       
       && 
       
       (!(\exists Grade g_other ;
       student.grades.contains(g_other) ;
       g_other.assignment.name.equals(grade.assignment.name)))
       
       &&
       
       //
       // student is not null and exists in classData
       //
       (student != null) 
       
       && 
       
       (!(\exists Student s_other ;
       assignments.contains(s_other) ;
       s_other.studentID.equals(student.studentID)));
    
    ensures
    //
    // The grade exists iff it is the new one.
    //
    (\forall Grade g_other ;
    student.grades.contains(g_other) <==>
    g_other.assignment.name.equals(grade.assignment.name) || \old(student.grades).contains(grade));
    @*/
   public abstract void addGrade(Student student, Grade grade);
   
   /**
    * Changes a Grade to the given points for the given assignment in the given student.
    */
   /*@
    requires
    //
    // grade is not null and exists in student.grades
    //
    (grade != null)
    
    &&
    
    ((\exists Grade g_other ;
    student.grades.contains(g_other) ;
    g_other.assignment.name.equals(grade.assignment.name)))
    
    &&
    
    //
    // student is not null and exists in classData
    //
    (student != null)
    
    &&
    
    (!(\exists Student s_other ;
    assignments.contains(s_other) ;
    s_other.studentID.equals(student.studentID)));
    
    ensures
    //
    // The grade is updated if it exists
    //
    (\forall Grade g_other ;
    student.grades.contains(g_other) <==>
    (g_other.points == grade.points) || !(\old(student.grades).contains(grade)));
    @*/
   public abstract void editGrade(Student student, Grade grade);
   
   /**
    * Removes a Grade for the given assignment in the given student.
    */
   /*@
    requires
    //
    // grade is not null and exists in student.grades
    //
    (grade != null)
    
    &&
    
    ((\exists Grade g_other ;
    student.grades.contains(g_other) ;
    g_other.assignment.name.equals(grade.assignment.name)))
    
    &&
    
    //
    // student is not null and exists in classData
    //
    (student != null)
    
    &&
    
    (!(\exists Student s_other ;
    assignments.contains(s_other) ;
    s_other.studentID.equals(student.studentID)));
    
    ensures
    //
    // The grade does not exist
    //
    (\forall Grade g_other ;
    student.grades.contains(g_other) <==>
    !g_other.assignment.name.equals(grade.assignment.name));
    @*/
   public abstract void deleteGrade(Student student, Grade grade);
   
   //dguastaf
   /**
    * Adds a graded item to the gradebook
    */
   /*@
    requires
    //
    //GradedItem g is not null
    //
    (g != null)
    
    &&
    
    //There is not a graded item in asignments that has
    //the same name as the given graded item g
    
    (!(\exists GradedItem g_other ;
    assignments.contains(g_other) ;
    g_other.name.equals(g.name)));
    
    ensures
    
    //A GradedItem is in the list of assignments iff it is the new
    //GradedItem to be added, or it is already in the list of GradedItems
    (\forall GradedItem g_other ;
    assignments.contains(g_other) <==>
    g_other.equals(g) || \old(assignments).contains(g));
    
    @*/
   public abstract void addGradedItem(GradedItem g);
   
   //dguastaf
   /**
    * Deletes a GradedItem from the Gradebook
    */
   /*@
    requires
    //
    //GradedItem g is not null
    //
    (g != null)
    
    &&
    
    //The GradedItem is in the collection of assignments
    assignments.contains(g);
    
    
    ensures
    
    //A GradedItem g has been deleted if it is not the item to
    //be deleted and it is in the list of GradedItems
    (\forall GradedItem g_other ;
    assignments.contains(g_other) <==>
    !g_other.equals(g) || \old(assignments).contains(g));
    
   @*/
   public abstract void deleteGradedItem(GradedItem g);
   
   //dguastaf
   /**
    * Modifies a GradedItem in the gradebook
    */
   /*@
    requires
    //
    //GradedItem g is not null
    //
    (g != null)
    
    &&
    
    //The GradedItem is in the collection of assignments
    assignments.contains(g)
    
    &&
    //There is not a graded item in asignments that has
    //the same name as the given graded item g
    
    (!(\exists GradedItem g_other ;
    assignments.contains(g_other) ;
    g_other.name.equals(g.name)));
    
    ensures
    
    //The GradedItem is still in the list of GradedItems
    assignments.contains(g)
    
    &&
    
    //The old GradedItem does not exist in the list
    (!(\exists GradedItem g_other ;
    assignments.contains(g_other) ;
    g_other.equals(\old(g))));
    
    
   @*/
   public abstract void modifyGradedItem(GradedItem g);
   
   //bstanaka
   /**
    * Adds a Category to the Gradebook
    */
   /*@
    requires
    //
    //Category cat is not null
    //
    (cat != null)
    
    &&
    
    //There is not a Category that has
    //the same name as the given Category cat
    
    (!(\exists Category cat_other ;
    categories.contains(cat_other) ;
    cat_other.name.equals(cat.name)));
    
    ensures
    
    //A Category is in the list of categories iff it is the new
    //Category to be added, or it is already in the list of Categories
    (\forall Category cat_other ;
    categories.contains(cat_other) <==>
    cat_other.equals(cat) || \old(categories).contains(cat_other));
    
    @*/

   public abstract void addCategory(Category cat);

   //dguastaf
   /**
    * Deletes a Category from the Gradebook
    */
   /*@
    requires
    //
    //Category cat is not null
    //
    (cat != null)
    
    &&
    
    //The Category is in the collection of categories
    categories.contains(cat);
    
    
    ensures
    
    //A Category cat has been deleted if it is not the category to
    //be deleted and it is in the list of categories
    (\forall Category cat_other ;
    categories.contains(cat_other) <==>
    !cat_other.equals(cat) || \old(categories).contains(cat));
    
   @*/
   public abstract void deleteCategory(Category cat);


   //dguastaf
   /**
    * Modifies a Category in the gradebook
    */
   /*@
    requires
    //
    //Category cat is not null
    //
    (cat != null)
    
    &&
    
    //The Category is in the collection of categories
    categories.contains(cat)
    
    &&
    //There is not a category in categories that has
    //the same name as the given category
    
    (!(\exists Category cat_other ;
    categories.contains(cat_other) ;
    cat_other.name.equals(cat.name)));
    
    ensures
    
    //The Category is still in the list of Categories
    categories.contains(cat)
    
    &&
    
    //The old Category does not exist in the list
    (!(\exists Category cat_other ;
    assignments.contains(cat_other) ;
    cat_other.equals(\old(cat))));
    
    
   @*/
   public abstract void modifyCategory(Category cat);



   //bstanaka
   /**
    * Sorts a column denoted by g, in either ascending
    * or descending order, given by s.
    */
   /*@
    requires
    //
    // GradedItem g is not null and classData is not null.
    //
    
    (g != null) && (classData != null);
    
    ensures
    //
    // Sorts the gradebook such that g is in ascending/descending order denoted by s.
    // The sort will be stable.
    //
    
    (\forall int i ; (i >= 0) && (i < \result.size() - 1) ;
    ((\result.get(i).getGrade(g).points) - (\result.get(i+1).getGrade(g).points)) < 0)
    
    ||
    
    (\forall int i ; (i >= 0) && (i < \result.size() - 1) ;
    ((\result.get(i).getGrade(g).points) - (\result.get(i+1).getGrade(g).points)) > 0);
   @*/
   public abstract List<Student> sort(GradedItem g, SortType s);
   
   //crahm 
   /**
    * Obtains the number of students who have a particular letter grade.
    * @param letter the letter grade being searched for
    * @return the number of students who have the letter grade
    */
   /*@
       requires
       //
       //letter is a valid letter grade (i.e. A, B, C, D, F)
       //
       (letter.equals("A") || letter.equals("B") || letter.equals("C") || letter.equals("D") || letter.equals("F"));
   @*/
   public abstract int getNumStudentsWithGrade(String letter);
 
   public enum SortType {
      ASCEND, DESCEND
   }
}