import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.util.Vector;
import java.awt.Rectangle;

public class Bounce2 extends Applet implements ActionListener, AdjustmentListener, MouseListener, MouseMotionListener, Runnable
{
  //runtime variables
  boolean running = false;
  boolean kill = false;

  //buttons
  Button runbutton = new Button("Run");
  Button pausebutton = new Button("Pause");
  Button quitbutton = new Button("Quit");

  //text
  Label speedlabel = new Label("Speed");
  Label sizelabel = new Label("Size");

  Panel drawingpanel;

  //scrollbars
  private final int barHeight = 20, SLIDER_WIDTH = 10, MAXSPEED = 110, MINSPEED = 0, MAX_SIZE = 110, MIN_SIZE = 10;
  Scrollbar speedbar = new Scrollbar(Scrollbar.HORIZONTAL, MAXSPEED/2, SLIDER_WIDTH, MINSPEED, MAXSPEED);
  Scrollbar sizebar = new Scrollbar(Scrollbar.HORIZONTAL, MAX_SIZE/2, SLIDER_WIDTH, MIN_SIZE, MAX_SIZE);

  //drawn objs
  Ball ball;
  
  private Thread ballThread;

  //boundaries
  static int boundx = 640;
  static int boundy = 400;

  //speed
  int speed = MAXSPEED/2;
  int delay = MAXSPEED - speed;

  //initialize the applet and draw everything
  public void init()
  {
    double colWeight[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//15 cols
    double rowWeight[] = {1,1,1,1,1,1,1,1,1,1}; //10 rows
    int colWidth[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//15 cols
    int rowHeight[] = {1,1,1,1,1,1,1,1,1,1}; //10 rows
    GridBagConstraints c = new GridBagConstraints();
    GridBagLayout gbl = new GridBagLayout();
    gbl.rowHeights = rowHeight;
    gbl.rowWeights = rowWeight;
    gbl.columnWeights = colWeight;
    gbl.columnWidths = colWidth;
    c.anchor = GridBagConstraints.CENTER;

    setBounds(0,0,480,640);
    setLayout(new BorderLayout());
    Panel controlpanel = new Panel();
    controlpanel.setLayout(gbl);
    controlpanel.setSize(640,80);

    drawingpanel = new Panel();
    drawingpanel.setLayout(new BorderLayout(0,0));
    drawingpanel.setSize(640,400);
    ball = new Ball();
    drawingpanel.add("Center",ball);
    drawingpanel.setVisible(true);

    //speed scrollbar
    c.weightx = 1;
    c.weighty = 1;
    c.gridwidth = 3;
    c.gridheight = 1;
    c.gridx = 1;
    c.gridy = 7;
    c.fill= GridBagConstraints.HORIZONTAL;
    gbl.setConstraints(this.speedbar,c);

    //run button
    c.weightx = 1;
    c.weighty = 1;
    c.gridwidth = 2;
    c.gridheight = 1;
    c.gridx = 5;
    c.gridy = 7;
    c.fill= GridBagConstraints.HORIZONTAL;
    gbl.setConstraints(this.runbutton,c);

    //pause button
    c.weightx = 1;
    c.weighty = 1;
    c.gridwidth = 2;
    c.gridheight = 1;
    c.gridx = 8;
    c.gridy = 7;
    c.fill= GridBagConstraints.HORIZONTAL;
    gbl.setConstraints(this.pausebutton,c);

    //size scrollbar
    c.weightx = 1;
    c.weighty = 1;
    c.gridwidth = 3;
    c.gridheight = 1;
    c.gridx = 11;
    c.gridy = 7;
    c.fill= GridBagConstraints.HORIZONTAL;
    gbl.setConstraints(this.sizebar,c);

    //speed text label
    c.weightx = 1;
    c.weighty = 1;
    c.gridwidth = 3;
    c.gridheight = 1;
    c.gridx = 1;
    c.gridy = 8;
    c.fill= GridBagConstraints.HORIZONTAL;
    gbl.setConstraints(this.speedlabel,c);

    //size text label
    c.weightx = 1;
    c.weighty = 1;
    c.gridwidth = 3;
    c.gridheight = 1;
    c.gridx = 11;
    c.gridy = 8;
    c.fill= GridBagConstraints.HORIZONTAL;
    gbl.setConstraints(this.sizelabel,c);

    //quit button
    c.weightx = 1;
    c.weighty = 1;
    c.gridwidth = 3;
    c.gridheight = 1;
    c.gridx = 6;
    c.gridy = 9;
    c.fill= GridBagConstraints.HORIZONTAL;
    gbl.setConstraints(this.quitbutton,c);

    //add to the screen
    controlpanel.add(this.speedbar);
    controlpanel.add(this.runbutton);
    controlpanel.add(this.pausebutton);
    controlpanel.add(this.sizebar);
    controlpanel.add(this.speedlabel);
    controlpanel.add(this.sizelabel);
    controlpanel.add(this.quitbutton);

    //add listners
    speedbar.addAdjustmentListener(this);
    runbutton.addActionListener(this);
    pausebutton.addActionListener(this);
    sizebar.addAdjustmentListener(this);
    quitbutton.addActionListener(this);
    ball.addMouseListener(this);
    ball.addMouseMotionListener(this);

    //add the panels
    add("South", controlpanel);
    add("Center", drawingpanel);


    //drawing paramaters
    //loc = new Point(loc.x+dx, loc.y+dy);
  }

  public void start()
  {
    if (ballThread == null)
    {
      ballThread = new Thread(this);
      ballThread.start();
    }
  }

  public void run()
  {
    Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
    while (!kill)
    {
      if (running)
      {
        ball.repaint();
      }
      try
      {
        Thread.sleep(delay);
      }
      catch(InterruptedException e){System.err.println("Interrupted.");}
    }
    stop();
  }

  public void paint(Graphics g)
  {
    super.paint(g);
  }

	public void mouseClicked(MouseEvent m)
	{
		if (!ball.flag)
    {
      Point p;
      p = new Point(m.getPoint());
      Vector<Rectangle> v;
      v = (Vector<Rectangle>)ball.r;
      boolean done = false;
      int i = 0;

      while (!done)
      {
        if(v.elementAt(i).contains(p))
        {
          v.removeElement(i);
          System.err.print("Element removed");
        }
        i++;
        if(i>=v.size())
          done=true;
      } 
      ball.r = v;
    }
	}
	
	public void mousePressed(MouseEvent m)
	{
		ball.flag = true;
		ball.start = m.getPoint();
    ball.end = ball.start;
    ball.cur = ball.start;
	}
	
	public void mouseDragged(MouseEvent m)
	{
		if (ball.flag == true)
		{
			ball.cur = m.getPoint();
		}
	}
	
	public void mouseReleased(MouseEvent m)
	{
    //add rect method in ball
    ball.end = ball.cur;
    if (ball.flag)
    {
      ball.addrect();
    }
		ball.flag = false;
	} 
	
	public void mouseEntered(MouseEvent m){}
	public void mouseExited(MouseEvent m){}
	public void mouseMoved(MouseEvent m){}

  public void actionPerformed(ActionEvent e)
  {
    Object source = e.getSource();
    if (source == this.runbutton)
    {
      running = true;
    }
    else if (source == this.pausebutton)
    {
      running = false;
    }
    else if (source == this.quitbutton)
    {
      //kill processes
      kill = true;
      //remove listeners
      stop();
    }
  }

  public void adjustmentValueChanged(AdjustmentEvent e)
  {
    Object source = e.getSource();
    //set the new size.
    if (source == sizebar)
    {
      //check for clipping
      int newsize = sizebar.getValue();
      newsize = ball.resize(newsize);
      sizebar.setValue(newsize);
    }
    if (source == speedbar)
    {
      speed = speedbar.getValue();
      delay = MAXSPEED - speed;
    }
  }

  public void stop()
  {
    this.speedbar.removeAdjustmentListener(this);
    this.runbutton.removeActionListener(this);
    this.pausebutton.removeActionListener(this);
    this.sizebar.removeAdjustmentListener(this);
    this.quitbutton.removeActionListener(this);
    this.removeMouseListener(this);
    this.removeMouseMotionListener(this);
    Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
  }
}	

//class to handle animations
class Ball extends Canvas
{   
  Rectangle temp;
  static int size = 50;
  private Graphics obj;
  Image offscreen = null;
  static Point loc = new Point(100,100); //location of the ball

  //boundaries
  static int boundx = 640;
  static int boundy = 400;

  //directions
  int dx = 1; //1 = left, -1 = right
  int dy = 1; //1 = up, -1 = down

  static boolean flag = false;

  //rectangle vector and associated things
  Vector<Rectangle> r = new Vector<Rectangle>();
  Point start, cur, end;

  public int resize(int newsize)
  {
    // x
    if (loc.x+newsize >= boundx)
    {
      newsize = boundx - loc.x - 1;
      return newsize;
    }

    // y
    if (loc.y+newsize >= boundy + 100)
    {
      newsize = boundy+100 - loc.y - 1;
      return newsize;
    }
    size = newsize;
    return newsize;
  }

  public void move()
  {
    //if it will hit the right or left boundary, flip the x direction and set it
    if (loc.x+size >= boundx || loc.x <= 0)
    { dx *= -1; }
    //if it will hit the top or bottom boundray, flip the y direction and set it
    if (loc.y+size >= boundy || loc.y <= 0)
    { dy *= -1; }
    

    for (int i = 0; i < r.size(); i++)
    {

      temp = new Rectangle(r.elementAt(i));
      int rx = temp.x;
      int ry = temp.y;
      int rh = temp.height;
      int rw = temp.width;
      //If the ball hits either side of the rectangle, change the x direction
      if((loc.x > rx && loc.x > ry && loc.x < (ry + rh))||(loc.x < (rx + rw) && loc.x > rx && loc.x <(ry + rh)))
        {dx *= -1;}
      //If the ball hits either the top or bottom, change the y direction
      if((loc.y > ry && loc.y > rx && loc.y < (rx + rw))||(loc.y < (ry + rh) && loc.y > ry && loc.y <(rx + rw)))
        {dy *= -1;}
    }
    //Increment or decrement the location of the ball based on the X and Y directions.
    loc.x += dx;
    loc.y += dy;
  }

  public void addrect()
  {
    //What was wrong here is that for the final parameter, I had Math.abs(end.y-start.x). 
    if (end.x > start.x && end.y > start.y)
    r.add(new Rectangle(start.x,start.y, Math.abs(end.x-start.x),Math.abs(end.y-start.y)));
    if (end.x > start.x && end.y < start.y)
      r.add(new Rectangle(start.x,end.y, Math.abs(end.x-start.x),Math.abs(end.y-start.y)));  
    if (end.x < start.x && end.y > start.y)
      r.add(new Rectangle(end.x, start.y, Math.abs(end.x-start.x),Math.abs(end.y-start.y)));
    if (end.x < start.x && end.y < start.y)
      r.add(new Rectangle(end.x, end.y, Math.abs(end.x-start.x),Math.abs(end.y-start.y)));
  }

  public void update(Graphics g)
  {
    //super.update(g);       //Getting rid of this fixed double buffering
    // I looked at P-dawg's code and he didn't have it.
    Graphics buffer;
    if (offscreen == null)
    {
      offscreen = createImage(getWidth(), getHeight());
    }
    buffer = offscreen.getGraphics();
    buffer.setColor(getBackground());
    buffer.fillRect(0,0,640, 410);
    mypaint(buffer);
    g.drawImage(offscreen, 0, 0, this);
  }

  public void mypaint(Graphics g)
  {
    //update loc
    move();

    //draw
    g.setColor(Color.RED);
    for (int i = 0; i < r.size(); i++)
    {
      Rectangle t;
      t = new Rectangle(r.elementAt(i));
      g.fillRect(t.x,t.y,t.width,t.height);
    }
    g.setColor(Color.BLACK);
    g.drawOval(loc.x, loc.y, size, size);
    g.fillOval(loc.x, loc.y, size, size);

    if (flag)
    {
      if (!r.isEmpty())
      {
        for (int i = 0; i < r.size(); i++) 
        {
          if (cur.x > start.x && cur.y > start.y)
          {
            g.drawRect(start.x, start.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y));
            g.setColor(Color.gray);
            g.fillRect(start.x, start.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y));
          }  
          if (cur.x > start.x && cur.y < start.y)
          {
            g.drawRect(start.x, cur.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y));
            g.setColor(Color.gray);
            g.fillRect(start.x, cur.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y));              
          }
          if (cur.x < start.x && cur.y > start.y)
          {
            g.drawRect(cur.x, start.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y));
            g.setColor(Color.gray);
            g.fillRect(cur.x, start.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y)); 
          }
          if (cur.x < start.x && cur.y < start.y)
          {
            g.drawRect(cur.x, cur.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y));
            g.setColor(Color.gray);
            g.fillRect(cur.x, cur.y, Math.abs(cur.x - start.x), Math.abs(cur.y - start.y));
          }
        }
      }
    }
  }
}