
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JPanel;


/**
 * This class creates a draw area where the actual drawing takes place in paint.
 *
 * @author Bryce Borchers.
 *         Created Mar 27, 2008.
 */
public class DrawArea extends JPanel implements MouseListener,
		MouseMotionListener{
	
	/*
	 * 
	 * DrawArea Fields
	 * 
	 */
	
	/**
	 * This array keeps track of all the shapes drawn so far.
	 */
	protected static Shape[] shapeArray = new Shape[100000];
	
	/**
	 * This array keeps track of each corresponding color of each shape drawn.
	 */
	protected static Color[] colorArray = new Color[100000];
	
	/**
	 * This array keeps track if we fill or draw each shape drawn.
	 */
	protected static boolean[] fill = new boolean[100000];
	
	/**
	 * This array keeps track of the stroke size of each shape drawn.
	 */
	protected static BasicStroke[] strokeArray = new BasicStroke[100000];
	
	/**
	 * This is a count of how many lines have been drawn on a specific polygon.
	 */
	protected static int polyCount = 0;
	
	private int count = 0;
	private int PermY;
	private int PermX;
	private boolean isShift = false;
	private boolean isDragging = false;
	private int x = -10;
	private int y = -10;

	/*----------------------------------------------------------------------------------------------------*/
	
	/*
	 * 
	 * Constructor
	 * 
	 */

	/**
	 * Constructs a new draw area and allows draw area to listen to itself.
	 */
	public DrawArea() {
		this.setBackground(Color.WHITE);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
	}
	
	/*----------------------------------------------------------------------------------------------------*/
	
	/*
	 * 
	 * Methods
	 * 
	 */

	public void mouseClicked(MouseEvent e) {
		// Don't do anything.
	}

	public void mouseEntered(MouseEvent e) {
		// Don't do anything.
	}

	public void mouseExited(MouseEvent e) {
		// Don't do anything.
	}

	public void mousePressed(MouseEvent e) {
		this.x = e.getX();
		this.y = e.getY();
		this.PermX = e.getX();
		this.PermY = e.getY();
		this.repaint();
		this.isShift = e.isShiftDown();
		DrawArea.polyCount++;
		this.count++;
	}

	public void mouseReleased(MouseEvent e) {
		// Don't do anything.
	}

	public void mouseDragged(MouseEvent e) {
		this.x = e.getX();
		this.y = e.getY();
		this.repaint();
		this.isShift = e.isShiftDown();
		this.isDragging = true;
	}

	public void mouseMoved(MouseEvent e) {
		this.x = e.getX();
		this.y = e.getY();
		this.isShift = e.isShiftDown();
		if (DrawTool.drawTool == "Polygon") {
			this.repaint();
		}
	}
	
	
	/*
	 * 
	 * The majority of the code is executed inside the paintComponent.  To manage 
	 * drawing different shapes and effects based on the current draw tool we used
	 * a series of if/else if statments.  Inside these statments we update the four
	 * arrays that kept track of each shape that needs to be drawn and its
	 * properties.  Lastly each shape in the shape array is drawn.
	 * 
	 */
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;
		DrawArea.strokeArray[this.count] = new BasicStroke(1);
		
		if (DrawTool.drawTool == "Pencil") { // Implement the pencil draw tool.
			
			if (this.isDragging == false) {
				Ellipse2D.Double pencil = new Ellipse2D.Double(
						this.x - 1, this.y - 1, 2, 2);
				DrawArea.shapeArray[this.count] = pencil;
				DrawArea.colorArray[this.count] = ColorTool.drawColor;
				DrawArea.fill[this.count] = true;
				this.count++;
			} else {
				Line2D.Double line = new Line2D.Double(
						this.PermX,
						this.PermY,
						this.x,
						this.y);
				DrawArea.shapeArray[this.count] = line;
				DrawArea.colorArray[this.count] = ColorTool.drawColor;
				DrawArea.fill[this.count] = false;
				this.count++;
				this.PermX = this.x;
				this.PermY = this.y;
				this.isDragging = false;
			}
			
		} else if (DrawTool.drawTool == "Brush") { // Implement the brush draw tool.
			
			if (this.isDragging == false) {
				Rectangle2D.Double brush = new Rectangle2D.Double(
						this.x - SliderPanel.stroke/2.0,
						this.y - SliderPanel.stroke/2.0,
						SliderPanel.stroke,
						SliderPanel.stroke);
				DrawArea.shapeArray[this.count] = brush;
				DrawArea.colorArray[this.count] = ColorTool.drawColor;
				DrawArea.fill[this.count] = true;
				this.count++;
			} else {
				Line2D.Double line = new Line2D.Double(
						this.PermX,
						this.PermY,
						this.x,
						this.y);
				DrawArea.shapeArray[this.count] = line;
				DrawArea.colorArray[this.count] = ColorTool.drawColor;
				DrawArea.fill[this.count] = false;
				DrawArea.strokeArray[this.count] = new BasicStroke(SliderPanel.stroke);
				this.count++;
				this.PermX = this.x;
				this.PermY = this.y;
				this.isDragging = false;
			}
			
		} else if (DrawTool.drawTool == "Rectangle") { // Implement the rectangle draw tool.
			
			Rectangle2D.Double rectangle;
			if(this.isShift == false) {
				rectangle = new Rectangle2D.Double(
						this.PermX < this.x ? this.PermX: this.x,
						this.PermY < this.y ? this.PermY: this.y,
						Math.abs(this.x - this.PermX),
						Math.abs(this.y - this.PermY));
			} else {
				rectangle = new Rectangle2D.Double(
						this.PermX < this.x ? this.PermX : this.x,
						this.PermY < this.y ? this.PermY : this.y,
						Math.abs(this.x - this.PermX),
						Math.abs(this.x - this.PermX));
			}				
						
			DrawArea.shapeArray[this.count] = rectangle;
			DrawArea.colorArray[this.count] = ColorTool.drawColor;
			DrawArea.strokeArray[this.count] = new BasicStroke(SliderPanel.stroke / 2);
			DrawArea.fill[this.count] = false;
			
		} else if (DrawTool.drawTool == "Oval") { // Implement the oval draw tool.
			
			Ellipse2D.Double oval;
			if(this.isShift == false) {
				oval = new Ellipse2D.Double(
						this.PermX < this.x ? this.PermX : this.x,
						this.PermY < this.y ? this.PermY : this.y,
						Math.abs(this.x - this.PermX),
						Math.abs(this.y - this.PermY));
			} else {
				oval = new Ellipse2D.Double(
						this.PermX < this.x ? this.PermX : this.x,
						this.PermY < this.y ? this.PermY : this.y,
						Math.abs(this.x - this.PermX),
						Math.abs(this.x - this.PermX));
			}
			
			DrawArea.shapeArray[this.count] = oval;
			DrawArea.colorArray[this.count] = ColorTool.drawColor;
			DrawArea.strokeArray[this.count] = new BasicStroke(SliderPanel.stroke / 2);
			DrawArea.fill[this.count] = false;
			
		} else if (DrawTool.drawTool == "Polygon") { // Implement the polygon draw tool.
			
			if (DrawArea.polyCount > 0) {
				if (this.isShift == false) {
					Line2D.Double line = new Line2D.Double(
							this.PermX,
							this.PermY,
							this.x,
							this.y);
					DrawArea.shapeArray[this.count] = line;
					DrawArea.colorArray[this.count] = ColorTool.drawColor;
					DrawArea.fill[this.count] = false;
				} else {
					Line2D.Double line = new Line2D.Double(
							((Line2D.Double) DrawArea.shapeArray[this.count - DrawArea.polyCount + 1]).getX1(),
							((Line2D.Double) DrawArea.shapeArray[this.count - DrawArea.polyCount + 1]).getY1(),
							((Line2D.Double) DrawArea.shapeArray[this.count - 1]).getX2(),
							((Line2D.Double) DrawArea.shapeArray[this.count - 1]).getY2());
					DrawArea.shapeArray[this.count] = line;
					DrawArea.colorArray[this.count] = ColorTool.drawColor;
					DrawArea.fill[this.count] = false;
					DrawArea.polyCount = 0;
					this.count++;
				}
			}
			
		} else if (DrawTool.drawTool == "Spray Paint") { // Implement the spray paint draw tool.
			
			double randx;
			double randy;
			randx = (Math.random() - 0.5) * SliderPanel.stroke * 2;
			randy = (Math.random() - 0.5) * SliderPanel.stroke * 2;
			Ellipse2D.Double spray = new Ellipse2D.Double(
					this.x - 1 + randx, this.y - 1 + randy, 2, 2);
			DrawArea.shapeArray[this.count] = spray;
			DrawArea.colorArray[this.count] = ColorTool.drawColor;
			DrawArea.fill[this.count] = true;
			this.count++;
			
		} else if (DrawTool.drawTool == "Eraser") { // Implement the eraser draw tool.
			
			if (this.isDragging == false) {
				Rectangle2D.Double brush = new Rectangle2D.Double(
						this.x - SliderPanel.stroke/2.0,
						this.y - SliderPanel.stroke/2.0,
						SliderPanel.stroke,
						SliderPanel.stroke);
				DrawArea.shapeArray[this.count] = brush;
				DrawArea.colorArray[this.count] = Color.WHITE;
				DrawArea.fill[this.count] = true;
				this.count++;
			} else {
				Line2D.Double line = new Line2D.Double(
						this.PermX,
						this.PermY,
						this.x,
						this.y);
				DrawArea.shapeArray[this.count] = line;
				DrawArea.colorArray[this.count] = Color.WHITE;
				DrawArea.fill[this.count] = false;
				DrawArea.strokeArray[this.count] = new BasicStroke(SliderPanel.stroke);
				this.count++;
				this.PermX = this.x;
				this.PermY = this.y;
				this.isDragging = false;
			}
			
		} else if (DrawTool.drawTool == "Undo") { // Implement the undo draw tool.
			
			DrawArea.shapeArray[this.count] = null;
			if(this.count > 0)
				this.count--;		
			
		} else {  // Used as a test to make sure out if statments were working correctly.
			
			System.out.println("This should never print.");
		}
		
		
		// Loop through the shape array and draw each shape according to color, fill, and stroke.
		for(int i=0; i<=this.count; i++) {
			if (DrawArea.shapeArray[i] == null) {
				//Do Nothing;
			} else{
				g2.setColor(DrawArea.colorArray[i]);
				g2.setStroke(DrawArea.strokeArray [i]);
				if (DrawArea.fill[i] == true) {
					g2.fill(DrawArea.shapeArray[i]);
				} else {
					g2.draw(DrawArea.shapeArray[i]);
				}
			}
		}
	}
}
