//========================================================
// QuikDraw - a simple drawing applet
// Written by Chris DeCoro - cdecoro@cat.nyu.edu
// Sept. 9, 2002
//========================================================

import java.util.*;
import java.awt.*;
import java.awt.event.*;

abstract class Shape extends Object
{
	void setPosition(int x, int y)
	{
		xCoord = x;
		yCoord = y;
	}
	
	int getX()
	{
		return xCoord;
	}
	
	int getY()
	{
		return yCoord;
	}

	void setCorner(int x, int y)
	{
		xCorner = x;
		yCorner = y;
	}
	
	int getCornerX()
	{
		return xCorner;
	}
	
	int getCornerY()
	{
		return yCorner;
	}

	int getUpperLeftX()
	{
		int xHalfWidth = Math.abs(xCoord - xCorner);
		return xCoord - xHalfWidth;		
	}

	int getUpperLeftY()
	{
		int yHalfWidth = Math.abs(yCoord - yCorner);
		return yCoord - yHalfWidth;		
	}

	int getWidth()
	{
		return 2*Math.abs(xCoord - xCorner);
	}
	
	int getHeight()
	{
		return 2*Math.abs(yCoord - yCorner);
	}

	void setColor(Color c)
	{
		color = c;
	}
	
	Color getColor()
	{
		return color;
	}
	
	int sq(int x)
	{
		return x*x;
	}
	
	int distToCorner()
	{
		return (int)Math.sqrt( sq(xCoord-xCorner) + sq(yCoord-yCorner) );
	}

	boolean hitBBox(int x, int y)
	{
		int ulX = getUpperLeftX();
		int ulY = getUpperLeftY();

		if( x >= ulX &&
		    x <= ulX + getWidth() &&
		    y >= ulY &&
		    y <= ulY + getHeight() )
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	abstract void draw(Graphics g);
	abstract String getName();

	private int xCoord=0;
	private int yCoord=0;	

	private int xCorner=0;
	private int yCorner=0;	

	private Color color=Color.red;
}

class Circle extends Shape
{
	void draw(Graphics g)
	{
		g.setColor(getColor());	
		g.fillOval(getUpperLeftX(), getUpperLeftY(), 
			getWidth(), getHeight());	
	}	

	String getName()
	{
			return "Circle";
	}
}

class Rect extends Shape
{
	void draw(Graphics g)
	{
		g.setColor(getColor());	
		g.fillRect(getUpperLeftX(), getUpperLeftY(), 
			   getWidth(), getHeight());	
	}

	String getName()
	{
			return "Rectangle";
	}
}

class OpenCircle extends Shape
{
	void draw(Graphics g)
	{
		g.setColor(getColor());	
		g.drawOval(getUpperLeftX(), getUpperLeftY(), 
			getWidth(), getHeight());	
	}	

	String getName()
	{
			return "Open Circle";
	}
}

class OpenRect extends Shape
{
	void draw(Graphics g)
	{
		g.setColor(getColor());	
		g.drawRect(getUpperLeftX(), getUpperLeftY(), 
			getWidth(), getHeight());	
	}	

	String getName()
	{
			return "Open Rectangle";
	}
}

class Line extends Shape
{
	void draw(Graphics g)
	{
		g.setColor(getColor());	
		int cX=getCornerX(), cY=getCornerY();
		
		//g.drawLine(getX(), getY(), getCornerX(), getCornerY());
		g.drawLine(getX()+(getX()-cX), getY()+(getY()-cY), cX, cY);
	}

	String getName()
	{
			return "Line";
	}
}

public class QuikDraw extends GenericApplet
{
	int x = 100, y = 100;

	public void render(Graphics g) 
	{
		if (damage) 
		{
			g.setColor(Color.white);
			g.fillRect(0, 0, bounds().width, bounds().height);

			for(int i=0; i<shapeArray.size(); i++)
			{
				Shape shape = (Shape)shapeArray.elementAt(i);
				shape.draw(g);
			}

			g.setColor(Color.red);
			g.drawLine(x - 20, y + 20, x + 20, y - 20);
			g.drawLine(x - 20, y - 20, x + 20, y + 20);

			g.setColor(selectedColor != null ?
				   selectedColor :
				   makeRandomColor() );
			g.drawString("Current Shape: " + makeSelectedShape().getName(),
						20, 20);
		}
	}

	public boolean mouseDown(Event e, int x, int y) 
	{		 
		boolean leftButton, centerButton, rightButton;
		centerButton = ((e.modifiers & e.ALT_MASK) > 0);
		rightButton = ((e.modifiers & e.META_MASK) > 0);
		leftButton = !centerButton && !rightButton;

		if(centerButton)
			System.out.println("Right Button");

		boolean picked = false;
		currentShape = pickShape(x, y);
		if( currentShape != null )
				picked = true;

		if(!picked && leftButton)
		{
			Shape shape = null;
			shape = makeSelectedShape();

			shapeArray.addElement(shape);
			currentShape = shape;
		
			currentShape.setPosition(x,y);
			currentShape.setCorner(x-10,y-10);
		}
		else if(picked && leftButton && deletePressed)
		{
			shapeArray.remove(currentShape);
		}
		else if(picked && centerButton)
		{
			currentShape.setColor(
				selectedColor != null ? 
				selectedColor : 
				makeRandomColor() );
		}		
	   
		setDamage();
		return true;

		//moveXY(x, y); return true; 
	}

	public boolean mouseDrag(Event e, int x, int y) 
	{ 
		boolean leftButton, centerButton, rightButton;
		centerButton = ((e.modifiers & e.ALT_MASK) > 0);
		rightButton = ((e.modifiers & e.META_MASK) > 0);
		leftButton = !centerButton && !rightButton;
		boolean picked = ( currentShape == null ? false : true );
			
		//circle.setCorner(x,y);
		if(picked && leftButton)
		{
			currentShape.setCorner(x,y);
		}
		else if(picked && rightButton)
		{
			int oldX, oldY;
			oldX = currentShape.getX();
			oldY = currentShape.getY();
			int cX, cY;
			cX = currentShape.getCornerX();
			cY = currentShape.getCornerY();
			
			currentShape.setPosition(x,y);	
			currentShape.setCorner(x+(cX-oldX), y+(cY-oldY));
		}
		
		setDamage();
		moveXY(x, y); return true; 
	}

	public boolean keyDown(Event evt, int key)
	{
		switch(key)
		{
		case '1':
			selectedShape = CIRCLE;
			break;
		case '2':
			selectedShape = RECT;
			break;
		case '3':
			selectedShape = OPEN_CIRCLE;
			break;
		case '4':
			selectedShape = OPEN_RECT;
			break;
		case '5':
			selectedShape = LINE;
			break;
		case 'b':
			selectedColor = Color.blue;
			break;
		case 'c':
			selectedColor = Color.cyan;
			break;
		case 'g':
			selectedColor = Color.green;
			break;
		case 'k':
			selectedColor = Color.black;
			break;
		case 'm':
			selectedColor = Color.magenta;
			break;
		case 'o':
			selectedColor = Color.orange;
			break;
		case 'p':
			selectedColor = Color.pink;
			break;
		case 'r':
			selectedColor = Color.red;
			break;
		case 'y':
			selectedColor = Color.yellow;
			break;	
		case '?':
			selectedColor = null;
			break;
		case Event.DELETE:
			deletePressed = true;
			break;
		}

		setDamage();	
		return true;		
	}

	public boolean keyUp(Event evt, int key)
	{
		if( key == evt.DELETE )
		{
				deletePressed = false;
		}

		return true;
	}
	
	Shape makeSelectedShape()
	{
		return makeShape(selectedShape);
	}

	Shape makeShape(int shapeType)
	{
		if( shapeType > LAST_SHAPE || shapeType <= 0 )
				throw new RuntimeException("Specified an invalid shape");

		Shape shape = null;
		switch(selectedShape)
		{
		case CIRCLE:
			shape = new Circle();
			break;
		case RECT:
			shape = new Rect();
			break;
		case OPEN_CIRCLE:
			shape = new OpenCircle();
			break;
		case OPEN_RECT:
			shape = new OpenRect();
			break;
		case LINE:
			shape = new Line();
			break;
		}
		
		shape.setColor(selectedColor != null ? selectedColor
				: makeRandomColor() );	
		return shape;
	}

	Shape pickShape(int x, int y)
	{
		Shape pickedShape = null;
		
		//Loop through all of the shapes to see if we find a hit
		for(int i=0; i<shapeArray.size(); i++)
		{
			Shape shape = (Shape)shapeArray.elementAt(i);
		
			if( shape.hitBBox(x,y) )
			{
				pickedShape = shape;
			}
		}

		return pickedShape;
	}	
	
	Vector shapeArray = new Vector();
	Shape currentShape = null;
	Circle circle = new Circle();

	public static final int CIRCLE = 1;
	public static final int RECT = 2;
	public static final int OPEN_CIRCLE = 3;
	public static final int OPEN_RECT = 4;
	public static final int LINE = 5;
	public static final int LAST_SHAPE = 5;

	boolean deletePressed=false;
	int selectedShape = CIRCLE;
	Color selectedColor = Color.red;

	Color makeRandomColor()
	{
		return new Color(
			(int)(Math.random() * 255.0),
			(int)(Math.random() * 255.0),
			(int)(Math.random() * 255.0)
				);
	}
	
	void moveXY(int x, int y) { this.x = x; this.y = y; damage = true; }

	void setDamage()
	{
		damage = true;
	}
}

class GenericApplet extends java.applet.Applet implements Runnable
{
   public boolean damage = true;        // you can force a render
   public void render(Graphics g) { }   // you can define how to render

   private Image image = null;
   private Graphics buffer = null;
   private Thread t;
   private Rectangle r = new Rectangle(0, 0, 0, 0);

   public void start() { if (t == null) { t = new Thread(this); t.start(); } }
   public void stop()  { if (t != null) { t.stop(); t = null; } }
   public void run()   { try { while (true) { repaint(); t.sleep(30); } }
                             catch(InterruptedException e){}; }

   public void update(Graphics g) {
      if (r.width != bounds().width || r.height != bounds().height) {
         image = createImage(bounds().width, bounds().height);
         buffer = image.getGraphics();
         r = bounds();
         damage = true;
      }
      render(buffer);
      damage = false;
      if (image != null)
         g.drawImage(image,0,0,this);
   }
}



