import java.util.Arrays;

/**
 * A class to hold some very basic information about a student.
 *
 * @author Mark Young (A00000000)
 */
public class Student {

    // ---------- Instance variables (and constants) ------------------ //

    /** The student's A-Number */
    public final String A_NUMBER;
    /** The student's name */
    private String name;
    /** The student's grades (as percentages) */
    private int[] asgnGrades;


    // ---------- Class variables (and constants) --------------------- //

    /** The number of student objects so far */
    private static int numberOfStudents = 0;
    /** Number of assignments graded so far */
    private static int asgnsGraded = 0;

    /** Total number of assignments */
    public static final int NUM_ASGN = 8;
    /** Maximum possible percentage grade */
    public static final int MAX_GRADE = 100;
    /** Minimum possible percentage grade */
    public static final int MIN_GRADE = 0;


    // ---------- Constructors ---------------------------------------- //

    /**
     * Create a Student object.
     *
     * @param initialName      the requested name
     */
    public Student(String initialName) {
        // count this student we're creating
        ++numberOfStudents;

        // assign next A-Number to this Student
        A_NUMBER = String.format("A%08d", numberOfStudents);

        // set personal data
        name = initialName;

        // set academic data
        asgnGrades = new int[NUM_ASGN];
    }


    // ---------- Getters --------------------------------------------- //

    /**
     * Get the value of A_NUMBER
     *
     * @return the value of A_NUMBER
     */
    public String getANumber() {
        return A_NUMBER;
    }

    /**
     * Get the value of name
     *
     * @return the value of name
     */
    public String getName() {
        return this.name;
    }

    /**
     * Get the assignment grades
     *
     * @return the array of graded assignment scores
     */
    public int[] getAsgnGrades() {
        return Arrays.copyOf(asgnGrades, asgnsGraded);
    }

    /**
     * Get one assignment grade
     *
     * @return the grade for the given assignment, or zero if it doesn't exist
     */
    public int getAsgnGrade(int asgnNum) {
        // if it's a valid assignment number AND it's been released
        if (isValidAsgnNum(asgnNum) && asgnNum <= asgnsGraded) {
            return asgnGrades[asgnNum - 1];
        } else {
            return 0;
        }
    }

    /**
     * get letter grade for this student
     *
     * @return  the letter grade equivalent to this student's percentage grade
     */
    public String getLetterGrade() {
        return letterGradeFor(getPctGrade());
    }

    /**
     * Calculate and return this student's average assignment grade.
     *
     * @return the rounded average of this student's graded assignments
     *         or 0 if no assignments have been graded.
     */
    public int getPctGrade() {
        int sum = 0;
        for (int i = 0; i < asgnsGraded; ++i) {
            sum += asgnGrades[i];
        }
        if (asgnsGraded == 0) {
            return 0;
        } else {
            return (int)Math.round((double)sum / (double)asgnsGraded);
        }
    }

    /**
     * Get the student's grade on a single assignment.
     * Note: assignments are numbered starting at 1.
     *
     * @param asgnNum   the number of an assignment (1 .. NUM_ASGN)
     * @return  this student's grade on that assignment
     */
    public int getGrade(int asgnNum) {
        if (isValidAsgnNum(asgnNum)) {
            return asgnGrades[asgnNum - 1];
        }
        return 0;
    }


    // ---------- Setters --------------------------------------------- //

    /**
     * Set the value of name
     *
     * @param newName new value of name
     */
    public void setName(String newName) {
        this.name = newName;
    }

    /**
     * Set the assignment grades
     *
     * @param newGrades  array with suitable grades
     */
    public void setAsgnGrades(int[] newGrades) {
        if (areValidGrades(newGrades)) {
            asgnGrades = Arrays.copyOf(newGrades, NUM_ASGN);
        }
    }

    /**
     * Set one assignment's grade
     *
     * @param asgnNum   the number of the assignment (1-based)
     * @param newGrade  the grade for that assignment
     */
    public void setAsgnGrade(int asgnNum, int newGrade) {
        if (isValidAsgnNum(asgnNum) && isValidGrade(newGrade)) {
            asgnGrades[asgnNum - 1] = newGrade;
        }
    }


    // ---------- Other observer methods ------------------------------ //

    /**
     * Print a brief summary of the student's data.
     */
    public void printStudentRecord() {
        System.out.println("Number: " + this.A_NUMBER);
        System.out.println("Name:   " + this.name);
        System.out.println("Grade:  " + this.getPctGrade());
        System.out.println("Letter: " + this.getLetterGrade());
    }

    /**
     * Create a String to represent this Student.
     * This version uses their name and A-number.
     *
     * @return  a String representing this Student
     */
    @Override
    public String toString() {
        return this.name + " (" + this.A_NUMBER + ")";
    }


    // ---------- Class methods --------------------------------------- //

    /**
     * Return whether a given grade is in the valid range.
     *
     * @param g     the grade to check for validity
     * @return  whether g is in the correct range.
     */
    public static boolean isValidGrade(int g) {
        return (MIN_GRADE <= g && g <= MAX_GRADE);
    }

    /**
     * Return whether a list of grades is in the valid range.
     *
     * @param gg    the list of grades to check for validity
     * @return  whether gg is of a suitable length with all values
     *          in the correct range.
     */
    public static boolean areValidGrades(int[] gg) {
        if (gg.length > NUM_ASGN) {
            return false;
        }
        for (int i = 0; i < gg.length; ++i) {
            if (!isValidGrade(gg[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Return whether an assignment number is in the valid range
     *
     * @param asgnNum   the supposed assignment number
     * @return  whether asgnNum is in the range 1..NUM_ASGN
     */
    public static boolean isValidAsgnNum(int asgnNum) {
        return 1 <= asgnNum && asgnNum <= NUM_ASGN;
    }

    /**
     * Return the letter grade corresponding to the given grade.
     *
     * @param g     the percentage grade to be converted to a letter grade.
     * @return  the letter grade corresponding to g.
     */
    public static String letterGradeFor(int g) {
        if (g < 50) {
            return "F";
        } else if (g < 60) {
            return "D";
        } else if (g < 70) {
            return "C";
        } else if (g < 80) {
            return "B";
        } else {
            return "A";
        }
    }

    /**
     * Set the number of graded assignments.
     *
     * @param released  the number of the latest assignment released
     */
    public static void releaseAssignment(int released) {
        if (isValidAsgnNum(released)) {
            asgnsGraded = released;
        }
    }

}
