import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;


	 /**	 
	 * Canvas for user to create images on.
	 * @author Eric Schulte & Jacob Wise
	 *         Created Apr 12, 2008.
	 */
	public class PaintCanvas extends JPanel implements MouseListener, 
	  MouseMotionListener, ActionListener {

		/**
		 * Paint Canvas is the canvas in which everything is drawn
		 *
		 */

		private boolean pencilActive = true;
		private boolean lineActive = true;
		private boolean paintBrushActive = true;
		private boolean rectangleActive = true;
		private boolean ovalActive = true;
		private boolean filledRectangleActive = true;
		private boolean filledOvalActive = true;
		private boolean polygonActive = true;
		private boolean sprayCanActive = true;
		private boolean eraserActive = true;
	
		
		private int currentx = 0;
		private int currenty = 0;
		private int previousx = 0;
		private int previousy = 0;
		
		private int originx = 0;
		private int originy = 0;
		private int originWidth = 0;
		private int originHeight = 0;
		private int drawX = 0;
		private int drawY = 0;
		
		private int brushLength = 10;
		private ToolBar toolBar ;
		private ColorBar colorBar;
	
		
	 /**
	 * Constructs canvas.
	 * @param t 
	 * @param c 
	 */
	public PaintCanvas(ToolBar t, ColorBar c) {
		this.toolBar = t;
		this.colorBar = c;
		this.setBackground(Color.WHITE);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);  
	  }
	
	

		/**
		 * Determines if mouse has moved from previous position 
		 * @param e 
		 * @return whether or not the mouse has moved
		 */
		public boolean mouseHasMoved(MouseEvent e) {
			return (this.currentx != e.getX() || this.currenty != e.getY());
		}

		public void mouseClicked(MouseEvent e) {
			/* Update current mouse coordinates to screen */
			updateMouseCoordinates(e);
			
			switch (this.toolBar.getOperation())	{
				case 2:
					paintBrushAction(e);
					break;
				case 7:
					polygonAction(e);
					break;
				case 8:
					sprayCanAction(e);
					break;
			}
		}

		/**
		 * Calls end routine for current operation.
		 * @param e 
		 */
		public void mouseReleased(MouseEvent e) {
			/* Update current mouse coordinates to screen */
			updateMouseCoordinates(e);
			
			switch (this.toolBar.getOperation()) {
				case 0:
					releasedPencil();
					break;
				case 1:
					releasedLine();
					break;
				case 2:
					releasedPaintBrush();
					break;
				case 3:
					releasedRectangle();
					break;
				case 4:
					releasedFilledRectangle();
					break;
				case 5:
					releasedOval();
					break;
				case 6:
					releasedFilledOval();
					break;
				case 7:
					releasedPolygon(e);
					break;
				case 8:
					releasedSprayCan();
					break;
				case 9:
					releasedEraser();
					break;
			}
		}
				
		/**
		 * Communicates when the eraser is released
		 *
		 */
		private void releasedEraser() {
			this.eraserActive = true;
			Graphics g = this.getGraphics();
			g.setColor(Color.WHITE);
			g.fillRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
					this.brushLength * 2, this.brushLength * 2);
		}

		/**
		 * Not used
		 *
		 */
		private void releasedSprayCan() {
			// empty //
		}

		/**
		 * Not used
		 */
		private void releasedPolygon(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
					this.polygonActive = true;
					Graphics g = this.getGraphics();
					
					g.setColor(this.colorBar.getColor());
					g.drawLine(this.drawX, this.drawY, this.currentx, this.currenty);
				}
		}

		/**
		 * Communicates when the filled oval is released
		 *
		 */
		private void releasedFilledOval() {
			this.filledOvalActive = true;
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());
			g.fillOval(this.drawX, this.drawY, this.originWidth, this.originHeight);
			
		}

		/**
		 * Communicates when the oval is released
		 *
		 */
		private void releasedOval() {
			this.ovalActive = true;
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());
			g.drawOval(this.drawX, this.drawY, this.originWidth, this.originHeight);
		}

		/**
		 * Communicates when the filled rectangle is released
		 *
		 */
		private void releasedFilledRectangle() {
			
			this.filledRectangleActive = true;
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());
			g.fillRect(this.drawX, this.drawY, this.originWidth, this.originHeight);
			
		}

		/**
		 * communicates when the rectangle is released
		 *
		 */
		private void releasedRectangle() {
			this.rectangleActive = true;
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());
			g.drawRect(this.drawX, this.drawY, this.originWidth, this.originHeight);
			
		}
		
		private void releasedPaintBrush() {
			this.paintBrushActive = true;
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());
			g.fillRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
					this.brushLength * 2, this.brushLength);
			
		}

		public void mouseDragged(MouseEvent e) {
			updateMouseCoordinates(e);
			
			switch (this.toolBar.getOperation()) {
				case 0: 
					pencilAction(e);
					break;
				
				case 1: 
					lineAction(e);
					break;
	
				case 2:
					paintBrushAction(e);
					break;
	
				case 3:
					rectangleAction(e);
					break;
	
				case 4:
					filledRectangleAction(e);
					break;
	
				case 5:
					ovalAction(e);
					break;
	
				case 6:
					filledOvalAction(e);
					break;
					
				case 7:
					polygonAction(e);
					break;
					
				case 8:
					sprayCanAction(e);
					break;
					
				case 9:
					eraserAction(e);
					break;
			}
		}

		/**
		 * communicates eraser action
		 *
		 * @param e
		 */
		private void eraserAction(MouseEvent e) {
			Graphics g = this.getGraphics();

			/*
			 In initial state setup default values
			 for mouse coordinates
			 */
			if (this.eraserActive) {
				setGraphicalDefaults(e);
				this.eraserActive = false;
				g.setColor(Color.WHITE);
				g.fillRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength * 2);
				g.setColor(Color.BLACK);
				g.drawRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength * 2);
				
				this.previousx = this.currentx;
				this.previousy = this.currenty;
			}

			/*
			 Make sure that the mouse has actually
			 moved from its previous position.
			 */
			if (mouseHasMoved(e)) {

				g.setColor(Color.WHITE);
				g.drawRect(this.previousx - this.brushLength, this.previousy - this.brushLength,
						this.brushLength * 2, this.brushLength * 2);

				/* Get current mouse coordinates */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* Draw eraser block to panel */
				g.setColor(Color.WHITE);
				g.fillRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength * 2);
				g.setColor(Color.WHITE);
				g.drawRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength * 2);
				
				this.previousx = this.currentx;
				this.previousy = this.currenty;
			}	
		}

		/**
		 * communicates spray can action
		 *
		 * @param e
		 */
		private void sprayCanAction(MouseEvent e) {
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());

			if (this.sprayCanActive) {
				setGraphicalDefaults(e);
				this.sprayCanActive = false;
				g.fillOval(this.currentx, this.currenty, this.brushLength, this.brushLength);
			}

			/* checks for mouse movement */ 
			if (mouseHasMoved(e)) {
				/* set mouse coordinates to current position */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* draws ovals at the current mouse coordinates */
				g.fillOval(this.currentx, this.currenty, this.brushLength, this.brushLength);

				this.previousx = this.currentx;
				this.previousy = this.currenty;
			}
			
		}

		/**
		 * communicates polygon action
		 *
		 * @param e
		 */
		private void polygonAction(MouseEvent e) {
			
			if (this.polygonActive) {
				this.previousx = e.getX();
				this.previousy = e.getY();
				this.drawX = e.getX();
				this.drawY = e.getY();
				this.polygonActive = false;
			}

			else {
				this.currentx = e.getX();
				this.currenty = e.getY();
				Graphics g = this.getGraphics();
				g.setColor(this.colorBar.getColor());
				g.drawLine(this.previousx, this.previousy, this.currentx, this.currenty);
				this.previousx = this.currentx;
				this.previousy = this.currenty;

			}
		}

		/**
		 * communicates filled oval action
		 *
		 * @param e
		 */
		private void filledOvalAction(MouseEvent e) {
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());

			/*
			 In initial state setup default values
			 for mouse coordinates
			 */
			if (this.filledOvalActive) {
				setGraphicalDefaults(e);
				this.filledOvalActive = false;
			}

			/*
			 Make sure that the mouse has actually
			 moved from its previous position.
			 */
			if (mouseHasMoved(e)) {
				/*
				 Delete previous rectangle shadow
				 by xor-ing the graphical object
				 */
				g.setXORMode(this.getBackground());
				g.drawOval(this.drawX, this.drawY, this.originWidth, this.originHeight);

				/* Update new mouse coordinates */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* Check new mouse coordinates for negative errors */
				setActualBoundry();

				/* Draw rectangle shadow */
				g.drawOval(this.drawX, this.drawY, this.originWidth, this.originHeight);
			}
		}

		/**
		 * communicates oval action
		 *
		 * @param e
		 */
		private void ovalAction(MouseEvent e) {
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());

			/*
			 In initial state setup default values
			 for mouse coordinates
			 */
			if (this.ovalActive) {
				setGraphicalDefaults(e);
				this.ovalActive = false;
			}

			/*
			 Make sure that the mouse has actually
			 moved from its previous position.
			 */
			if (mouseHasMoved(e)) {
				/*
				 Delete previous oval shadow
				 by xor-ing the graphical object
				 */
				g.setXORMode(this.getBackground());
				g.drawOval(this.drawX, this.drawY, this.originWidth, this.originHeight);

				/* Update new mouse coordinates */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* Check new mouse coordinates for negative errors */
				setActualBoundry();

				/* Draw oval shadow */
				g.drawOval(this.drawX, this.drawY, this.originWidth, this.originHeight);

			}
			
		}
		/**
		 * communicates filled rectangle action
		 *
		 * @param e
		 */
		private void filledRectangleAction(MouseEvent e) {
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());

			/*
			 In initial state setup default values
			 for mouse coordinates
			 */
			if (this.filledRectangleActive) {
				setGraphicalDefaults(e);
				this.filledRectangleActive = false;
			}

			/*
			 Make sure that the mouse has actually
			 moved from its previous position.
			 */
			if (mouseHasMoved(e)) {
				/*
				 Delete previous rectangle shadow
				 by xor-ing the graphical object
				 */
				g.setXORMode(this.getBackground());
				g.drawRect(this.drawX, this.drawY, this.originWidth - 1, this.originHeight - 1);

				/* Update new mouse coordinates */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* Check new mouse coordinates for negative errors */
				setActualBoundry();

				/* Draw rectangle shadow */
				g.drawRect(this.drawX, this.drawY, this.originWidth - 1, this.originHeight - 1);

			}
		}

		/**
		 * communicates rectangle action
		 *
		 * @param e
		 */
		private void rectangleAction(MouseEvent e) {
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());

			/*
			 In initial state setup default values
			 for mouse coordinates
			 */
			if (this.rectangleActive) {
				setGraphicalDefaults(e);
				this.rectangleActive = false;
			}

			/*
			 Make sure that the mouse has actually
			 moved from its previous position.
			 */
			if (mouseHasMoved(e)) {
				/*
				 Delete previous rectangle shadow
				 by xor-ing the graphical object
				 */
				g.setXORMode(this.getBackground());
				g.drawRect(this.drawX, this.drawY, this.originWidth, this.originHeight);

				/* Update new mouse coordinates */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* Check new mouse coordinates for negative errors */
				setActualBoundry();

				/* Draw rectangle shadow */
				g.drawRect(this.drawX, this.drawY, this.originWidth, this.originHeight);

			}
			
		}

		/**
		 * communicates the paintbrush action
		 *
		 * @param e
		 */
		private void paintBrushAction(MouseEvent e) {
			Graphics g = this.getGraphics();

			/*
			 In initial state setup default values
			 for mouse coordinates
			 */
			if (this.paintBrushActive) {
				setGraphicalDefaults(e);
				this.paintBrushActive = false;
				g.setColor(this.colorBar.getColor());
				g.fillRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength);
				g.setColor(this.colorBar.getColor());
				g.drawRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength);
				
				this.previousx = this.currentx;
				this.previousy = this.currenty;
			}

			/*
			 Make sure that the mouse has actually
			 moved from its previous position.
			 */
			if (mouseHasMoved(e)) {

				g.setColor(this.colorBar.getColor());
				g.drawRect(this.previousx - this.brushLength, this.previousy - this.brushLength,
						this.brushLength * 2, this.brushLength);

				/* Get current mouse coordinates */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* Draw rectangle to panel */
				g.setColor(this.colorBar.getColor());
				g.fillRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength);
				g.setColor(this.colorBar.getColor());
				g.drawRect(this.currentx - this.brushLength, this.currenty - this.brushLength,
						this.brushLength * 2, this.brushLength);
				
				this.previousx = this.currentx;
				this.previousy = this.currenty;
			}	
		}

		/**
		 * Draws a line from previous to current mouse coordinates.
		 * @param e
		 */
		private void lineAction(MouseEvent e) {
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());
			
			
			if (this.lineActive) {
				setGraphicalDefaults(e);
				g.setXORMode(this.colorBar.getColor());
				g.drawLine(this.originx, this.originy, this.currentx, this.currenty);
				this.lineActive = false;
			}

			/* check for mouse movement */
			if (mouseHasMoved(e)) {
				/*
				 Delete previous line shadow
				 by xor-ing the graphical object
				 */
				
				g.setXORMode(this.getBackground());
				g.drawLine(this.originx, this.originy, this.currentx, this.currenty);

				/* Update new mouse coordinates */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* Draw line shadow */
				g.drawLine(this.originx, this.originy, this.currentx, this.currenty);
			}
		}

		/**
		 * Helper method for line tool.
		 */
		private void releasedLine() {
			if ((Math.abs(this.originx - this.currentx) + Math.abs(this.originy - this.currenty)) != 0) {
				this.lineActive = true;
				Graphics g = this.getGraphics();
				
				g.setColor(this.colorBar.getColor());
				g.drawLine(this.originx, this.originy, this.currentx, this.currenty);
			}
		}

		/**
		 * Emulates a pen graphic by drawing line from previous to current mouse coordinates.
		 * @param e
		 */
		private void pencilAction(MouseEvent e) {
			Graphics g = this.getGraphics();
			g.setColor(this.colorBar.getColor());

			if (this.pencilActive) {
				setGraphicalDefaults(e);
				this.pencilActive = false;
				g.drawLine(this.previousx, this.previousy, this.currentx, this.currenty);
			}

			/* checks for mouse movement */ 
			if (mouseHasMoved(e)) {
				/* set mouse coordinates to current position */
				this.currentx = e.getX();
				this.currenty = e.getY();

				/* draws a line from previous to current mouse coordinates */
				g.drawLine(this.previousx, this.previousy, this.currentx, this.currenty);

				this.previousx = this.currentx;
				this.previousy = this.currenty;
			}
		}
		
		/**
		 * Helper method for pencil drawing.
		 */
		public void releasedPencil() {
			this.pencilActive = true;
		}

		public void mouseMoved(MouseEvent e) {
			updateMouseCoordinates(e);
		}
		
		public void mouseEntered(MouseEvent e) {
			updateMouseCoordinates(e);
		}

		public void mouseExited(MouseEvent e) {
			updateMouseCoordinates(e);
		}

		public void mousePressed(MouseEvent e) {
			updateMouseCoordinates(e);
		}
		
		/**
		* Calculates the new values for the
		* global varibles drawX & drawY according to
		* the new mouse coordinates.
		* This method elininates the possibility that
		* a negative width or height can be passed 
		* to the constructor.
		*/
		public void setActualBoundry() {

			/* checks for negative movement */
			if (this.currentx < this.originx || this.currenty < this.originy) {

				/* if the current mouse x coordinate is smaller
				 than the origin x coordinate,
				 equates the drawX to be the difference. */
				if (this.currentx < this.originx) {
					this.originWidth = this.originx - this.currentx;
					this.drawX = this.originx - this.originWidth;
				}

				else {
					this.drawX = this.originx;
					this.originWidth = this.currentx - this.originx;

				}

				/* if the current mouse y coordinate is smaller
				 than the origin y coordinate,
				 equates the drawY to be the difference. */
				if (this.currenty < this.originy) {
					this.originHeight = this.originy - this.currenty;
					this.drawY = this.originy - this.originHeight;
				}

				else {
					this.drawY = this.originy;
					this.originHeight = this.currenty - this.originy;
				}

			}

			/* if movement occured in a positive manner, 
			  set variables to logical amount. */
			else {
				this.drawX = this.originx;
				this.drawY = this.originy;
				this.originWidth = this.currentx - this.originx;
				this.originHeight = this.currenty - this.originy;
			}

		}
		
		/**
		 * Updates the mouse coordinates x & y values.
		 * @param e
		 */
		public void updateMouseCoordinates(MouseEvent e) {
			@SuppressWarnings("unused")
			String xCoor = "";
			@SuppressWarnings("unused")
			String yCoor = "";

			if (e.getX() < 0)
				xCoor = "0";
			else {
				xCoor = String.valueOf(e.getX());
			}

			if (e.getY() < 0)
				xCoor = "0";
			else {
				yCoor = String.valueOf(e.getY());
			}
		}
		
		/**
		 * Sets drawing variabls to current mouse position & default values.
		 * @param e
		 */
		public void setGraphicalDefaults(MouseEvent e) {
			this.currentx = e.getX();
			this.currenty = e.getY();
			this.previousx = e.getX();
			this.previousy = e.getY();
			this.originx = e.getX();
			this.originy = e.getY();
			this.drawX = e.getX();
			this.drawY = e.getY();
			this.originWidth = 0;
			this.originHeight = 0;
		}

		public void actionPerformed(ActionEvent e) {
			//empty//
		}
		/**
		 * clears the screen
		 */
		public void clearScreen(){
			this.setBackground(Color.WHITE);
			this.repaint();
		}
		
		/**
		 * Fills background to current color selected
		 */
		public void fillBackground(){
			this.setBackground(this.colorBar.getColor());
		}
		
		/**
		 * figures out what the screen size should be for a screenshot
		 *
		 * @return Rectangle screenshot
		 */
		public Rectangle screenSize(){
			Rectangle screenShot = new Rectangle();
			screenShot.setLocation(5,55);
			screenShot.setSize(this.getWidth(), this.getHeight());
			return screenShot;
		}
}