/*
 * Assignment3: the main class for Assignment 3, CSC108, Summer 2001.
 *
 * This program is used to model a system that keeps track of
 * information for a triathalon.  The triathalon has a number of
 * different participant categories, which are specified by the user
 * when the program is run.  The events in the triathalon are swimming,
 * cycling, and running.
 *
 * When the program starts, the user is instructed to enter the
 * triathalon's name and the number of categories in the triathalon.
 * Then a menu appears on the screen.  For the remainder of this
 * program, the user continuously selects commands from the menu.
 *
 * The user must start by using the menu option to enter a category.
 * The user specifies the name of the category.  Note that athletes
 * competing in the first category entered will later be assigned a
 * competitor number that ends with "0", athletes in the second
 * category a number that ends with "1", etc.
 *
 * Next the user should use the menu option to register athletes
 * that will compete in the triathalon.  The menu option registers
 * any number of athletes in a specified category.  This registration
 * may often take place well in advance of the day of the triathalon.
 * If on the day of the triathalon a particular athlete does not show
 * up to compete, there is also a menu option that can be used to
 * remove an athlete from a particular category.  Once all
 * registration information has been finalized, we are ready for
 * the actual triathalon to begin.
 *
 * The user of the program will next enter the menu command that records
 * how long it took each athlete to complete each event. The first time
 * an event time is recorded for a given athlete, it will be considered
 * to be the athlete's time for the "swim" event.  The next time an
 * event time is added for an athlete, it will be the "bike" event time,
 * and finally the next time an event time is entered for an athlete,
 * it will be the "run" time.  The event times that are entered are
 * cumulative, and are entered in the order that they occur.
 *
 * Note that once at least one event time has been recorded for an
 * athlete in a particular category, the triathalon is assumed to have
 * started, and at this point no new athlete registrations or deletions
 * are allowed in this category.
 *
 * Once all event times have been recorded for all athletes, the
 * user of the program can use the menu options to report summaries of
 * the data that has been collected.  The overall results can be
 * displayed, a summary for one category in the race, or a summary
 * for an individual athlete in the race.  "NA" (not available) is
 * printed if an athlete did not finish an event.
 *
 * This program is that it assumes all athlete names are unique.  If
 * two athletes have the same name, you'll need to use the middle initial
 * for one of the athletes.  This program does not allow you to enter
 * duplicate athlete names.
 *
 * This program handles most errors but it does not handle errors
 * that involve entering the wrong type of data (i.e., user enters a
 * String when program was expecting an integer).  You do not need to
 * test errors of this nature.
 *
 * This class currently reads input from the keyboard and displays
 * output on the screen.  Once you are sure your program is working,
 * and you are ready to create your output file, you should modify this
 * class to use file input and output, in the same way that you did
 * for assignment 1 and 2.
 *
 * -- Programmed by Andria Hunter, July 2001
 */

import java.io.*;
public class Assignment3 {

	// Declare variables for reading input and writing output.
	private static BufferedReader in;
	private static PrintStream out;

	// Declare the reference to the triathalon object.  All information
	// about the triathalon can be obtained through this reference.
	private static Triathalon triathalon;

	// Constants representing various menu options.
	private final static int ADD_A_CATEGORY = 1;
	private final static int REGISTER_ATHLETES = 2;
	private final static int DELETE_ATHLETES = 3;
	private final static int ADD_EVENT_TIMES = 4;
	private final static int REPORT_ALL = 5;
	private final static int REPORT_CATEGORY = 6;
	private final static int REPORT_ATHLETE = 7;
	private final static String STOPPER = "quit";

	// Indicates end of data for a list of numbers.
	private final static String END_OF_DATA = "stop";

	// Constants representing limits of input values allowed.
	private final static int MIN_CATEGORIES = 1;
	private final static int MAX_CATEGORIES = 10;
	private final static int MIN_ATHLETES = 1;

	/* main: The main method is the entry point into the program.  This
	 *       method maintains the 'triathalon' reference to the Triathalon
	 *       object.  All updates of triathalon information are done
	 *       through this reference.
	 *
	 *       This method provides the user interface of the program.
	 *       It repetitively displays a menu and reads commands from the
	 *       user and processes them.
	 */
	public static void main (String[] args) throws IOException {

		// Input is from the keyboard.
		in = new BufferedReader (new InputStreamReader (System.in));

		// Uncomment this statement and comment out the statement
		// above it, to have your input read from a file named
		// "input1.txt" instead.
		//in = new BufferedReader (new FileReader ("input1.txt"));

		// Uncomment these two statements to send the output
		// of the program to a file called "output1.txt" instead.
		//out = new PrintStream (new FileOutputStream
		//  (new File ("output1.txt")));
		//System.setOut (out);

		// The user must enter the name of the triathalon and the number
		// of categories, in order to initialize our Triathalon object.
		System.out.println ("Triathalon Program");
		System.out.println ("------------------\n");
		System.out.print ("Name of the triathalon? ");
		String triathalonName = in.readLine();

		// Read in number of categories until valid value entered.
		int numCategories;
		while (true) {
			System.out.print ("How many categories in the triathalon? ");
			numCategories = Integer.parseInt(in.readLine());

			if (numCategories >= MIN_CATEGORIES &&
				numCategories <= MAX_CATEGORIES) {
				break;
			}
			System.out.println ("Must have between " + MIN_CATEGORIES +
				" and " + MAX_CATEGORIES + " categories.  Please try again.");
		}

		triathalon = new Triathalon (triathalonName, numCategories);

		// Display the program menu for the user.
		printMenu();

		// Read the user's first menu command.
		String choice = in.readLine();
		System.out.println();

		// This loop continues until the user enters the stopping
		// value for the program.  One iteration of this loop
		// processes a single command from the menu, and then
		// reads in the next command.
		while (!choice.equals(STOPPER)) {

			// Add a new category to the triathalon
			if (Integer.parseInt(choice) == ADD_A_CATEGORY) {
				addACategory();

			// Register athletes in a category
			} else if (Integer.parseInt(choice) == REGISTER_ATHLETES) {
				registerAthletes();

			// Delete athletes
			} else if (Integer.parseInt(choice) == DELETE_ATHLETES) {
				deleteAthletes();

			// Add event times for athletes in a category
			} else if (Integer.parseInt(choice) == ADD_EVENT_TIMES) {
				addEventTimes();

			// Report information for entire triathalon.
			} else if (Integer.parseInt(choice) == REPORT_ALL) {
				reportAll();

			// Report for one category.
			} else if (Integer.parseInt(choice) == REPORT_CATEGORY) {
				reportCategory();

			// Report for one athlete.
			} else if (Integer.parseInt(choice) == REPORT_ATHLETE) {
				reportAthlete();

			// Command must have been invalid; inform user.
			} else {
				System.out.println ("Invalid menu option.  Try again.");
			}

			// Display menu again, and read selection from user.
			printMenu();
			choice = in.readLine();
			System.out.println();
		}
	}

	/*
	 * printMenu: Display the help menu, so that the user
	 *    knows which commands are available.
	 */
	private static void printMenu() {
		System.out.println ("\nTriathalon Program Menu");
		System.out.println ("   " + ADD_A_CATEGORY +
			": Add a category");
		System.out.println ("   " + REGISTER_ATHLETES +
			": Register athletes in a category");
		System.out.println ("   " + DELETE_ATHLETES +
			": Delete athletes");
		System.out.println ("   " + ADD_EVENT_TIMES +
			": Add event times for athletes in a category");
		System.out.println ("   " + REPORT_ALL +
			": Report all triathalon information");
		System.out.println ("   " + REPORT_CATEGORY +
			": Report for one category");
		System.out.println ("   " + REPORT_ATHLETE +
			": Report for one athlete");
		System.out.print ("Menu number or \"quit\" to quit: ");
	}

	/*
	 * addACategory: This method carries out the menu option ADD_A_CATEGORY.
	 *    The user is prompted to enter information that will be
	 *    used to add a new category.
	 */
	private static void addACategory() throws IOException {
		System.out.println ("Adding a Category");
		String categoryName;

		// Read in category name until user enters a category name
		// that has not already been added to the triathalon.
		while (true) {
			System.out.print ("Category name? ");
			categoryName = in.readLine();

			// Name does not already exist
			if (triathalon.findCategory(categoryName) == null) {
				break;
			}

			System.out.println ("Category already exists.");
		}

		// Create a Category object, and add it to the triathalon.
		Category newCategory = new Category (categoryName);
		if (!triathalon.addCategory(newCategory)) {
			System.out.println ("No space to add this category.");
		}
	}

	/*
	 * registerAthletes: This method carries out the menu option for
	 *    REGISTER_ATHLETES.  The user is asked for the category, and
	 *    then athletes are added to this category.
	 */
	private static void registerAthletes() throws IOException {
		System.out.println ("Registering athletes");

		// Cannot register athletes if no categories added yet.
		if (triathalon.getCategoryCount() == 0) {
			System.out.println ("Must add at least one category " +
				"first.  Try again.");
			return;
		}

		// Read in category name from the user, and retrieve the
		// corresponding Category object. Verify that a category
		// with this name exists.
		System.out.print ("Which category are the athletes in? ");
		String categoryName = in.readLine();
		Category category = triathalon.findCategory(categoryName);

		if (category == null) {
			System.out.println ("Invalid category name.  Try again.");
			return;
		}

		// Cannot register athletes in a category if competition
		// has already begun.
		if (category.hasStarted()) {
			System.out.println ("The triathalon has already started." +
				"  Command ignored.");
			return;
		}

		System.out.println ("Enter athletes for the '" + categoryName
			+ "' category.   Type '" + END_OF_DATA + "' when finished.");

		// Read in each athlete's name, until END_OF_DATA is entered
		// for the athlete name.
		int numberAdded = 0;		// Count number of athletes added.
		while (true) {

			System.out.print ("Athlete Name? ");
			String athleteName = in.readLine();

			// Stop entering athlete names if END_OF_DATA entered.
			if (athleteName.equals(END_OF_DATA)) {
				break;

			// When registering athletes, you cannot enter duplicates.
			} else if (triathalon.findAthlete(athleteName) != null) {
				System.out.println ("This athlete was already " +
					"registered in the triathalon.  Try again.");

			// Go ahead and register this athlete.
			} else {

				// Generate an athlete number for this athlete.
				int athleteNum = category.generateNumber();

				// Create athlete object and add to appropriate category.
				Athlete newAthlete = new Athlete (athleteName, athleteNum);
				category.addAthlete (newAthlete);

				System.out.println (athleteName + " registered with" +
					" athlete number " + athleteNum + ".");
				numberAdded++;
			}
		}

		System.out.println ("Added " + numberAdded +
			" athlete(s) to category " + categoryName + ".");
	}

	/*
	 * deleteAthletes: This method carries out the menu option for
	 *    DELETE_ATHLETES.  The athlete number is entered, and then
	 *    this athlete is removed from the triathalon.
	 */
	private static void deleteAthletes() throws IOException {
		System.out.println ("Deleting athletes");

		// Cannot delete athletes if no categories added yet.
		if (triathalon.getCategoryCount() == 0) {
			System.out.println ("Must add at least one category " +
				"first.  Try again.");
			return;
		}

		System.out.println ("Enter athlete numbers.  Type '" +
			END_OF_DATA + "' when finished.");

		// Read in each athlete's number, until END_OF_DATA is entered
		// for the athlete's number.
		int numberDeleted = 0;		// Count number of athletes deleted.
		while (true) {

			System.out.print ("Number of the athlete to delete? ");
			String athleteNumStr = in.readLine();

			// Stop entering athlete numbers if END_OF_DATA entered.
			if (athleteNumStr.equals(END_OF_DATA)) {
				break;
			}

			// Determine this athlete's category.
			int athleteNum = Integer.parseInt(athleteNumStr);
			Category category = triathalon.findCategory(athleteNum);

			// Check for invalid athlete number.
			if (category == null) {
				System.out.println ("Invalid athlete number. " +
					"Try again.");

			// Cannot delete athletes in a category if competition has
			// already begun.
			} else if (category.hasStarted()) {
				System.out.println ("Competition has already " +
					"started in the " + category.getName() +
					" category.  Command ignored.");

			// Go ahead and delete this athlete.
			} else {

				// Retrieve a reference to the athlete in this
				// category that has this athlete number.
				Athlete athlete = category.findAthlete(athleteNum);

				if (athlete == null) {
					System.out.println ("This athlete has not been " +
					"registered in the " + category.getName() +
					" category.  Try again.");

				} else {
					category.deleteAthlete(athleteNum);
					System.out.println ("Athlete " + athleteNum +
							" deleted.");
					numberDeleted++;
				}
			}
		}

		System.out.println ("Deleted " + numberDeleted + " athlete(s).");
	}

	/*
	 * addEventTimes: This method carries out the menu option
	 *    ADD_EVENT_TIMES.  The user is asked which category the event
	 *    times will be added to, and then the user enters the athlete
	 *    numbers and event times to be added to this category.
	 */
	private static void addEventTimes() throws IOException {
		System.out.println ("Adding event times for athletes");

		// Cannot add event times if no categories added yet.
		if (triathalon.getCategoryCount() == 0) {
			System.out.println ("Must add at least one category " +
				"first.  Try again.");
			return;
		}

		// Read in category name from user, and retrieve corresponding
		// Category object. Verify that a category with this name exists.
		System.out.print ("In which category are you adding athlete " +
			"event times? ");
		String categoryName = in.readLine();
		Category category = triathalon.findCategory(categoryName);

		if (category == null) {
			System.out.println ("Invalid category name.  Try again.");

		} else {

			System.out.println ("Enter athlete numbers and their event " +
				"times (in seconds).");
			System.out.println ("Type '" + END_OF_DATA + "' for athlete"
				+ " number when finished.");

			// Read in each athlete's number, until END_OF_DATA is entered
			// for the athlete number.  Retrieve this athlete from the
			// triathalon and update the specified event time.
			int numberAdded = 0;		// Count number of event times added.
			while (true) {

				System.out.print ("Athlete Number? ");
				String athleteNumStr = in.readLine();

				// Stop adding times if END_OF_DATA entered.
				if (athleteNumStr.equals(END_OF_DATA)) {
					break;
				}

				// Retrieve a reference to the Athlete object in this
				// category that has the athlete number entered.
				int athleteNum = Integer.parseInt(athleteNumStr);
				Athlete athlete = category.findAthlete(athleteNum);

				// Athlete not registered in this category.
				if (athlete == null) {
					System.out.println ("This athlete has not been " +
						"registered in the " + categoryName + " category.");

				// Read the event time from the user, and as long as the
				// addEventTime method returns 'true', the event time is
				// added for this athlete.
				} else {
					System.out.print ("Event Time? ");
					int eventTime = Integer.parseInt(in.readLine());

					if (athlete.addEventTime (eventTime)) {
						numberAdded++;
					}
				}
			}

			System.out.println ("Added " + numberAdded + " event " +
				"time(s) to category " + categoryName + ".");
		}
	}

	/*
	 * reportAll: This method carries out the menu option REPORT_ALL.
	 *    It generates a report for the entire triathalon.
	 */
	private static void reportAll() {
		System.out.println ("Triathalon Report\n");
		triathalon.report();
	}

	/*
	 * reportCategory: This method carries out the menu option
	 *    REPORT_CATEGORY.  It reports for one category.
	 */
	private static void reportCategory() throws IOException {
		System.out.println ("Category Report");

		// Cannot generate category report if no categories added yet.
		if (triathalon.getCategoryCount() == 0) {
			System.out.println ("Must add at least one category " +
				"first.  Try again.");
			return;
		}

		// Retrieve appropriate category.
		System.out.print ("Which category? ");
		String categoryName = in.readLine();
		System.out.println();

		Category category = triathalon.findCategory(categoryName);

		if (category == null) {
			System.out.println ("Category does not exist.");

		} else {
			System.out.println ("\tCateg.\tAthlete\t #\tSwim\tBike\tRun\tOverall");
			category.report();
		}
	}

	/*
	 * reportAthlete: This method carries out the menu option REPORT_ATHLETE.
	 *    It generates a report for one athlete in the triathalon.
	 */
	private static void reportAthlete() throws IOException {
		System.out.println ("Athlete Report");

		// Cannot generate report for athlete if no categories added yet.
		if (triathalon.getCategoryCount() == 0) {
			System.out.println ("Must add at least one category " +
				"first.  Try again.");
			return;
		}

		System.out.print ("Athlete has which athlete number? ");
		int athleteNum = Integer.parseInt(in.readLine());
		System.out.println();

		// Retrieve appropriate category.
		Category category = triathalon.findCategory(athleteNum);

		if (category == null) {
			System.out.println ("Invalid athlete number.  Try again.");

		} else {

			// Retrieve appropriate athlete.
			Athlete athlete = category.findAthlete(athleteNum);

			if (athlete == null) {
				System.out.println ("This athlete was not registered.");

			} else {
				// Print report for this athlete.
				System.out.println
					("\tCateg.\tAthlete\t #\tSwim\tBike\tRun\tOverall");
				System.out.println ("\t" + category.getName() + "\t" +
							athlete.report());
			}
		}
	}
}

