/**
 * 
 * Die Klasse stellt eine von <code>javax.swing.JLayeredPane</code> abgeleitete
 * Komponente bereit, die es auf einfache Weise erm&#246;glicht, mehrfach auf ein Bild
 * zu zeichnen. Der Fortschritt der Zeichnung kann in Form von Outline-Rahmen
 * w&#228;hrend des Zeichenvorgangs verfolgt werden.<br>
 * Aus den folgenden Zeichnungstypen kann gew&#228;hlt werden:
 * <table>
 * <tr>
 * <td>Rechteck</td>
 * <td>ein ausgef&#252;lltes Rechteck</td>
 * </tr>
 * <tr>
 * <td>Oval</td><td>ein ausgef&#252;lltes Oval</td>
 * </tr>
 * <tr>
 * <td>Gerade Linie</td><td>Mausdown legt den Startpunkt, Mausup den Endpunkt
 * fest</td>
 * </tr>
 * <tr>
 * <td>Freihandzeichnen</td>
 * <td></td>
 * </tr>
 * </table>
 * Transparenz, Farbe und Linienst&#228;rke k&#246;nnen eingestellt werden.<br>
 * Die Klasse besitzt wie die Elternklasse ein Null-Layout, sodass z.B. die
 * Gr&#246;&#223;e durch den Entwickler explizit angegeben werden muss. Bei
 * Aufruf des Konstruktors mit einem ImageIcon wird dies als Ma&#223;stab
 * verwendent.
 */1
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
/*
 * @JB_PaintLayer.java     12.02.2010
 * build: 12.02.2010
 * Copyright 2010 javabeginners.de. All rights reserved
 * @author Joerg Czeschla
 * 
 * This file is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License along with this file;
 * if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */


@SuppressWarnings("serial")
class JB_PaintLayer extends JLayeredPane {

	/**
	 * Definiert den Zeichnungstyp Default-Wert ist <code>PaintType.Rect</code>
	 * 
	 */
	public static enum PaintType {
		RECT, OVAL, LINE, FREE
	};

	/**
	 * Konstruiert eine leere PaintLayer
	 */
	public JB_PaintLayer() {
		this(null, null, 1f, 1f, null);
	}

	/**
	 * Konstruiert eine PaintLayer mit vorgeladenem Bild und
	 * vorausgew&#228;hltem Zeichnungstyp
	 * 
	 * @param icon
	 *            das geladene Icon
	 * @param paintType
	 *            der Zeichnungstyp f&#252;r das &#220;berzeichnen
	 */
	public JB_PaintLayer(ImageIcon icon, PaintType paintType) {
		this(icon, paintType, 1f, 1f, null);
	}

	/**
	 * Konstruiert eine PaintLayer mit vorgeladenem Bild, vorausgew&#228;hltem
	 * Zeichnungstyp und festgelegter Linienst&#228;rke
	 * 
	 * @param icon
	 *            das geladene Icon
	 * @param paintType
	 *            der Zeichnungstyp f&#252;r das &#220;berzeichnen
	 * @param lineWidth
	 *            die Linienst&#228;rke Sie wird f&#252;r die Auswahlrahmen beim
	 *            Aufziehen und als dauerhafte Linien beim Linien- und
	 *            Freihandzeichnen verwendet
	 */
	public JB_PaintLayer(ImageIcon icon, PaintType paintType, float lineWidth) {
		this(icon, paintType, lineWidth, 1f, null);
	}

	/**
	 * Konstruiert eine PaintLayer mit vorgeladenem Bild, vorausgew&#228;hltem
	 * Zeichnungstyp, Linienst&#228;rke, Alphawert und Farbe
	 * 
	 * @param icon
	 *            das geladene Icon
	 * @param paintType
	 *            der Zeichnungstyp f&#252;r das &#220;berzeichnen
	 * @param lineWidth
	 *            die Linienst&#228;rke Sie wird f&#252;r die Auswahlrahmen beim
	 *            Aufziehen und als dauerhafte Linien beim Linien- und
	 *            Freihandzeichnen verwendet.
	 * @param alpha
	 *            Alphawert der Zeichnung
	 * @param color
	 *            Farbe der Zeichnung
	 */
	public JB_PaintLayer(ImageIcon icon, PaintType paintType, float lineWidth,
			float alpha, Color color) {

		PaintPanel.paintType = paintType == null ? PaintType.RECT : paintType;
		PaintPanel.lineWidth = lineWidth > 0 ? lineWidth : 1f;
		PaintPanel.alpha = (alpha >= 0f && alpha <= 1f) ? alpha : 1f;
		PaintPanel.color = color != null ? color : Color.BLACK;

		Dimension dimension = super.getPreferredSize();

		if (icon != null) {
			dimension.width = icon.getIconWidth();
			dimension.height = icon.getIconHeight();
			JLabel bildLabel = new JLabel(icon);
			bildLabel.setSize(dimension);
			bildLabel.setOpaque(true);
			add(bildLabel, new Integer(0));
		}

		final PaintPanel paintPanel = new PaintPanel();
		paintPanel.setSize(dimension);
		add(paintPanel, new Integer(1));

		setPreferredSize(dimension);
		setSize(dimension);
	}

	/**
	 * Leert die gesamte Zeichnung &#252;ber dem Bild
	 */
	public void clear() {
		PaintPanel.counter = -1;
		PaintPanel.pointList.clear();
		repaint();
	}

	/**
	 * Leert den zuletzt ausgef&#252;hrten Schritt der Zeichnung &#252;ber dem
	 * Bild
	 */
	public void removeLast() {
		PaintPanel.counter--;
		PaintPanel.pointList.remove(PaintPanel.pointList.size() - 1);
		repaint();
	}

	/**
	 * Liefert den aktuellen Zeichnungstyp
	 * 
	 * @return der Zeichnungstyp
	 */
	public PaintType getPaintType() {
		return PaintPanel.paintType;
	}

	/**
	 * Setzt den Zeichnungstyp
	 * 
	 * @param paintType
	 *            der Zeichnungstyp
	 */
	public void setPaintType(PaintType paintType) {
		PaintPanel.paintType = paintType;
	}

	/**
	 * Liefert die aktuelle Linienst&#228;rke
	 * 
	 * @return die Linienst&#228;rke
	 */
	public float getLineWidth() {
		return PaintPanel.lineWidth;
	}

	/**
	 * Setzt die Linienst&#228;rke
	 * 
	 * @param lineWidth
	 *            die Linienst&#228;rke
	 */
	public void setLineWidth(float lineWidth) {
		PaintPanel.lineWidth = lineWidth;
	}

	/**
	 * Liefert den aktuellen Alphawert der Zeichnung
	 * 
	 * @return der aktuelle Alphawert der Zeichnung
	 */
	public float getAlpha() {
		return PaintPanel.alpha;
	}

	/**
	 * Setzt den Alphawert
	 * 
	 * @param alpha
	 *            der Alphawert, er muss zwischen 0.0f (transparent) und 1.0
	 *            (opak) gesetzt werden. Bei abweichenden Werten wird er auf
	 *            1.0f gesetzt
	 */
	public void setAlpha(float alpha) {
		PaintPanel.alpha = (alpha >= 0f && alpha <= 1.0) ? alpha : 1.0f;
	}

	/**
	 * Liefert die aktuelle Vordergrundfarbe der Zeichnung
	 * 
	 * @return die Vordergrundfarbe
	 */
	public Color getColor() {
		return PaintPanel.color;
	}

	/**
	 * Setzt die Vordergrundfarbe
	 * 
	 * @param color
	 *            die Vordergrundfarbe
	 */
	public void setColor(Color color) {
		PaintPanel.color = color;
	}

	static class PaintPanel extends JPanel implements MouseListener,
			MouseMotionListener {

		private static PaintType paintType;
		private static float lineWidth = 1f;
		private static float alpha = 1f;
		private static Color color = Color.BLACK;
		private static int counter = -1;
		private PaintState paintState;
		private static ArrayList<Integer[]> pointList = new ArrayList<Integer[]>(
				3);

		private enum PaintState {
			START, RUNNING, END
		}

		public PaintPanel() {
			paintState = PaintState.START;
			this.addMouseListener(this);
			this.addMouseMotionListener(this);
			setOpaque(false);
		} // Ende Konstruktor

		protected void paintComponent(Graphics g) {
			Graphics2D g2 = (Graphics2D) g;
			// opakes Composite zwischenspeichern
			Composite oldComp = g2.getComposite();
			// transparentes Composite setzen und zeichnen
			g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
					alpha));
			g2.setStroke(new BasicStroke(lineWidth, BasicStroke.CAP_BUTT,
					BasicStroke.JOIN_MITER));
			g2.setColor(color);

			if (paintType == PaintType.RECT)
				paintRect(g2);
			if (paintType == PaintType.OVAL)
				paintOval(g2);
			if (paintType == PaintType.LINE)
				paintLine(g2);
			if (paintType == PaintType.FREE)
				paintFree(g2);

			// wieder opakes Composite setzen
			g2.setComposite(oldComp);
		} // Ende paintComponent()

		private void paintRect(Graphics2D g2) {
			for (int i = 0; i < pointList.size(); i++) {
				Integer[] intArr = (Integer[]) pointList.get(i);
				if (paintState == PaintState.START
						|| paintState == PaintState.RUNNING)
					g2.drawRect(intArr[0], intArr[1], intArr[2] - intArr[0],
							intArr[3] - intArr[1]);
				else if (paintState == PaintState.END)
					g2.fillRect(intArr[0], intArr[1], intArr[2] - intArr[0],
							intArr[3] - intArr[1]);
			}
		}

		private void paintOval(Graphics2D g2) {
			for (int i = 0; i < pointList.size(); i++) {
				Integer[] intArr = (Integer[]) pointList.get(i);
				if (paintState == PaintState.START
						|| paintState == PaintState.RUNNING)
					g2.drawOval(intArr[0], intArr[1], intArr[2] - intArr[0],
							intArr[3] - intArr[1]);
				else if (paintState == PaintState.END)
					g2.fillOval(intArr[0], intArr[1], intArr[2] - intArr[0],
							intArr[3] - intArr[1]);
			}
		}

		private void paintLine(Graphics2D g2) {
			for (int i = 0; i < pointList.size(); i++) {
				Integer[] intArr = (Integer[]) pointList.get(i);
				g2.drawLine(intArr[0], intArr[1], intArr[2], intArr[3]);
			}
		}

		private void paintFree(Graphics2D g2) {
			for (int i = 0; i < pointList.size(); i++) {
				Integer[] intArr = (Integer[]) pointList.get(i);
				g2.drawLine(intArr[0], intArr[1], intArr[2], intArr[3]);
				intArr[0] = intArr[2];
				intArr[1] = intArr[3];
			}
		}

		/**************** EventHandling *****************/

		public void mousePressed(MouseEvent e) {
			update(getGraphics());
			pointList.add(++counter, new Integer[] { e.getX(), e.getY(),
					e.getX(), e.getY() });
		}

		public void mouseDragged(MouseEvent e) {
			paintState = PaintState.RUNNING;
			Integer[] arr = (Integer[]) pointList.get(counter);
			arr[2] = (Integer) e.getX();
			arr[3] = (Integer) e.getY();
			if (paintType != PaintType.FREE)
				repaint();
			else
				update(getGraphics());
		}

		public void mouseReleased(MouseEvent e) {
			paintState = PaintState.END;
			if (paintType != PaintType.FREE)
				repaint();
		}

		public void mouseMoved(MouseEvent e) {
		}

		public void mouseClicked(MouseEvent e) {
		}

		public void mouseEntered(MouseEvent e) {
		}

		public void mouseExited(MouseEvent e) {
		}
	}
}

