// Program: Brian's Superbly Incredible, Amazingly Functional Clock
// Author: Brian Kell
// Date: September/October 2001

import aLibrary.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
import java.util.*;
import java.io.Serializable;
// This is my pile of import statements.

public class Director extends AWindow implements Serializable, Runnable {
// Our Director class extends the IncredibleHandler class, which is needed to
// make the buttons work.

  /*
   * CLASS CONSTANTS
   */
  final static int numClocks = 3;  // how many different clocks we have

  /*
   * CLASS VARIABLES
   */

  AButton toggleButton;  // our button to switch between the various clocks
  Clock[] myRolex = new Clock[3];  // array of our different clocks
  Color[] myCrayons = {Color.black, Color.blue, Color.cyan, Color.darkGray,
    Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange,
    Color.pink, Color.red, Color.yellow};  // for a random color (later)
  int whichClock = 0;  // which clock is currently displayed
  Timer tickTock;  // executes run(). This is a java.util.Timer
  IncredibleTask toDo;  // we need this to overcome Java's limitations

  /*
   * CONSTRUCTOR
   */

  public Director() {
  // This constructor takes no arguments and constructs a Director object.
  //
  // Preconditions: none.
  //
  // Postconditions: a new IncredibleWindow is instantiated and assigned to
  // myYard. The title of this window is set to "Brian's Superbly Incredible,
  // Amazingly Functional Clock".

    super(100, 100, 600, 400);
    // AWindow constructor

    setTitle("Brian's Superbly Incredible, Amazingly Functional Clock");
    // Set the title of the window

    toggleButton = new AButton(200, 350, 200, 30);
    toggleButton.addActionListener(this);
    toggleButton.place(this);
    // Place our button and set it up

    // Set up our clocks:
    myRolex[0] = new AnalogClock(150, 25, 300, 300,        // Analog clock, in
      myCrayons[(int)(Math.random() * myCrayons.length)]);  // a random color.
    myRolex[1] = new DigitalClock(150, 100, 300, 150);  // Digital clock.
    myRolex[2] = new UpcClock(25, 25, 550, 300);  // A way cool UPC clock.

    // Describe our clocks:
    myRolex[0].setDescription("Make me analog!");
    myRolex[1].setDescription("Make me digital!");
    myRolex[2].setDescription("Make 7 UPC Yours!");

    myRolex[0].place(this);
    // Place the analog clock in the window

    updateCurrentClock();
    // Update the current clock to show the correct time.

    changeToggleText();
    // Change the text on the toggle button.

    repaint();
    // Repaint the window.

    tickTock = new Timer(true);
    // The true means that we want this Timer to run as a daemon thread, which
    // means that it will not prolong the lifetime of the application.

    toDo = new IncredibleTask(this);
    // This is the task that we want to be performed whenever the Timer says
    // to. This task then calls the run() method of this class, Director. If
    // this method of doing things seems overly convoluted, blame Java. It was
    // the only way I could think of getting this to work.

    tickTock.schedule(toDo, 0, 100);
    // Execute the run() method every 100 ms (1/10 s).

  }

  /*
   * UPDATE METHODS
   */

  public void run() {
  // Defines a method that can be executed. We need this method because our
  // Director class implements Runnable. This will come in handy when we have
  // to pass this Director object to an IncredibleTask in order to get the
  // Timer to like it--because java.util.Timer requires a TimerTask, which
  // means that I need to extend TimerTask (an abstract class) in order to do
  // anything with java.util.Timer, but Director is already extending AWindow!
  // Since Java really craps out when it comes to scenarios like this, I have
  // to use an interface (Runnable happens to come in handy here), and then
  // define a WHOLE NEW CLASS called IncredibleTask just to get around Java's
  // stupid insistence on single inheritance.
  //
  // Preconditions:
  //   none.
  //
  // Postconditions:
  //   The current clock is updated and this window is repainted.

    updateCurrentClock();

    repaint();

  }

  public void toggleAction() {
  // This method is called whenever the toggle button in the upper part of the
  // window is clicked. It accepts no arguments and returns no value.
  //
  // Preconditions:
  //   none.
  //
  // Postconditions:
  //   myRolex is changed to the next type of clock.
  //   The text on the toggle button is changed.
  //   The window is redrawn.

    myRolex[whichClock].remove();
    // We must first remove this clock from myYard, since we don't want it to
    // be there anymore.

    if (++whichClock >= numClocks) {
      whichClock = 0;
      myRolex[whichClock].setColor(
        myCrayons[(int)(Math.random() * myCrayons.length)]);
    }
    // So we don't get an out of bounds error (and also so we can change the
    // color of the analog clock)s

    myRolex[whichClock].place(this);
    changeToggleText();
    updateCurrentClock();
    // We will now perform these three instructions so that our changes will
    // show up on screen.

    repaint();

  }

  private void changeToggleText() {
  // Changes the text on the toggle button.
  //
  // Preconditions:
  //   none.
  //
  // Postcondition:
  //   The text on the toggle button is updated to reflect the current state
  //     of affairs going on in our window, and what will happen should the
  //     user click the toggle button at this point.

    toggleButton.setText(
      myRolex[(whichClock+1 < numClocks) ? whichClock+1 : 0].getDescription());

  }

  public void actionPerformed(ActionEvent e)  {
  // Figure out which button was clicked and do something different
  // depending on what it was.
    if (e.getSource() instanceof MenuItem)
            System.exit(0);
    else if (e.getSource() == toggleButton)
            toggleAction();
  }

  private void updateCurrentClock() {
  // Update whatever clock is currently on the screen.
  //
  // Preconditions:
  //   none.
  //
  // Postconditions:
  //   The clock currently onscreen is updated to show the current time.

      myRolex[whichClock].showTime(Timekeeper.currentMillis() / 1000d);

  }

}