/**
 * a class representing a student's grades 
 * 
 * a StudentGrades instance needs a student's last name, 
 *     a student's first name, and the number of (numeric) grades there
 *     will be for that student.
 * 
 * It provides means for getting and setting these values,
 *     getting and setting that many grades for that student,
 *     for obtaining that student's average grade,
 *     and for seeing if that student's average is higher
 *     than another's
 * 
 *  @author Sharon Tuttle 
 *  @version 2017-11-19
 *
 */

public class StudentGrades
{
    // data fields
    
    private String lastName;
    private String firstName;
    private int numGrades;
    private double grades[];
    
    /**
     * default constructor for StudentGrades
     */
    
    public StudentGrades()
    {
        this.lastName = "";
        this.firstName = "";
        this.numGrades = 0;
        this.grades = null;
    }
    
    /**
     * create a new StudentGrades instances with the given first
     *     and last name, and with a grades array of the specified
     *     size, with initial grades values of 0
     * (IF desired number of grades is 0, grades array is initially null;
     * if desired number of grades is < 0, initial number of grades is
     *     set to be 0, and grades array is also initially null)
     * 
     * @param desiredLastName new student's last name
     * @param desiredFirstName new student's first name
     * @param desiredNumGrades number of grades this student should be
     *        able to have
     */
    
    public StudentGrades(String desiredLastName, String desiredFirstName,
                         int desiredNumGrades)
    {
        this.lastName = desiredLastName;
        this.firstName = desiredFirstName;
        
        // don't allow numGrades to be less than 0
        
        if (desiredNumGrades >= 0)
        {
            this.numGrades = desiredNumGrades;
        }
        else
        {
            this.numGrades = 0;
        }
        
        // ONLY initialize grades array if numGrades is > 0
        
        if (this.numGrades == 0)
        {
            this.grades = null;
        }
        else
        {
            this.grades = new double[this.numGrades];
        
            // initially set this new student's grades to all 0s
        
            for (int i=0; i < this.numGrades; i++)
            {
                this.grades[i] = 0;
            }
        }
    }
    
    /**
     * return the last name for the calling StudentGrades instance
     * 
     * @return last name for calling StudentGrades instance
     */
    
    public String getLastName()
    {
        return this.lastName;
    }
    
    /**
     * return the first name for the calling StudentGrades instance
     * 
     * @return first name for calling StudentGrades instance
     */
    
    public String getFirstName()
    {
        return this.firstName;
    }
    
    /**
     * return the number of grades for the calling StudentGrades instance
     * 
     * @return number of grades for calling StudentGrades instance
     */
    
    public int getNumGrades()
    {
        return this.numGrades;
    }
    
    /**
     * return the grade with the specified index from the calling StudentGrades 
     *     instance. Return -1 if specified index is < 0 or >= the number of grades 
     *     for that instance.
     * 
     * @param whichGrade the index of the grade desired from the calling 
     *                   StudentGrades instance
     * @return the grade with that index from the calling StudentGrades instance,
     *         or -1 if the given index is >= the number of grades for that 
     *         instance
     */
    
    public double getGrade(int whichGrade)
    {
        // if specified grade index is unreasonable, return -1
        
        if ((whichGrade < 0) || (whichGrade >= this.numGrades))
        {
            return -1;
        }
        else
        {
            return this.grades[whichGrade];
        }
    }
    
    /** 
     * set the StudentGrades instance's last name to the given last name
     * 
     * @param newLastName the new last name for the calling StudentGrades instance
     */
    
    public void setLastName(String newLastName)
    {
        this.lastName = newLastName;
    }
    
    /** 
     * set the StudentGrades instance's first name to the given first name
     * 
     * @param newFirstName the new first name for the calling StudentGrades instance
     */
    
    public void setFirstName(String newFirstName)
    {
        this.firstName = newFirstName;
    }
    
    /** 
     * set the StudentGrades instance's number of grades to the given number of
     *     grades (except, if it is less than 0, number of grades is set to 0
     *     instead)
     * NOTE: this is quite draconian! If the StudentGrades instance HAD grades before,
     *     and this new number is SMALLER, it "throws away" the excess grades!
     *     (and if it is larger, it initializes the "new additional" grades to 0s)
     * 
     * @param newNumGrades the new number of grades for the calling StudentGrades instance
     */
    
    public void setNumGrades(int newNumGrades)
    {
        // only bother doing anything if new number of grades is
        //     different than the OLD number of grades
        
        if (newNumGrades == this.numGrades)
        {
            // no changes needed!

            return; 
        }
        
        //  ...and if new number of grades is <= 0, reset grades array to null
        
        else if (newNumGrades <= 0)
        {
            this.grades = null;
            
            // but don't allow a non-zero numGrades
            
            this.numGrades = 0;
            return;
        }
        
        // if REACH here, this IS a new (non-zero) number of grades than before
        //
        // SAVE current number of grades to help in copying
        //     over existing grades, then reset numGrades
        
        int prevNumGrades = this.numGrades;
        this.numGrades = newNumGrades;
 
        // create an array to eventually become the new grades array
        
        double newGrades[] = new double[this.numGrades];
            
        // if the new number of grades is LESS than before,
        //     only copy over that many grades from the previous set
        //     ("throwing away" the excess)
            
        if (this.numGrades < prevNumGrades)
        {
            for (int i=0; i < this.numGrades; i++)
            {
                newGrades[i] = grades[i];
            }
        }
            
        // otherwise, copy over the existing grades, and initialize
        //    the remaining grades to be 0s
        else
        {
            int i;
            
            // copy over all existing grades
                
            for (i=0; i < prevNumGrades; i++)
            {
                newGrades[i] = grades[i];
            }
                
            // initialize remaining grades to 0s
                
            for (i=prevNumGrades; i < this.numGrades; i++)
            {
                newGrades[i] = 0;
            }
        }
            
        // now it should be "safe" to set grades to reference
        //    new grades array
            
        this.grades = newGrades;
    }
                
    /**
     * set the grade with the specified index to the specified grade
     *     (except make NO change if the index is not in-bounds)
     *
     * @param whichGrade the index of the grade to set
     * @param newGrade the new grade for grades[whichGrade]
     */
    
    public void setGrade(int whichGrade, double newGrade)
    {
        // only change this.grades element if whichGrade is in-bounds
        
        if ( (whichGrade >= 0) && (whichGrade < this.numGrades) )
        {
            this.grades[whichGrade] = newGrade;
        }
    }
            
    /**
     * return the average of the calling StudentGrades' grades;
     *     return -1 if its number of grades is 0
     *
     * @return the average of the calling instance's grades, unless
     *     its number of grades is 0, in which case it returns -1
     */
        
     public double avgGrade()
     {
         // return -1 if calling StudentGrades instance has NO grades
         
         if (this.numGrades == 0)
         {
             return -1;
         }
         
         // if reach here, ARE grades to average
         
         double sumGrades = 0;
         
         for (int i=0; i < this.numGrades; i++)
         {
             sumGrades += grades[i];
         }
         
         return sumGrades / this.numGrades;
     }
     
     /**
      * return true if the calling StudentGrades instance has an average grade
      *     higher than the given StudentGrades instance; return false
      *     otherwise
      * 
      * @return whether calling StudentGrades instance has a higher average grade
      *         than the given StudentGrades instance
      */
     
     public boolean hasHigherAvgThan(StudentGrades otherStudentGrades)
     {
         return (this.avgGrade() > otherStudentGrades.avgGrade() );
     }
}