import java.awt.Color;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.awt.Image;
import javax.swing.ImageIcon;
import java.awt.Cursor;
import java.awt.Robot;
import java.awt.AWTException;
import java.awt.image.BufferedImage;
import java.awt.print.Printable;
import java.awt.print.PageFormat;
import java.awt.print.PrinterException;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.AWTException;
import java.awt.Font;
import java.awt.image.PixelGrabber;
import java.awt.image.MemoryImageSource;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.CubicCurve2D.Float;
import java.awt.geom.Point2D;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
import java.awt.geom.AffineTransform;

public class DrawingCanvas extends Canvas implements MouseListener, 
MouseMotionListener, Printable, FocusListener
{
	public static final int FILL = 1, NORMAL = 2, OUTLINE = 3, RECT = 4, OVAL = 5, LINE = 6; 
	public static BufferedImage selectImage;
	public static int extraOperations, lineWidth, radius, amtLines;
	private Color drawColor, oppositeColor;
	private BufferedImage printImage, resizeImage;
	private CubicCurve2D c;
	private Point2D p1, p2, p3, p4;
	private boolean polyStart, arcStart, curveMade, curveMade2, save, dragSave;
	private int mouseX, mouseY, prevX, prevY, originX, originY, width, height;
	private int[] pixels;
	
  	/** Creates a default DrawingCanvas. **/
  	public DrawingCanvas()
	{
		// save is set to true unless the focus is lost
		save = true;
		// sets the default linewidth and the spray radius
		lineWidth = 1;
		radius = 5;
		amtLines = 10;
		// adds listeners and sets settings
		setSize(400,400);
		setBackground(Color.WHITE);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.addFocusListener(this);
	}
	
	/**
	 * This methods adjusts the screen size of the user by taking a snapshot of 
	 * the canvas and seeing if it shifts up or down and sets the pixels 
	 * accordingly.
	 **/ 
	public void checkScreenSize()
	{
		saveFromResize();
		pixels = new int[getWidth()*getHeight()];
		PixelGrabber pg = new PixelGrabber(resizeImage, 0, 0, getWidth(), getHeight(), pixels, 0, getWidth());
    	
    	try {
		    pg.grabPixels();
		} catch (InterruptedException ie) {}
		
		
		if(pixels[0] != (Color.WHITE).getRGB())
			ControlClass.extraY = 78;	
		else
			ControlClass.extraY = 71;	
	}
	
	/** Saves an image of the current screen so if the canvas resizes the image doesnt get deleted. **/
	public void saveFromResize()
	{
		try{
			resizeImage = new Robot().createScreenCapture(
			new Rectangle(ControlClass.xCoord,ControlClass.yCoord,getWidth(),getHeight()));
		}catch(AWTException awte){}
	}
	
	/** Draws the image back to the screen after the canvas is resized **/
	public void pasteFromResize()
	{
		Graphics g = getBufferStrategy().getDrawGraphics();	
		g.drawImage(resizeImage,0,0,null);
		repaint();				
	}
	
	/** Fills anything of the same color the first pixel selected. **/
	public void fillOperation(MouseEvent e)
    {
    	// saves the image and then creates a pixel array
    	saveImageForPrinting();
		pixels = new int[getWidth()*getHeight()];
		
		// grabs all the pixels from the image
		PixelGrabber pg = new PixelGrabber(printImage, 0, 0, getWidth(), getHeight(), pixels, 0, getWidth());
    	try {
		    pg.grabPixels();
		} catch (InterruptedException ie) {}
		
		// calls the start fill method, rgb is the color clicked on
		int rgb = pixels[e.getY()*getWidth()+e.getX()];
		startFill(e.getX(),e.getY(),rgb);
		
		// creates an image from the pixel array and draws it back to the screen
		Image img = createImage(new MemoryImageSource(getWidth(), getHeight(), pixels, 0, getWidth()));
 		Graphics g = getBufferStrategy().getDrawGraphics();	
		g.drawImage(img,0,0,null);
 		repaint();
 		saveFromResize();
	}
	
	/** Changes the color of the pixel at the x and y coordinate. **/
   	private void changePixel(int x, int y, int rgb)
	{
		pixels[y*getWidth()+x] = rgb;
	}

	/** Gets the pixel at the x and y point **/
   	private int getPixel(int x, int y)
	{
		return pixels[y*getWidth()+x];
	}
	
	/** Helper method for fillOperation. This method does the actually filling. **/
	public void startFill(int x, int y, int original)
	{
		int i = y*getWidth()+x;
		if(pixels[i] != original)
			return;
		
		// sets the pixel quete to the whole canvas
     	int pixelQueue[] = new int[getWidth() * getHeight()];
		int pixelQueueSize = 0;
		
		// sets the first pixel
		pixelQueue[0] = (y << 16) + x;
		pixelQueueSize = 1;
		
		changePixel(x,y,original);

		// loop to check and does a flood fill to the original color
		while (pixelQueueSize > 0)
		{
			x = pixelQueue[0] & 0xffff;
			y = (pixelQueue[0] >> 16) & 0xffff;
			pixelQueueSize--;
	        pixelQueue[0] = pixelQueue[pixelQueueSize];
		    
		    if (x > 0) 
		    {
				if (getPixel(x-1, y) == original)
		        {
					changePixel(x-1, y, drawColor.getRGB());
					pixelQueue[pixelQueueSize] =  (y << 16) + x-1;
		   	        pixelQueueSize++;
				}
			}

        	if (y > 0) 
        	{
	          	if (getPixel(x, y-1) == original)
	          	{
		        	 changePixel(x, y-1, drawColor.getRGB());
				     pixelQueue[pixelQueueSize] = ((y-1) << 16) + x;
		             pixelQueueSize++;
		          }
			}
	        if (x < getWidth()-1) 
	        {
				if (getPixel(x+1, y) == original)
				{
		             changePixel(x+1, y, drawColor.getRGB());
		             pixelQueue[pixelQueueSize] = (y << 16) + x+1;
		             pixelQueueSize++;
		        }
			}
			if (y < getHeight()-1) 
			{
				if (getPixel(x, y+1) == original)
				{
					changePixel(x, y+1, drawColor.getRGB());
					pixelQueue[pixelQueueSize] = ((y+1) << 16) + x;
					pixelQueueSize++;
				}
			}
		}
	}
	
	/** Helper method for the rotation. Sets the amount of rotation 90 degrees. **/
	private AffineTransform setRotation() 
 	{
		AffineTransform atright = AffineTransform.getRotateInstance(Math.PI / 2, getWidth() / 2, getHeight() / 2);
	    return atright;
	} 
 	
 	/** This method actually rotates the image 90 degrees. **/
 	public void rotateOperation()
 	{
 		// saves the image and then clears it and draws it rotated 90 degrees.
 		Graphics g = getBufferStrategy().getDrawGraphics();
 		Graphics2D g2 = (Graphics2D) g;
       	saveFromResize();
        clearImage();
        g2.drawImage(resizeImage, setRotation(), this);
        repaint();
   	}
   	
	/** Saves the image so that it can be printed. **/
	public void saveImageForPrinting()
	{
		// saves the image of the screen
		try{
			printImage = new Robot().createScreenCapture(
			new Rectangle(ControlClass.xCoord,ControlClass.yCoord,getWidth(),getHeight()));
		}catch(AWTException awte){}
	}
	
	/** This is a helper method for print. Draws the image to be printed. **/
	public void drawImage(Graphics2D g2D)
	{
		// draws the print image out on paper
		g2D.drawImage(printImage,0,0,null);
	}
	
	/** Printing operation, it prints the image saved. **/
	public int print(Graphics g, PageFormat pf, int pIndex) throws PrinterException
	{
		if (pIndex > 0) 
			return Printable.NO_SUCH_PAGE;
	
		Graphics2D g2D = (Graphics2D)g;
		drawImage(g2D);
       	return Printable.PAGE_EXISTS;
    }
    
    /** Updates the screen and flips the buffers around. **/
	public void update(Graphics g)
	{
		getBufferStrategy().show();
	}
	
	/** Paints the image onto the screen. **/
	public void paint(Graphics g)
	{
		try{update(g);}catch(NullPointerException e){}
	}
	
	/** Opens a selected image and draws it onto the screen. **/
	public void openOperation(String path)
	{
		// creates an image and draws it to the screen
		Graphics g = getBufferStrategy().getDrawGraphics();	
		ImageIcon ic = new ImageIcon(path);	
		g.drawImage((Image)ic.getImage(),0,0,null);
	}
	
	/** Selects the image from the top left corner to the bottom right corner. **/
	public void selectOperation(MouseEvent e)
	{
		if(mouseHasMoved(e))
		{
			// this checks if you select off screen, if your off screen then it will
			// set the boundaries to the edge of the drawing canvas
			int tOriginX = ControlClass.xCoord+e.getX();
			int tOriginY = ControlClass.yCoord+e.getY();
			
			if(tOriginX > getWidth()+ControlClass.xCoord)
				tOriginX = getWidth()+ControlClass.xCoord;
			if(tOriginX < ControlClass.xCoord)
				tOriginX = ControlClass.xCoord;
			if(tOriginY > getHeight()+ControlClass.yCoord)
				tOriginY = getHeight()+ControlClass.yCoord;
			if(tOriginY < ControlClass.yCoord)
				tOriginY = ControlClass.yCoord;
			
			// calculates the width and height of the area
			width = tOriginX-(ControlClass.xCoord+originX);
			height = tOriginY-(ControlClass.yCoord+originY);
			
			if(width < 0 && height < 0)
			{
				originX+=width;
				originY+=height;
			}	
			else if(width < 0 && height > 0)
				originX+=width;
			else if(width > 0 && height < 0)
				originY+=height;
				
			width = Math.abs(width);
			height = Math.abs(height);
		}
	}
	
	/** Copies the selected image **/
	public void copyOperation()
	{
		// copies the boundaries set by the mouse
		try{
			DrawingCanvas.selectImage = new Robot().createScreenCapture(
    	   	new Rectangle(ControlClass.xCoord+originX,ControlClass.yCoord+originY,
    	   	width,height));
  		}catch(AWTException awte){}
	}
	/** Copies the selected image and deletes the selected portion **/	
	public void cutOperation()
	{
		// copies the image and then draws a rectangle over the selected area
		copyOperation();
		Graphics g = getBufferStrategy().getDrawGraphics();	
		g.setColor(oppositeColor);
		g.fillRect(originX,originY,width,height);
		width = height = 0;
		repaint();
	}
	
	/** Draws the image with regards to the mouse location. **/
	public void pasteOperation()
	{
		// draws the image of the selected image onto the screen
		Graphics g = getBufferStrategy().getDrawGraphics();	
		g.drawImage(DrawingCanvas.selectImage,originX,originY,null);
		repaint();
	}
	
	/** Draws random lines of random colors on the screen. **/
	public void randomDraw()
	{
		RandomPoint rPt = new RandomPoint(getWidth(),getHeight());
		Graphics g = getBufferStrategy().getDrawGraphics();	
		int i=0;
		while(i< amtLines)
		{
			g.setColor(rPt.getColor());
			g.drawLine((int)rPt.getX(),(int)rPt.getY(),rPt.nextX(),rPt.nextY());
			i++;
		}
		repaint();
	}
	
	/** Opens up a text dialog in the selected area. **/
	public void textOperation(MouseEvent e)
	{
		// opens up a text dialog
		setCursor(new Cursor(Cursor.TEXT_CURSOR));
		TextDialog tDialog = new TextDialog(e.getX(),e.getY());
	}
	
	/** Prints the text onto the screen **/
	public void textOperation2(String txt, int x, int y, Font f)
	{
		// draws the text onto the screen
		Graphics g = getBufferStrategy().getDrawGraphics();	
 		g.setFont(f);
 		g.setColor(drawColor);
 		g.drawString(txt,x,y);
 		g.setFont(new Font("Times New Roman", Font.PLAIN, 12));	
 		repaint();
	}
	
	/** An eraser that erases when the mouse is dragged. **/
 	public void eraserOperation(MouseEvent e)
 	{
 		setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
 		Graphics g = getBufferStrategy().getDrawGraphics();	
 		g.setColor(ColorBox.backGround.getBackground());
 		
 		if (mouseHasMoved(e))
    		g.fillRect(e.getX(),e.getY(),15,15);
    	
    	repaint();
	}
 	
 	/** Paint brush with different brushes like a square and a circle. **/
 	public void brushOperation(MouseEvent e)
 	{
 		setCursor(new Cursor(Cursor.HAND_CURSOR));
 		Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
    	
    	// depending on which brush is selected it will change the brush to that shape
 		if (mouseHasMoved(e))
 			if(extraOperations == OVAL)
 				g.fillOval(e.getX(),e.getY(),radius,radius);
    		else if(extraOperations == RECT)
    			g.fillRect(e.getX(),e.getY(),radius,radius);		
    	repaint();
	}
 	
 	/** Creates a pen when can be operated when mouse is dragged. **/
 	public void penOperation(MouseEvent e)
 	{
 		setCursor(new Cursor(Cursor.HAND_CURSOR));
 		Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
		
		// draws lines while your mouse is dragged
 		if (mouseHasMoved(e))
    	{
       		mouseX = e.getX();
       		mouseY = e.getY();
      
       		g.drawLine(prevX,prevY,mouseX,mouseY);
	
	 		prevX = mouseX;
       		prevY = mouseY;
    	} 		
 		repaint();
 	}
 	
 	/** Calls the line operation first and then starts the polygon operation **/
 	public void prePolyOperation(MouseEvent e)
 	{
 		if(!polyStart)
 		{
	 		lineOperation(e);
    		polyStart = true;
 		}
		polyOperation(e);	
 	}
 	
 	/** Starts a polygon shape by drawing many line. **/
 	public void polyOperation(MouseEvent e)
 	{
 		setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
	   	Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
		
		// draws a line on the screen
		if(mouseHasMoved(e))
    	{
			mouseX = e.getX();
	       	mouseY = e.getY(); 

	       	g.drawLine(prevX, prevY, mouseX, mouseY);

	       	prevX = mouseX;
	       	prevY = mouseY;
      	}
		
		// sets the polygon operation to false if the line is back at the origin
		if(isBeginning(e))
			polyStart = false;
			
		repaint();
 	}
 	
 	/** Helper method for the polygon to check if the line drawn is back at the origin. **/
 	public boolean isBeginning(MouseEvent e)
 	{
 		// returns true or false if the range is five away from the beginning 
 		return (Math.abs(originX-e.getX()) <= 5 && Math.abs(originY-e.getY()) <= 5);
 	}
 	
 	/** Checks if the polygon operation is deselected and stops it. **/
 	public void checkPolyOff()
 	{
 		// if the polygon operation is on and the polygon tool is not selected then 
 		// finish the line if it wasnt completed and reset the polygon operation
 		if(polyStart && ToolBox.toolSelected != ToolBox.POLYGON)
		{
			Graphics g  = getBufferStrategy().getDrawGraphics();
			g.setColor(drawColor);
    		g.drawLine(prevX,prevY,originX,originY);
			polyStart = false;
		}
 	}
 	
 	/** Checks if the curve line tool is deselected. **/
 	public void checkCurveOff()
 	{
 		// if the tool is deselected then reset variables to default
 		if(ToolBox.toolSelected != ToolBox.CURVELINE)
			curveMade = curveMade2 = false;
	}
	
	/** Starts the curve line tool. **/
	public void curveLineOperation(MouseEvent e)
 	{
 		Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
	
		if(mouseHasMoved(e))
		{
	   		mouseX = e.getX();
	       	mouseY = e.getY();
	       	
	       	Graphics2D g2 = (Graphics2D)g;
	  		
	  		// if the first and second point are not choosen then draw a straight line
	  		if(!curveMade && !curveMade2)
	  		{	
	  			c = new CubicCurve2D.Float(); 
	  			p1 = new Point2D.Float();
	  			p2 = new Point2D.Float();
	  			p3 = new Point2D.Float();
	  			p4 = new Point2D.Float();
	  			p1.setLocation(originX, originY);
	  			p4.setLocation(e.getX(), e.getY());
	 	 		curveMade = true;
	  			g.drawLine(originX,originY,e.getX(),e.getY());
	  			repaint();
	  		}
	  		// once one of the curves are made then you select a point
	  		else if(curveMade && !curveMade2)
	  		{
	  			pasteFromResize();
	  			p2.setLocation(e.getX(), e.getY());
	  			p3.setLocation(e.getX(), e.getY());
	  			curveMade2 = true;
	  			c.setCurve(p1, p2, p3, p4);
	  			g2.draw(c);
	  			repaint();
	  		}
	  		// once both points are choosen then the curve is drawn onto the screen
	  		else if(curveMade2 && curveMade)
	  		{
	  			pasteFromResize();
	  			p3.setLocation(e.getX(), e.getY());
	  			c.setCurve(p1, p2, p3, p4);
	  			g2.draw(c);
	  			curveMade = false;
	  			curveMade2 = false;
	  		}
	  	}	
	    repaint();
 	}
 	
 	/** Changes the foreground color to the selected color. **/
 	public void eyeDropperOperation(MouseEvent e)
 	{
 		// creates a screen shot and takes the pixel color where the mouse is situated
 		try{
 			Robot robot = new Robot();
 			ColorBox.foreGround.setBackground(robot.getPixelColor(ControlClass.xCoord+e.getX(),ControlClass.yCoord+e.getY()));
 		}catch(AWTException awt){}
 	}
 	
 	/** Creates a line from two points. **/
 	public void lineOperation(MouseEvent e)
	{
		setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
	   	Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
			
	    if (mouseHasMoved(e))
	    {
	      	mouseX = e.getX();
	       	mouseY = e.getY(); 
			
			g.drawLine(originX, originY, mouseX, mouseY);
	     	
	     	prevX = mouseX;
	      	prevY = mouseY;
	    }
	    pasteFromResize();
	    g.drawLine(originX, originY, mouseX, mouseY);
		repaint();
	}
	
	/** 
	 * Draw a rectangle from two given points. Draws a fill operation, a outlined
	 * rectangle or a outlined with fill operation.
	 **/
	public void drawRectangle(MouseEvent e)
	{
	  	Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
		
		// gets the mouse coordinate and gets the width and height
		mouseX = e.getX();
    	mouseY = e.getY();
		int width = mouseX-originX;
		int height = mouseY-originY;
		
		// draws a filled rectangle
		if(extraOperations == FILL)
		{
			if(width > 0 && height > 0)
				g.fillRect(originX,originY,width,height);
			else if(width < 0 && height < 0)
				g.fillRect(originX+width,originY+height,Math.abs(width),Math.abs(height));	
			else if(width < 0 && height > 0)
				g.fillRect(originX+width,originY,Math.abs(width),height);
			else if(width > 0 && height < 0)
				g.fillRect(originX,originY+height,width,Math.abs(height));
		}
		// draws a normal rectangle
		else if(extraOperations == NORMAL)
		{
			if(width > 0 && height > 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRect(originX+i,originY+i,width-(i*2),height-(i*2));	
			else if(width < 0 && height < 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRect(originX+width+i,originY+height+i,Math.abs(width)-(i*2),Math.abs(height)-(i*2));	
			else if(width < 0 && height > 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRect(originX+width+i,originY+i,Math.abs(width)-(i*2),height-(i*2));
			else if(width > 0 && height < 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRect(originX+i,originY+height+i,width-(i*2),Math.abs(height)-(i*2));
		}
		// draws a filled rectangle with an outline
		else if(extraOperations == OUTLINE)
		{
			if(width > 0 && height > 0)
			{
				g.fillRect(originX,originY,width,height);
				g.setColor(oppositeColor);
				g.fillRect(originX+lineWidth,originY+lineWidth,width-(lineWidth*2),height-(lineWidth*2));
			}
			else if(width < 0 && height < 0)
			{
				g.fillRect(originX+width,originY+height,Math.abs(width),Math.abs(height));	
				g.setColor(oppositeColor);
				g.fillRect(originX+width+lineWidth,originY+height+lineWidth,Math.abs(width)-(lineWidth*2),Math.abs(height)-(lineWidth*2));
			}
			else if(width < 0 && height > 0)
			{
				g.fillRect(originX+width,originY,Math.abs(width),height);
				g.setColor(oppositeColor);
				g.fillRect(originX+width+lineWidth,originY+lineWidth,Math.abs(width)-(lineWidth*2),height-(lineWidth*2));
			}
			else if(width > 0 && height < 0)
			{
				g.fillRect(originX,originY+height,width,Math.abs(height));
				g.setColor(oppositeColor);
				g.fillRect(originX+lineWidth,originY+height+lineWidth,width-(lineWidth*2),Math.abs(height)-(lineWidth*2));
			}
		}
	}
	
	/** Calls the drawRectangle operation. **/
	public void rectOperation(MouseEvent e)
 	{
 		setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));

	 	if (mouseHasMoved(e))
    		drawRectangle(e);
   
      	pasteFromResize();
   		drawRectangle(e);
      	repaint();
 	}
	
	/** This method draws a rectangle with rounded edges. **/
	public void drawRoundRectangle(MouseEvent e)
	{
		Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
		
		// gets the mouse coordinate and gets the width and height
		mouseX = e.getX();
	    mouseY = e.getY();
    	int width = mouseX-originX;
		int height = mouseY-originY;
		
		// draws a filled rounded rectangle
		if(extraOperations == FILL)
		{
			if(width > 0 && height > 0)
				g.fillRoundRect(originX,originY,width,height,25,25);
			else if(width < 0 && height < 0)
				g.fillRoundRect(originX+width,originY+height,Math.abs(width),Math.abs(height),25,25);	
			else if(width < 0 && height > 0)
				g.fillRoundRect(originX+width,originY,Math.abs(width),height,25,25);
			else if(width > 0 && height < 0)
				g.fillRoundRect(originX,originY+height,width,Math.abs(height),25,25);
		}
		// draws a normal rounded rectangle
		else if(extraOperations == NORMAL)
		{
			if(width > 0 && height > 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRoundRect(originX+i,originY+i,width-(i*2),height-(i*2),25,25);
			else if(width < 0 && height < 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRoundRect(originX+width+i,originY+height+i,Math.abs(width)-(i*2),Math.abs(height)-(i*2),25,25);	
			else if(width < 0 && height > 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRoundRect(originX+width+i,originY+i,Math.abs(width)-(i*2),height-(i*2),25,25);
			else if(width > 0 && height < 0)
				for(int i=0;i<lineWidth;i++)
					g.drawRoundRect(originX+i,originY+height+i,width-(i*2),Math.abs(height)-(i*2),25,25);
		}
		// draws a filled rounded rectangle with an outline
		else if(extraOperations == OUTLINE)
		{
			if(width > 0 && height > 0)
			{
				g.fillRoundRect(originX,originY,width,height,25,25);
				g.setColor(oppositeColor);
				g.fillRoundRect(originX+lineWidth,originY+lineWidth,width-(lineWidth*2),height-(lineWidth*2),25,25);
			}
			else if(width < 0 && height < 0)
			{
				g.fillRoundRect(originX+width,originY+height,Math.abs(width),Math.abs(height),25,25);	
				g.setColor(oppositeColor);
				g.fillRoundRect(originX+width+lineWidth,originY+height+lineWidth,Math.abs(width)-(lineWidth*2),Math.abs(height)-(lineWidth*2),25,25);	
			}
			else if(width < 0 && height > 0)
			{
				g.fillRoundRect(originX+width,originY,Math.abs(width),height,25,25);
				g.setColor(oppositeColor);
				g.fillRoundRect(originX+width+lineWidth,originY+lineWidth,Math.abs(width)-(lineWidth*2),height-(lineWidth*2),25,25);
			}
			else if(width > 0 && height < 0)
			{
				g.fillRoundRect(originX,originY+height,width,Math.abs(height),25,25);
				g.setColor(oppositeColor);
				g.fillRoundRect(originX+lineWidth,originY+height+lineWidth,width-(lineWidth*2),Math.abs(height)-(lineWidth*2),25,25);
			}
		}
	}
	
	/** Calls the drawRoundRectangle operation. **/
	public void roundOperation(MouseEvent e)
	{
		setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
		   	
		if (mouseHasMoved(e))
    		drawRoundRectangle(e);	
	
	 	pasteFromResize();
	 	drawRoundRectangle(e);
	 	repaint();
	}
	
	/** Draws an oval onto the screen. **/
	public void drawOval(MouseEvent e)
	{
	 	Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.setColor(drawColor);
	
	  	// gets the mouse coordinate and gets the width and height
		mouseX = e.getX();
	    mouseY = e.getY();
    	int width = mouseX-originX;
		int height = mouseY-originY;
		
		// draws a filled oval
		if(extraOperations == FILL)
		{
			if(width > 0 && height > 0)
				g.fillOval(originX,originY,width,height);
			else if(width < 0 && height < 0)
				g.fillOval(originX+width,originY+height,Math.abs(width),Math.abs(height));	
			else if(width < 0 && height > 0)
				g.fillOval(originX+width,originY,Math.abs(width),height);
			else if(width > 0 && height < 0)
				g.fillOval(originX,originY+height,width,Math.abs(height));
		}
		// draws a normal oval
		else if(extraOperations == NORMAL)
		{
			if(width > 0 && height > 0)
				for(int i=0;i<lineWidth;i++)
					g.drawOval(originX+i,originY+i,width-(i*2),height-(i*2));
			else if(width < 0 && height < 0)
				for(int i=0;i<lineWidth;i++)
					g.drawOval(originX+width+i,originY+height+i,Math.abs(width)-(i*2),Math.abs(height)-(i*2));	
			else if(width < 0 && height > 0)
				for(int i=0;i<lineWidth;i++)
					g.drawOval(originX+width+i,originY+i,Math.abs(width)-(i*2),height-(i*2));
			else if(width > 0 && height < 0)
				for(int i=0;i<lineWidth;i++)
					g.drawOval(originX+i,originY+height+i,width-(i*2),Math.abs(height)-(i*2));
		}
		// draws a filled oval with an outline
		else if(extraOperations == OUTLINE)
		{
			if(width > 0 && height > 0)
			{
				g.fillOval(originX,originY,width,height);
				g.setColor(oppositeColor);
				g.fillOval(originX+lineWidth,originY+lineWidth,width-(lineWidth*2),height-(lineWidth*2));
			}
			else if(width < 0 && height < 0)
			{
				g.fillOval(originX+width,originY+height,Math.abs(width),Math.abs(height));	
				g.setColor(oppositeColor);
				g.fillOval(originX+width+lineWidth,originY+height+lineWidth,Math.abs(width)-(lineWidth*2),Math.abs(height)-(lineWidth*2));	
			}
			else if(width < 0 && height > 0)
			{
				g.fillOval(originX+width,originY,Math.abs(width),height);
				g.setColor(oppositeColor);
				g.fillOval(originX+width+lineWidth,originY+lineWidth,Math.abs(width)-(lineWidth*2),height-(lineWidth*2));
			}
			else if(width > 0 && height < 0)
			{
				g.fillOval(originX,originY+height,width,Math.abs(height));
				g.setColor(oppositeColor);
				g.fillOval(originX+lineWidth,originY+height+lineWidth,width-(lineWidth*2),Math.abs(height)-(lineWidth*2));
			}
		}
	}
 
 	/** Calls the drawOval operation. **/
 	public void ovalOperation(MouseEvent e)
 	{
 		setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
			   	
		if (mouseHasMoved(e))
  			drawOval(e);
  		
  		pasteFromResize();
  		drawOval(e);
	 	repaint();
	}
	
	/** Drag Brush Operation. You draw a shape around as your brush. **/
	public void dragOperation(MouseEvent e)
	{
		Graphics g  = getBufferStrategy().getDrawGraphics();
		g.setColor(drawColor);
	
		if (mouseHasMoved(e))
 			if(extraOperations == LINE)
	    		g.drawLine(e.getX(),e.getY(),e.getX()+radius,e.getY()-radius); 	
			else if(extraOperations == RECT)
				g.drawRect(e.getX(),e.getY(),radius,radius);
			else if(extraOperations == OVAL)
				g.drawOval(e.getX(),e.getY(),radius,radius);
		repaint();
	}
	
	/** Spray brush tool. Draws many tiny dots within a set radius. **/
	public void sprayOperation(MouseEvent e)
	{
		Graphics g  = getBufferStrategy().getDrawGraphics();
		g.setColor(drawColor);
		// sets random points to the origin and draws them onto the screen
		for(int i=0;i<radius;i++)
		{
			setRandomPts(e);
			g.fillOval(originX,originY,1,1);
			repaint();
		}
	}
	
	/** Gets random points that are within a circle with a certain radius. **/
	public void setRandomPts(MouseEvent e)
	{
		boolean valid = false;
		do{
			originX = randInt(e.getX()-radius,e.getX()+radius);
			originY = randInt(e.getY()-radius,e.getX()+radius);
			if(Math.pow(e.getX()-originX,2)+Math.pow(e.getY()-originY,2) <= Math.pow(radius,2))
				valid = true;
		}while(!valid);
	}
	
	/** Gets a random integer within a set bound **/
	public int randInt(int start, int end)
	{
		return (int)(Math.random()*Math.abs(end-start)+start);	
	}
	
	/** Draws a clear white rectangle onto the screen. **/
	public void clearImage()
	{
		Graphics g  = getBufferStrategy().getDrawGraphics();
    	g.clearRect(0,0,getWidth(),getHeight());
		repaint();
	}
	
 	/** Set these variables with the values of the mouse coordinates. **/
 	public void setFieldDefaults(MouseEvent e)
 	{
    	prevX    = e.getX();
    	prevY    = e.getY();
    	originX  = e.getX();
    	originY  = e.getY();    	
 	}
 	
 	/** Checks if the mouse has moved. **/
	public boolean mouseHasMoved(MouseEvent e)
	{
		return (mouseX != e.getX() || mouseY != e.getY());
 	}
 	
 	/** 
 	 * If the curve is created and there is a focus then save and paste the image.
 	 * When the mouse is clicked set the drawColor and the oppositeColor. Check 
 	 * if the polygon operation and the curve tool is interrupted.
 	 **/
 	public void mousePressed(MouseEvent e)
 	{	

 		if(e.getButton() == e.BUTTON1)
 		{
 			drawColor = ColorBox.foreGround.getBackground();
			oppositeColor = ColorBox.backGround.getBackground();
		}
		if(e.getButton() == e.BUTTON3)
		{
			drawColor = ColorBox.backGround.getBackground();
			oppositeColor = ColorBox.foreGround.getBackground();
		}	
 		checkPolyOff();
 		checkCurveOff();
 	}
 	
 	/**
 	 * This method controls which operation is being performed when the mouse 
 	 * is released.
 	 **/
 	public void mouseReleased(MouseEvent e)
 	{
 		int currentTool = ToolBox.toolSelected;
 		
 		if(currentTool == ToolBox.POLYGON)
 			prePolyOperation(e);
  		else if(currentTool == ToolBox.CURVELINE)
 			curveLineOperation(e);
 		else if(currentTool == ToolBox.TEXT)
 			textOperation(e);
 		else if(currentTool == ToolBox.SELECT)
 			selectOperation(e);
 		else if(currentTool == ToolBox.FILL)
 			fillOperation(e);
 		else if(currentTool == ToolBox.RANDOMDRAW)
  			randomDraw();
     }
     
 	/**
 	 * This method controls which oepration is being performed when the mouse is 
 	 * being dragged.
 	 **/
 	public void mouseDragged(MouseEvent e)
	{	
		dragSave = true;
		
		int currentTool = ToolBox.toolSelected;
 		
 		if(currentTool == ToolBox.ERASER)
 			eraserOperation(e);
		else if(currentTool == ToolBox.PENCIL)
			penOperation(e);
		else if(currentTool == ToolBox.BRUSH)
			brushOperation(e);
		else if(currentTool == ToolBox.EYEDROPPER)
 			eyeDropperOperation(e);
 		else if(currentTool == ToolBox.SPRAY)
 			sprayOperation(e);
 		else if(currentTool == ToolBox.LINE)
 			lineOperation(e);
 		else if(currentTool == ToolBox.RECTANGLE)
 			rectOperation(e);
 		else if(currentTool == ToolBox.OVAL)
 			ovalOperation(e);
 		else if(currentTool == ToolBox.ROUNDRECTANGLE)
 			roundOperation(e);
 		else if(currentTool == ToolBox.DRAG)
 			dragOperation(e);
 	} 
 	
	/** Invoked when the mouse button has been clicked (pressed and released) on a component. **/
	public void mouseClicked(MouseEvent e)
	{
		// when the drag is released then undo the save.
		dragSave = false;
	}
	
	/** Invoked when the mouse enters a component.**/
	public void mouseEntered(MouseEvent e){}     
 	/** Invoked when the mouse exits a component. **/
	public void mouseExited(MouseEvent e){} 
 	/** If it is not the polygon operation then call setFieldDefaults. **/
 	public void mouseMoved(MouseEvent e)
 	{
 		if(!polyStart)
 			setFieldDefaults(e);
 	}
 	/** If the focus is lost then do not save the screen. **/
 	public void focusLost(FocusEvent e){save = false;}
 	/** If the focus is found then save the screen. **/
	public void focusGained(FocusEvent e){save = true;}
 }