/**
 * GradeAnalyzerWithMenu.java
 *
 * Computer Science S-111
 * 
 * Demonstrates the use of an array to store and process a collection of data
 * -- specifically a collection of grades.
 *
 * This version of the program includes a menu that allows the user to 
 * choose which operations to perform, as well as support for some additional 
 * types of operations.
 */

import java.util.*;

public class GradeAnalyzerWithMenu {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);

        // Read in the grades.
        System.out.print("How many grades? ");
        int maxNumGrades = console.nextInt();
        int[] grades = new int[maxNumGrades];
        int numGrades = 0;
        while (numGrades < maxNumGrades) {
            System.out.print("Enter a grade (-1 if no more grades): ");
            int grade = console.nextInt();
            if (grade == -1) {
                break;
            }
            grades[numGrades] = grade;
            numGrades++;
        }
        
        processUserChoices(console, grades, numGrades);
    }
    
    /*
     * processUserChoices - repeatedly give the user a menu of choices
     * and process each choice that is entered using the specified Scanner.
     * The choices correspond to operations on the specified array of grades.
     * The method takes as its third parameter the number of grades in the array,
     * since we may not be using all of the positions of the array.
     */
    public static void processUserChoices(Scanner console, int[] grades, int numGrades) {        
        // Repeatedly process menu selections by the user.
        int choice;
        do {
            System.out.println();
            System.out.println("Grade analyzer menu:");
            System.out.println("(1) print the grades");
            System.out.println("(2) compute the average grade");
            System.out.println("(3) find the highest grade");
            System.out.println("(4) count the number of grades of each type");
            System.out.println("(5) add a grade");
            System.out.println("(6) see if a particular grade is present");
            System.out.println("(7) quit");
            choice = console.nextInt();
            
            if (choice == 1) {
                System.out.println(Arrays.toString(grades));
            } else if (choice == 2) {
                double average = averageGrade(grades, numGrades);
                System.out.println("average grade = " + average);
            } else if (choice == 3) {
                int max = maxGrade(grades, numGrades);
                System.out.println("max grade = " + max);
            } else if (choice == 4) {
                int max = maxGrade(grades, numGrades);
                int[] counts = getCounts(grades, numGrades, max);
                for (int i = 0; i < counts.length; i++) {
                    if (counts[i] > 0) {
                        System.out.println(counts[i] + " grade(s) of " + i);
                    }
                }
            } else if (choice == 5) {
                if (numGrades >= grades.length) {
                    System.out.println("There is no room for additional grades.");
                } else {
                    System.out.print("grade to add: ");
                    int grade = console.nextInt();
                    grades[numGrades] = grade;
                    numGrades++;
                }
            } else if (choice == 6) {
                System.out.print("grade to look for: ");
                int grade = console.nextInt();
                if (contains(grade, grades, numGrades)) {
                    System.out.println(grade + " is in the collection of grades.");
                } else {
                    System.out.println(grade + " is not in the collection of grades.");
                }
            }
        } while (choice != 7);
    }
    
    /*
     * averageGrade - compute and return the average grade in the specified
     * array of grades.
     * The method takes as its second parameter the number of grades in the array,
     * since we may not be using all of the positions of the array.
     */ 
    public static double averageGrade(int[] grades, int numGrades) {
        if (grades == null || grades.length == 0) {
            throw new IllegalArgumentException("grades array must have at least one element");
        }
        
        int total = 0;
        for (int i = 0; i < numGrades; i++) {
            total += grades[i];
        }

        return (double)total / numGrades;
    }
    
    /*
     * maxGrade - find and return the largest grade in the specified
     * array of grades.
     * The method takes as its second parameter the number of grades in the array,
     * since we may not be using all of the positions of the array.
     */
    public static int maxGrade(int[] grades, int numGrades) {
        if (grades == null || grades.length == 0) {
            throw new IllegalArgumentException("grades array must have at least one element");
        }
        
        int max = grades[0];
        for (int i = 1; i < numGrades; i++) {
            if (grades[i] > max) {
                max = grades[i];
            }
        }

        return max;
    }
    
    /*
     * getCounts - create and return an array that contains counts of the
     * number of times that each grade appears in the specified grades array.
     * The method takes as its second parameter the number of grades in the array,
     * since we may not be using all of the positions of the array.
     * The method takes as its third parameter the largest grade in the array,
     * so that it will know how big to make the counts array.
     */
    public static int[] getCounts(int[] grades, int numGrades, int maxGrade) {
        if (grades == null || grades.length == 0) {
            throw new IllegalArgumentException("grades array must have at least one element");
        }
        
        int[] counts = new int[maxGrade + 1];
        
        for (int i = 0; i < numGrades; i++) {
            counts[grades[i]]++;
        }

        return counts;
    }
    
    /*
     * contains - looks for the specified grade in the specified grades array, 
     * returning true if the grade is in the array and false otherwise.
     * The method takes as its third parameter the number of grades in the array,
     * since we may not be using all of the positions of the array.
     */
    public static boolean contains(int grade, int[] grades, int numGrades) {
        for (int i = 0; i < numGrades; i++) {
            // If we find it, we return true.
            if (grades[i] == grade) {
                return true;
            }
        }
    
        // If we make it here, it must be the case that the grade is not
        // in the array, since otherwise we would have already returned true.
        return false;
    }
}