// Archibald Haddock (123456789)
// COMP 202-A, Section 0 (Fall 2007)
// Instructor: Cuthbert Calculus
// Assignment 0, Question 0

/**
 * An instance of class <code>BikeRace</code> manages a race which involves
 * a certain number of <code>Cyclists</code> racing on a <code>RaceTrack</code>.
 */
public class BikeRace {
	// The user interface object to be notified of events. Informally implements
	// the observer pattern.
	private BikeRaceUI userInterface;

	/**
	 * Creates an instance of a <code>BikeRace</code>
	 * @param userInterface the object implementing the user interface which
	 *        will be notified of events which occur during the race.
	 */
	public BikeRace(BikeRaceUI userInterface) {
		this.userInterface = userInterface;
	}

	/* Methods not required by the assignment specification, also called
	   helper methods, MUST be private. */
	/* Turning this code fragment into a method actually improves the clarity of
	   the method in which this method is called. This SHOULD be done whenever
	   possible. */
	// Determines whether the race is over by checking the status of the
	// cyclists.
	private static boolean isRaceOver(Cyclist[] cyclists, RaceTrack track) {
		boolean raceOver;
		int i;

		// Assume that the race is over.
		i = 0;
		raceOver = true;
		/* The '{' which indicates the beginning of a loop body SHOULD be on the
		   same line as the 'while', 'for' or 'do' statement. */
		while ((i < cyclists.length) && (raceOver)) {
			// If a cyclist is not done, then we know the race is NOT over.
			if (!cyclists[i].isDone(track)) {
				raceOver = false;
			} else {
				i++;
			}
		/* Loop body ends. Indentation level decreases by 1 so that the '}' has
		   the same indentation level as the 'if' statement or the 'else'
		   clause. */
		}
		return raceOver;
	}

	/**
	 * Main worker method which determines what happens during the race.
	 * @param cyclists the <code>Cyclist</code>s which take part in the race
	 * @param track the <code>RaceTrack</code> on which the race takes place
	 */
	public void runRace(Cyclist[] cyclists, RaceTrack track) {
		double time;
		double minTime;
		int minPosition;

		// Reset the status of all cyclists.
		/* A loop index variable declared in the body of a loop. This is
		   certainly acceptable, but not required. */
		for (int i = 0; i < cyclists.length; i++) {
			cyclists[i].reset();
		}

		// While the race is not over, check if each cyclist is alive. If a
		// cyclist is alive, have him attempt the next lap. Notify the user
		// interface of the status of each cyclist.
		while (!isRaceOver(cyclists, track)) {
			for (int i = 0; i < cyclists.length; i++) {
				if (!cyclists[i].hasFinishedRace(track)) {
					if (!cyclists[i].isAlive()) {
						this.userInterface.cyclistIsDead(cyclists[i]);
					} else {
						time = cyclists[i].raceLap(track);
						if (!cyclists[i].isAlive()) {
							this.userInterface.cyclistDies(cyclists[i]);
						} else {
							if (cyclists[i].isResting()) {
								this.userInterface.cyclistRests(cyclists[i],
										track, time);
							} else {
								this.userInterface.cyclistCompletesLap(
										cyclists[i], track, time);
							}
						}
					}
				}
			}
		}

		// Determine the cyclist who completed the race in the least amount of
		// time, if any completed the race.
		minTime = Double.MAX_VALUE;
		minPosition = -1;
		for (int i = 0; i < cyclists.length; i++) {
			time = cyclists[i].getTimeSoFar();
			if (cyclists[i].isAlive() && (time < minTime)) {
				minTime = time;
				minPosition = i;
			}
		}

		// Notify the user interface of the results of the race.
		for (int i = 0; i < cyclists.length; i++) {
			if (i == minPosition) {
				this.userInterface.displayWinner(cyclists[i], track);
			} else {
				if (!cyclists[i].isAlive()) {
					this.userInterface.displayDead(cyclists[i], track);
				} else {
					this.userInterface.displayLoser(cyclists[i], track);
				}
			}
		}
	}
}
