import wheels.users.*;
import java.awt.Color;
import java.awt.event.MouseEvent;
import java.util.Vector;

/**
 * Poorly simulates the old hand-held version of Simon-Says.
 *
 * @author Zach Tomaszewski
 * @version 16 Nov 07
 */
public class SimonSays {

  /*
   * Warning: Though it works, this game is still pretty rough.
   * It's certainly not release ready, and I'm a little uncertain about
   * my thread use.
   *
   * In other words, don't look to this code as a good example to
   * be emulated!
   */

  /** How long (in milliseconds) a button stays hightlighted when pressed */
  public static final int FLASH_LENGTH = 500;

  protected Frame window;
  protected Button red;
  protected Button green;
  protected Button blue;
  protected Button yellow;

  protected boolean locked = false;  //whether the interface should be frozen

  //holds the sequence to match
  protected Vector<Button> sequence = new Vector<Button>();
  //holds the user's sequence of guesses
  protected Vector<Button> guesses = new Vector<Button>();


  /**
   * Builds a SimonSays interface with four buttons
   * (red, green, blue, yellow) to click.
   */
  public SimonSays() {
    window = new Frame();

    //calculate dimensions of buttons
    int bWidth = Frame._dp.getWidth() / 2;
    int bHeight = Frame._dp.getHeight() / 2;

    //create buttons
    red = new Button("RED", 0, 0, bWidth, bHeight,
                       Color.red, new Color(100, 0, 0));
    green = new Button("GREEN", bWidth, 0, bWidth, bHeight,
                       Color.green, new Color(0, 100, 0));
    blue = new Button("BLUE", 0, bHeight, bWidth, bHeight,
                       Color.blue, new Color(0, 0, 100));
    yellow = new Button("YELLOW", bWidth, bHeight, bWidth, bHeight,
                       Color.yellow, new Color(100, 100, 0));
  }


  /**
   * Returns one of this SimonSays' four buttons at random.
   *
   * @return a random button from this SimonSays
   */
  public Button getRandomButton() {
    int random = (int) (Math.random() * 4);
    switch (random) {
      case 0: return this.red;
      case 1: return this.blue;
      case 2: return this.green;
      default: return this.yellow;
    }
  }

  /**
   * Visually animates the current sequence of random buttons.
   */
  public void playSequence() {
    for (Button button : sequence) {
      //use do-while since want at least a little delay after each button
      do {
        //delay until the UI is free again
        try {
          Thread.sleep(200);
        }catch (InterruptedException ie){
          //interrupted, which shouldn't happen
          System.err.println("PlaySquence thread interrupted.");
        }
      } while (locked);
      //UI finally free, so highlight next button
      button.highlight();
    }
  }

  /**
   * Runs a game of SimonSays.
   */
  public void run() {
    boolean userCorrect = true;

    //while user continues to guess right, continue extending sequence
    while (userCorrect) {

      //add next button to sequence and play
      Button next = this.getRandomButton();
      this.sequence.add(next);
      this.playSequence();

      //now let user guess at the sequence
      while (guesses.size() < sequence.size()) {
        try {
          //pause periodically to take it easy on CPU
          Thread.sleep(300);
        }catch (InterruptedException ie){
          //interrupted, which shouldn't happen
          System.err.println("Delay thread interrupted.");
        }

        //compare last guess
        int i = guesses.size() - 1; //index of last guess
        if (i >= 0 && sequence.get(i) != guesses.get(i)) {
          userCorrect = false;
          System.out.println("Incorrect!");
          System.out.println("Sequence: " + sequence);
          System.out.println("Guesses:  " + guesses);
          break;
        }
      }
      //clear guesses for next sequence
      guesses.clear();
    }

  }


  /**
   * Creates an instance of Simon Says for the player to enjoy.
   */
  public static void main(String[] args) {
    SimonSays game = new SimonSays();
    game.run();
  }



  /**
   * A clickable button that changes color from on-to-off or
   * off-to-on each time it is clicked.
   */
  protected class Button extends Rectangle {

    private Color onColor;
    private Color offColor;
    private String name;

    /**
     * Creates a new button with its upper-left corner
     * positioned at (xPos, yPos).  The button has the given
     * width and height (in pixels), and it changes between
     * onColor and offColor when clicked.  Its name is used
     * in toString() output.
     * <p>
     * A new button starts in the offColor.
     */
    public Button(String name, int xPos, int yPos, int width, int height,
                  Color onColor, Color offColor) {
      super();
      this.setLocation(xPos, yPos);
      this.setSize(width, height);

      this.name = name;
      this.onColor = onColor;
      this.offColor = offColor;
      this.setColor(offColor);
    }

    /**
     * When pressed, a Button will highlight itself and add itself to
     * the sequence of user guesses.
     * Overrides Recantangle's method to receive clicks.
     */
    public void mousePressed(MouseEvent me) {
      if (!locked) {  //ignore new clicks if UI is locked
        guesses.add(this); //add this button to user guesses
        this.highlight();
      }
    }

    /**
     * This button toggles its color for FLASH_LENGTH
     * milliseconds, and then reverts to its current state.
     */
    public void highlight() {
      this.toggle();
      //delay using a separate thread, or else above toggle never completes
      new HighlightDelay(this).start();
    }

    /**
     * Returns whether this button is currently showing
     * its on color (rather than its off color).
     */
    public boolean isOn() {
      return (this.getColor().equals(onColor));
    }

    /**
     * If this Button is on, it switches off; if off, it turns on.
     */
    public void toggle() {
      if (this.isOn()){
        this.setColor(offColor);
      }else {
        this.setColor(onColor);
      }
    }

    /**
     * Returns this button's name.
     */
    public String toString() {
      return this.name;
    }


    /**
     * A separate delay thread so that the animation thread
     * is not suspended during a button flash.  Will toggle
     * the given button again when it's done delaying.
     */
    private class HighlightDelay extends Thread {

      private Button button;

      /**
       * Takes the button to toggle at the end of this delay.
       */
      public HighlightDelay(Button b) {
        this.button = b;
      }

      /**
       * Locks the SimonSays user interface, delays FLASH_LENGTH,
       * and then unlocks the interface again and toggles the button
       * (which usually means turning it off).
       */
      public void run() {
        locked = true;
        try {
          Thread.sleep(FLASH_LENGTH);
        }catch (InterruptedException ie) {
          //delaying interrupted, which shouldn't happen
          System.err.println("HighlightDelay thread interrupted.");
        }
        locked = false;
        button.toggle();
      }

    }//end HighlightDelay

  }//end Button

}//end SimonSays
