import java.applet.*;
import java.awt.*;
import java.util.*;
import java.net.*;

/** Demo is a simple applet demonstrating the utility of the Sprite
    and PlayField classes.  It detects mouse clicks and alternately
    creates new spiralling digits or cycles the color of the digit the
    user clicked.
    requires: presence of supporting classes and image files in
              directory applet is served from.
*/
public class Demo extends Applet implements Runnable
{
  PlayField playground;
  Raster bufferRaster;
  Image bufferImg;
  Image[] numbers;
  
  Thread animate;
  
  int currentNum;
  
  /** Applet initializer.  
      effects: Creates PlayField playground,
      setting its background, bufferRaster to draw into,
      loads images into numbers[], creates a
      ColorCyclingFollower sprite, adding it to playground,
      and creates the animation thread.  
  */  
  public void init() {
    bufferImg = createImage(500, 500);
    playground = new PlayField(500, 500);
    playground.fill(new Color(0x00000066));
    bufferRaster = new Raster(500, 500);
    try {
      bufferRaster.pixel = (int[]) playground.pixel.clone();
      // for compatibility between SGI and SUN compilers...
      if (false) throw new CloneNotSupportedException();
    } catch (CloneNotSupportedException e) {
      for (int i=0; i< bufferRaster.pixel.length; i++) {
	bufferRaster.pixel[i] = playground.pixel[i];
      }
    }
    bufferImg.getGraphics().drawImage
      (bufferRaster.toImage(), 0, 0, this);
    
    numbers = new Image[10];
    getNumbers();
    
    currentNum = 0;
    
    Follower f;
    
    f = new ColorCyclingFollower("Number:" + currentNum,
                               numbers[currentNum], 
			       new SpiralPath( 50, 50, 30., -.005));
    
    // f = new RotatingFollower("Number:" + currentNum,
    //			     numbers[currentNum],
    //			     new SpiralPath( 200, 200, 30., -.005),
    //			     Math.PI/100);

    playground.addSprite(f);
    
    animate = new Thread(this, "Animator");
  }
  
  
  /** Loads the required images into numbers[].
      requires: The image files exist and are readable.
    */ 
  private void getNumbers() {
    try {
      numbers[0] = getImage(new URL(getCodeBase(), "zero.gif"));
      numbers[1] = getImage(new URL(getCodeBase(), "one.gif"));
      numbers[2] = getImage(new URL(getCodeBase(), "two.gif"));
      numbers[3] = getImage(new URL(getCodeBase(), "three.gif"));
      numbers[4] = getImage(new URL(getCodeBase(), "four.gif"));
      numbers[5] = getImage(new URL(getCodeBase(), "five.gif"));
      numbers[6] = getImage(new URL(getCodeBase(), "six.gif"));
      numbers[7] = getImage(new URL(getCodeBase(), "seven.gif"));
      numbers[8] = getImage(new URL(getCodeBase(), "eight.gif"));
      numbers[9] = getImage(new URL(getCodeBase(), "nine.gif"));
      
    } catch (MalformedURLException e) {
      System.out.println("Bad Url " + e);
    }
    
  }

  /** Renders the bufferRaster to bufferImg.
      This is necessary for double buffering.  Demo draws the
      bufferRaster to an offscreen image, which is then displayed on
      the screen by the paint command in a seperate thread.
    */
  private synchronized void render() {
    bufferImg = bufferRaster.toImage();
    repaint();
  }    

  /** Component update method.
      overriding default update method to eliminate screen
      clearing (reduces flicker)
  */
  public void update(Graphics g) {
    paint(g);
  }

  /** Component paint method.
      requires: bufferImg is a proper Image object
      effects: draws bufferImg to the current Graphics context.
  */
  public void paint(Graphics g) {
    // System.out.println("paint(g) called");
    g.drawImage(bufferImg, 0, 0, this);
    // g.drawImage(oneImg, drawNumX, 10, this);
  }

  /** Applet start method.
      Starts the animate thread, or resumes it if it had been
      previously interrupted (presumably by the stop() method.
  */
  public void start() {
    if (!animate.isAlive()) {
      animate.start();
    } else {
      animate.resume();
    }
  }

  /** Applet stop method.
      Interrupts the animate thread.
  */
  public void stop() {
    animate.interrupt();
  }

  /** Thread run method.
      Causes a "tick" (essentially a discrete time step) to occur on
      the playground every fifty milliseconds and rerenders the
      bufferRaster afterwards.  Also performs a garbage collection
      every ten ticks (may slow performance, but keeps memory freed up
      on faster machines) 
  */
  public void run() {
    int ticks = 0; 
    while (true) {
      try {
	animate.sleep(50);
      } catch (InterruptedException e) {
	// System.out.println("sleep interrupted " + e);
      }
      if (ticks == 10) {
	System.gc();
	ticks = 0;
      } else {
	ticks++;
      }
      
      synchronized (this) {
	playground.tick(bufferRaster);
      }
      render();
    }
  }
  
  /** Old experimental method for drawing a 3x3 square on a raster.
      Kept around for testing purposes, but generally doesn't fit with
      the focus of the Demo applet.
      @deprecated
  */
  private void drawSquare(Raster raster, int x, int y) {
    raster.setPixel(390625, x+1, y+1);
    raster.setPixel(78125, x, y+1);
    raster.setPixel(15625, x-1, y+1);
    raster.setPixel(3125, x+1, y);
    raster.setPixel(625, x, y);
    raster.setPixel(125, x-1, y);
    raster.setPixel(25, x+1, y-1);
    raster.setPixel(5, x, y-1);
    raster.setPixel(1, x-1, y-1);
  }

  /** Component mouseDown method.
      Follows JDK 1.02 event model for backwards compatibility with
      old versions of java.
      Checks if the point (x,y) is on a Sprite in the playground.  If
      so, doesn't nothing (other than the sideeffects caused by the
      touch method of playground).  Otherwise it creates a new
      ColorCyclingFollower sprite spiralling around the center point
      and adds it the playground. 
  */
  public synchronized boolean mouseDown(Event evt, int x, int y) {
    boolean didUserTouchASprite = false;
    didUserTouchASprite = playground.touch(x,y);
    if (!didUserTouchASprite) {
      if (currentNum >= 9) {
	currentNum = 0;
      } else {
	currentNum++;
      }
      
      double xroot = (double)playground.getWidth()/2 - x;
      double yroot = (double)playground.getHeight()/2 - y;
      
      double growth = 2000./ Math.abs(xroot * xroot * xroot +
				      yroot * yroot * yroot);
      // System.out.println("Growth " + growth);

      // Path path = new SpiralPath( x, y, 50., -growth)
      Path path = 
	new SpiralPath
	( new Point(x,y),
	  new Point(playground.getWidth()/2, playground.getHeight()/2),
	  -growth);

      Follower f = new ColorCyclingFollower("Number:" + currentNum,
					    numbers[currentNum], 
					    path );
      playground.addSprite(f);
    }
    return true;
  }

}
