import java.applet.*;
import java.awt.*;

/** class Brickout 
  *
  * class Brickout implements a version of the classic {brickout, blockout,
  * arkanoid, etc} game, with specifiable art components.
  *
  * Parameters:
  *    bgimage:      file to use as the playfield image for the game.
  *                  This image should be the size of the applet.
  *
  *    tick_length:  time (ms) between animation frames
  *
  *    brick:        file to use as the brick image (needs to be 40x20)
  *
  *    paddle:       file to use as the paddle image
  *
  *    paddle_trans: translucency of the paddle (0-256, please!)
  */

public class Brickout extends Applet implements Runnable{
  BrickoutPlayfield playfield;
  Image output;
  Ball spinhead;
  Paddle paddle;
  Thread animate;
  int tick_length = 100;
  int paddle_trans = 256;
  int last_x, last_y;
  boolean mouse_moved = false;

  public void init() {
    String tl = getParameter("tick_length");
    if (tl != null) tick_length = Integer.parseInt(tl);

    tl = getParameter("paddle_trans");
    if (tl != null) paddle_trans = Integer.parseInt(tl);

    Image[] winimg = new Image[2];
    winimg[0]=getImage(getDocumentBase(),"youwin2.gif");
    winimg[1]=getImage(getDocumentBase(),"youwin1.gif");
    AnimatedSprite winsprite = new AnimatedSprite(winimg);
    winsprite.addState("blink",0,1,0,0);
    winsprite.addState("blink",1,1,0,0);
    winsprite.setTrack("blink");

    playfield = new BrickoutPlayfield(getImage(getDocumentBase(),
				       getParameter("bgimage")),
			      Integer.parseInt(getParameter("width")),
			      Integer.parseInt(getParameter("height")),
			      new Sprite(getImage(getDocumentBase(),
						  "gameover.gif")),
			      winsprite);
    output = playfield.toUpdatedImage();

    // Create the game ball sprite
    Image[] spinimgs = new Image[8];
    spinimgs[0]=getImage(getDocumentBase(),"spinhead/T1.gif");
    spinimgs[1]=getImage(getDocumentBase(),"spinhead/T2.gif");
    spinimgs[2]=getImage(getDocumentBase(),"spinhead/T3.gif");
    spinimgs[3]=getImage(getDocumentBase(),"spinhead/T4.gif");
    spinimgs[4]=getImage(getDocumentBase(),"spinhead/T5.gif");
    spinimgs[5]=getImage(getDocumentBase(),"spinhead/T6.gif");
    spinimgs[6]=getImage(getDocumentBase(),"spinhead/T7.gif");
    spinimgs[7]=getImage(getDocumentBase(),"spinhead/T8.gif");
    spinhead = new Ball(spinimgs);

    // Set up animation tracks
    spinhead.addState("spinright",0,1,0,0);
    spinhead.addState("spinright",1,1,0,0);
    spinhead.addState("spinright",2,1,0,0);
    spinhead.addState("spinright",3,1,0,0);
    spinhead.addState("spinright",4,1,0,0);
    spinhead.addState("spinright",5,1,0,0);
    spinhead.addState("spinright",6,1,0,0);
    spinhead.addState("spinright",7,1,0,0);
    
    spinhead.addState("spinleft",7,1,0,0);
    spinhead.addState("spinleft",6,1,0,0);
    spinhead.addState("spinleft",5,1,0,0);
    spinhead.addState("spinleft",4,1,0,0);
    spinhead.addState("spinleft",3,1,0,0);
    spinhead.addState("spinleft",2,1,0,0);
    spinhead.addState("spinleft",1,1,0,0);
    spinhead.addState("spinleft",0,1,0,0);
	
    spinhead.setTrack("spinright");
    
    // Add spinhead to the playfield
    playfield.addSprite(spinhead, true);
    spinhead.setPlayfield(playfield);
    spinhead.setPos(0,playfield.height-spinhead.height);
    spinhead.setVelocity(5,-5);

    // Build the bricks on the playfield
    Brick brick;
    for (int ypos=0; ypos<8; ypos++)
      for (int xpos=0; xpos<10; xpos++) {
	brick = new Brick(getImage(getDocumentBase(), getParameter("brick")),
			   "brick"+((ypos*10)+xpos));
	playfield.addSprite(brick);
	brick.setPlayfield(playfield);
	brick.setPos(xpos*40,ypos*20);
      }

    // Add the paddle to the playfield
    paddle = new Paddle(getImage(getDocumentBase(), getParameter("paddle")));
    paddle.multAlpha(paddle_trans);
    playfield.addSprite(paddle);
    paddle.setPlayfield(playfield);
    paddle.setPos(playfield.width/2-paddle.width/2, 
		  playfield.height-paddle.height-20);
    last_y = paddle.y;
    render();
  }

  // Brickout.start -- on start, start animation thread
  public void start() {
    animate = new Thread(this);
    animate.start();
  }
 
  // Brickout.stop -- on stop, kill animation thread
  public void stop() {
    animate = null;
  }

  // Brickout.run -- run() of the animate Thread
  public void run() {
    while (true) {
      try {
	animate.sleep(tick_length); // 1 tick = how many ms?
      } catch (InterruptedException e) {
      }

      // send a tick to the playfield
      playfield.Tick();
      if (mouse_moved) {
	paddle.setPos(last_x, last_y);
	mouse_moved = false;
      }
      render();
    }
  }

  // Brickout.paint -- draw the output image to screen
  public void paint(Graphics g) {
    g.drawImage(output, 0, 0, this);
  }

  public void update(Graphics g) {
    paint(g);
  }

  public boolean mouseMove(Event e, int x, int y) {
    last_x = x-paddle.width/2;
    mouse_moved = true;
    return true;
  }

  // Brickout.render -- render the output framebuffer
  //
  // This redoes the entire screen. Icky; that's a lot of bits to shuffle
  // around.  We now only update the changed portion of the image (thanks
  // to some magic with the evil MemoryImageSource) and only repaint the
  // changed portion of the image. After all this, my applet is almost 
  // what I would call 'speedy', especially for Java.
  void render() {
    playfield.Draw();
    playfield.source.newPixels(playfield.dirty_x, playfield.dirty_y,
			       playfield.dirty_w, playfield.dirty_h);
    repaint(playfield.dirty_x, playfield.dirty_y,
	    playfield.dirty_w, playfield.dirty_h);
  }
}
