import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import javax.swing.*;


/**
 * Provides mouseActions for the drawing.
 *
 * @author Pete Brousalis.
 *         Created Apr 13, 2008.
 */
public class CanvasPanel extends JPanel implements MouseListener, MouseMotionListener, KeyListener {
	
	private final static int RECT = 1, POLY = 2, OVAL = 3, PENCIL = 4,
			BRUSH = 5, TEXT = 6, LINE = 7, ERASER = 8;

	private Color objColor, backColor;

	private ArrayList<Coordinate> arrLine, arrBox, arrOval, arrPencil, arrBrush, arrText, arrErase;
	private ArrayList<ArrayList> arrPolyList;
	private ArrayList<ArrayList> arrTextList = new ArrayList<ArrayList>();
	
	private double x1, y1, x2, y2, linex1, linex2, liney1, liney2 = 0;
	
	private Point lastPoint;
	private int PolyMode =1;
	private int polycounter = -1;
	private String textIn;
	private FontMetrics metrics;
	private String message;

	private int drawMode = 0;
	private int brushWidth;
	private Point textPoint=new Point(0,0);
	private int textcounter=0;
	
	/**
	 * Constructor
	 *
	 */
	public CanvasPanel() {
		this.arrLine = new ArrayList<Coordinate>();
		this.arrBox = new ArrayList<Coordinate>();
		this.arrOval = new ArrayList<Coordinate>();
		this.arrPencil = new ArrayList<Coordinate>();
		this.arrBrush = new ArrayList<Coordinate>();
		this.arrText = new ArrayList<Coordinate>();
		this.arrErase = new ArrayList<Coordinate>();
		this.arrPolyList = new ArrayList<ArrayList>();
		
		addMouseListener(this);
		addMouseMotionListener(this);
		addKeyListener(this);

		this.objColor = Color.BLACK;
		this.backColor = Color.WHITE;
		setBackground(this.backColor);

		setDrawMode(4);
		setBrushWidth(4);

		
		this.message = new String();
		Font textFont = new Font("Arial",Font.PLAIN,24);
		this.metrics = getFontMetrics(textFont);		
		
		setFont(textFont);
		repaint();
		
		this.setFocusable(true);
	}
	
	/**
	 * Saves the current mouse point.
	 *
	 * @param p
	 */
	public void savePoint(Point p)
	{
		this.lastPoint = p;
	}

	@SuppressWarnings("unchecked")
	public void mousePressed(MouseEvent event) {
		this.x1 = this.linex1 = this.linex2 = event.getX();
		this.y1 = this.liney1 = this.liney2 = event.getY();
		if (this.drawMode == POLY){
			if(this.PolyMode ==1){
				
				this.polycounter++;
				this.arrPolyList.add(new ArrayList<Coordinate>());
				this.PolyMode = 0;
				
			}
			if (this.PolyMode == 0){
				this.arrPolyList.get(this.polycounter).add(new Coordinate(this.x1,this.y1, this.x1,this.y1,this.objColor));
			}
			
		}
		//System.out.println(drawMode);
	}

	public void mouseReleased(MouseEvent event) {
		if (this.drawMode == LINE) {
			this.arrLine.add(new Coordinate(this.x1, this.y1, event.getX(), event.getY(),
					this.objColor));
		}
		if (this.drawMode == RECT) {
			if (this.x1 > event.getX() || this.y1 > event.getY()) {
				this.arrBox.add(new Coordinate(event.getX(), event.getY(),
						this.x1, this.y1, this.objColor));
			} else {
				this.arrBox.add(new Coordinate(this.x1, this.y1, event.getX(), event
						.getY(), this.objColor));
			}
		}
		if (this.drawMode == OVAL) {
			if (this.x1 > event.getX() || this.y1 > event.getY()) {
				this.arrOval.add(new Coordinate(event.getX(), event.getY(), this.x1, this.y1,
						this.objColor));
			} else {
				this.arrOval.add(new Coordinate(this.x1, this.y1, event.getX(), event.getY(),
						this.objColor));
			}
		}

		this.x1 = this.linex1 = this.x2 = this.linex2 = 0;
		this.y1 = this.liney1 = this.y2 = this.liney2 = 0;
	}

	public void mouseDragged(MouseEvent event) {
		this.x2 = event.getX();
		this.y2 = event.getY();

		if(this.drawMode == PENCIL) {
			this.linex1 = this.linex2;
			this.liney1 = this.liney2;
			this.linex2 = this.x2;
			this.liney2 = this.y2;

			this.arrPencil.add(new Coordinate(this.linex1, this.liney1, this.linex2, this.liney2,
					this.objColor));
		}
		
		if(this.drawMode == BRUSH) {
			this.linex1 = this.linex2;
			this.liney1 = this.liney2;
			this.linex2 = this.x2;
			this.liney2 = this.y2;

			this.arrBrush.add(new Coordinate(this.linex1, this.liney1, this.linex2, this.liney2,
					this.objColor, this.brushWidth));
		}
			
		if(this.drawMode == ERASER) {
			this.arrErase.add(new Coordinate(this.lastPoint.x, this.lastPoint.y, 17, 18,
					this.backColor));
		}
		savePoint(event.getPoint());
		this.repaint();
	}

	public void mouseEntered(MouseEvent event) {
		/*if(this.drawMode == ERASER) {
			Toolkit toolkit = Toolkit.getDefaultToolkit();
			Image image = toolkit.getImage("pencil.gif");
			Point hotSpot = new Point(7,7);
			Cursor cursor = toolkit.createCustomCursor(image, hotSpot, "Eraser");
			setCursor(cursor);
		} else*/
			setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));		
	}

	public void mouseExited(MouseEvent event) {
		setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
	}

	public void mouseClicked(MouseEvent event) {
		requestFocusInWindow(true);
		savePoint(event.getPoint());
		if(this.drawMode == TEXT){
			this.textcounter++;
		}
		this.repaint();
	}
	public void mouseMoved(MouseEvent event) {/**/}

	public void keyPressed(KeyEvent kEvent)
	{
		this.message += kEvent.getKeyChar();
    }
	
	public void keyReleased(KeyEvent kEvent){/**/}

	public void keyTyped (KeyEvent kEvent)
	{
		ArrayList<Coordinate> Text = new ArrayList<Coordinate>();
		if(this.drawMode == TEXT){
		this.textIn = String.valueOf(kEvent.getKeyChar());		
		this.getGraphics().setColor(this.objColor);
		this.getGraphics().drawString(this.textIn, this.lastPoint.x, this.lastPoint.y);
		savePoint(new Point((this.lastPoint.x + 15/*this.metrics.stringWidth(this.textIn)*/), this.lastPoint.y));
		if(this.arrTextList.size()<this.textcounter){
			this.arrTextList.add(Text);
		}
			if (this.textcounter>0){
		arrTextList.get(textcounter-1).add(new Coordinate((this.lastPoint.x), (this.lastPoint.y), 0.0, 0.0,
				this.objColor, this.message));
		this.message="";
		this.repaint();
		}

		
		}
		
	}
	
	@SuppressWarnings("unchecked")
	private void redrawObjects(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		for (int i = 0; i < this.arrPencil.size(); i++) {
			g2.setColor(this.arrPencil.get(i).color());
			Line2D.Double pencil = new Line2D.Double(this.arrPencil.get(i).getX1(), this.arrPencil.get(i).getY1(),this.arrPencil.get(i).getX2(),this.arrPencil.get(i).getY2());
			g2.draw(pencil);
		}
		for (int i = 0; i < this.arrLine.size(); i++) {
			g2.setColor(this.arrLine.get(i).color());
			Line2D.Double pencil = new Line2D.Double(this.arrLine.get(i).getX1(), this.arrLine.get(i).getY1(),this.arrLine.get(i).getX2(),this.arrLine.get(i).getY2());
			g2.draw(pencil);
		}
	  	for (int i=0;i<this.arrOval.size();i++){	
         	g2.setColor(this.arrOval.get(i).color());
         	Ellipse2D.Double oval = new Ellipse2D.Double(this.arrOval.get(i).getX1(),this.arrOval.get(i).getY1(),this.arrOval.get(i).getX2()-this.arrOval.get(i).getX1(),this.arrOval.get(i).getY2()-this.arrOval.get(i).getY1());
         	g2.draw(oval);
	  	}
	  	for (int i=0;i<this.arrBox.size();i++){
         	g2.setColor(this.arrBox.get(i).color());
         	Rectangle2D.Double rect = new Rectangle2D.Double(this.arrBox.get(i).getX1(),this.arrBox.get(i).getY1(),this.arrBox.get(i).getX2()-this.arrBox.get(i).getX1(),this.arrBox.get(i).getY2()-this.arrBox.get(i).getY1());
         	g2.draw(rect);
	  	}
	  	for (int i=0;i<this.arrBrush.size();i++){	
	  		g2.setColor(this.arrBrush.get(i).color());
	  		Line2D.Double brush = new Line2D.Double(this.arrBrush.get(i).getX1(), this.arrBrush.get(i).getY1(),
	  				this.arrBrush.get(i).getX2(), this.arrBrush.get(i).getY2());
			
			g2.setStroke(new BasicStroke(this.arrBrush.get(i).brushWidth()));
			g2.draw(brush);
	  	}
	  	for (int i=0;i<this.arrTextList.size();i++){
	  		//String convert = ((Double)this.arrText.get(i).getX1()).toString();
	  		//String[] con = convert.split("");
	  		//int newX1 = Integer.parseInt(convert);
	  		//String convert2 =  ((Double)this.arrText.get(i).getY1()).toString();
	  		//int newY1 = Integer.parseInt(convert2);
	  		for(int j=0;j<this.arrTextList.get(i).size();j++){
	  		g2.drawString(((Coordinate)this.arrTextList.get(i).get(j)).message(), (float)((Coordinate)this.arrTextList.get(i).get(j)).getX1(), (float)((Coordinate)this.arrTextList.get(i).get(j)).getY1());
	  		g2.setColor(((Coordinate)this.arrTextList.get(i).get(j)).color());
	  		}
	  	}
	  	for (int j=0;j<this.arrPolyList.size();j++){
		  	for (int i=0;i<this.arrPolyList.get(j).size();i++){
		  		
		  		g2.setColor(((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i).color());
		  		if (this.arrPolyList.get(j).size()>0){
		  			if (i>0){
		  				if(Math.sqrt(Math.pow(((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i).getX1()-((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(0).getX1(),2)+Math.pow(((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i).getY1()-((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(0).getY1(),2))<10){
		  					Line2D.Double poly = new Line2D.Double(((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(0).getX1(), ((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(0).getY1(),((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i-1).getX1(),((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i-1).getY1());
		  					g2.draw(poly);
		  					if(j == this.arrPolyList.size()-1){
		  					this.PolyMode=1;
		  					}
		  				}
		  				else{
		  				Line2D.Double poly = new Line2D.Double(((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i).getX1(), ((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i).getY1(),((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i-1).getX1(),((ArrayList<Coordinate>)this.arrPolyList.get(j)).get(i-1).getY1());
		  				g2.draw(poly);
		  				}
		  			}
		  		}
		  		}
		  	}
		for (int i=0;i<this.arrErase.size();i++){
         	g2.setColor(this.arrErase.get(i).color());
         	Rectangle.Double rect = new Rectangle.Double(this.arrErase.get(i).getX1(),this.arrErase.get(i).getY1(),17,18);
         	ArrayList<ArrayList> lists = new ArrayList<ArrayList>();
         	lists.clear();
         	lists.add(this.arrLine);lists.add(this.arrBox);lists.add(this.arrOval);
         	lists.add(this.arrPencil);lists.add(this.arrBrush);
         	for(int z =0; z<this.arrPolyList.size();z++){
         		lists.add(this.arrPolyList.get(z));
         	}
         	for(int z =0; z<this.arrTextList.size();z++){
         		lists.add(this.arrTextList.get(z));
         	}
         	for (int j = 0;j<lists.size();j++){
         		if(lists.get(j).size()>0){
         			for(int k = 0;k<lists.get(j).size();k++){
         		Point2D.Double point = new Point2D.Double(((Coordinate)lists.get(j).get(k)).getX1(),((Coordinate)lists.get(j).get(k)).getY1());
         		Point2D.Double point2 = new Point2D.Double(((Coordinate)lists.get(j).get(k)).getX2(),((Coordinate)lists.get(j).get(k)).getY2());
         		double num1 = Math.sqrt(Math.pow(rect.getCenterX()-point.getX(),2)+Math.pow(rect.getCenterY()-point.getY(),2));
         		double num2 = Math.sqrt(Math.pow(rect.getCenterX()-point2.getX(),2)+Math.pow(rect.getCenterY()-point2.getY(),2));
         		if(num1<10||num2<10){
         				lists.get(j).remove(k);
         			}
         			}
         		}
         	this.arrErase.clear();
         	}
         	
         	
	  	}
		g2.setStroke(new BasicStroke(0));
		g2.setColor(this.objColor);
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;
		g2.setColor(this.objColor);
		redrawObjects(g2);

		if (this.drawMode == LINE) {
			Line2D.Double line = new Line2D.Double(this.x1, this.y1, this.x2,
					this.y2);
			g2.draw(line);
		}
		if (this.drawMode == OVAL) {
			if (this.x1 > this.x2 || this.y1 > this.y2) {
				Ellipse2D.Double oval = new Ellipse2D.Double(this.x2, this.y2,
						this.x1 - this.x2, this.y1 - this.y2);
				g2.draw(oval);
			} else {
				Ellipse2D.Double oval = new Ellipse2D.Double(this.x1, this.y1,
						this.x2 - this.x1, this.y2 - this.y1);
				g2.draw(oval);
			}
		}
		if (this.drawMode == BRUSH) {
			Line2D.Double brush = new Line2D.Double(this.linex1, this.liney1,
					this.linex2, this.liney2);
			g2.draw(brush);
		}
		if (this.drawMode == RECT) {
			if (this.x1 > this.x2 || this.y1 > this.y2) {
				Rectangle2D.Double rect = new Rectangle2D.Double(this.x2,
						this.y2, this.x1 - this.x2, this.y1 - this.y2);
				g2.draw(rect);
			} else {
				Rectangle2D.Double rect = new Rectangle2D.Double(this.x1,
						this.y1, this.x2 - this.x1, this.y2 - this.y1);
				g2.draw(rect);
			}

		}
		if (this.drawMode == PENCIL) {
			Line2D.Double line = new Line2D.Double(this.linex1, this.liney1,
					this.linex2, this.liney2);
			g2.draw(line);
		}
		
		if (this.drawMode == BRUSH) {/**/}
		if (this.drawMode == TEXT) {/**/}
		if (this.drawMode == POLY) {/**/}
	}
	
	/**
	 * Sets the width of the brush
	 * @param width 
	 */
	public void setBrushWidth(int width) {
		this.brushWidth = width;
	}
	
	/**
	 * Sets the drawMode for the tools.
	 *
	 * @param mode
	 */
	public void setDrawMode(int mode) {
		this.drawMode = mode;
	}

	/**
	 * Returns the draw mode for the tools.
	 *
	 * @return drawMode
	 */
	public int getDrawmode() {
		return this.drawMode;
	}

	/**
	 * Sets the color of the objects drawn.
	 *
	 * @param aColor
	 */
	public void setObjectColor(Color aColor) {
		this.objColor = aColor;
	}

	/**
	 * Returns the color of the objects drawn.
	 *
	 * @return color of objects drawn.
	 */
	public Color getObjectColor() {
		return this.objColor;
	}

	/**
	 * Sets the background color of the canvas.
	 *
	 * @param inputColor
	 */
	public void setBackGroundColor(Color inputColor) {
		this.backColor = inputColor;
		this.setBackground(this.backColor);
	}

	/**
	 * Returns the background color of the canvas.
	 *
	 * @return background color.
	 */
	public Color getBackGroundColor() {
		return this.backColor;
	}

	/**
	 * Clears the canvas.
	 *
	 */
	public void clearCanvas() {
		ArrayList<ArrayList> arrList = new ArrayList<ArrayList>();
		
		arrList.add(this.arrLine);arrList.add(this.arrBox);arrList.add(this.arrOval);
		arrList.add(this.arrPencil);arrList.add(this.arrPolyList);arrList.add(this.arrBrush);arrList.add(this.arrTextList);
		this.polycounter = -1;
		this.PolyMode = 1;
		for (int i=0;i<arrList.size();i++){
			arrList.get(i).clear();
		}
		
		repaint();
	}
}
