/**
 * 
 * Die Klasse Zeichnung ist ein JFrame zur Aufnahme von Graphikobjekten
 * Die sichbaren Attribute und Methoden wurden den BlueJ-Beispiel Shapes nachempfunden.
 *   
 * Die statische Methoden gibJFrame und gibPanel geben Referenzen auf das JPanel in dem JFrame zurück
 * Wenn es den JFrame und das JPanel noch nicht gibt, werden sie erzeugt.
 *   
 * Die statische Methode setFramesize ändert die Breite und Höhe des Frames
 * Wenn es den Frame und das Panel noch nicht gibt, werden sie erzeugt.
 *   
 *  Normalerweise braucht man sich um den Frame und das Panel nicht zu kümmern. 
 *  Sie werden von den Komponenten automatisch erzeugt.  
 *   
 * @author: Hans Witt 
 * @version: 1.2 
 *     Raster bei der Zeichenfläche eingeführt
 * 
 * Interface ITuWas geändert s.u.
 * 
 */

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

import java.awt.Color;
import java.awt.Graphics;

public class Zeichnung extends JFrame {
	
	public static Zeichenflaeche	panel;
	
	public static JFrame			single;
	
	public static Zeichenflaeche gibZeichenflaeche() {
		if (single == null) {
			single = new Zeichnung("BlueJ Graphik-Fenster");
		}
		return panel;
	}
	
	public static JFrame gibJFrame() {
		if (single == null) {
			single = new Zeichnung("BlueJ Graphik-Fenster");
		}
		return single;
	}
	
	public Zeichnung() {
		// Frame-Initialisierung
		super();
		setTitle("BlueJ-JGUIToolbox");
		
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent evt) {
				System.exit(0);
			}
		});
		
		int frameWidth = 1000;
		int frameHeight = 750;
		setSize(frameWidth, frameHeight);
		Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
		
		// Zeichenfläche zentriert
		// int x = (d.width - getSize().width) / 2;
		// int y = (d.height - getSize().height) / 2;
		
		// Zeichenfläche rechtsbündig
		int x = d.width - getSize().width;
		int y = (d.height - getSize().height) / 2;
		setLocation(x, y);
		panel = new Zeichenflaeche();
		this.getContentPane().add(panel);
		
		setExtendedState(Frame.NORMAL);
		setResizable(true);
		setVisible(true);
		// Damit immer der gleiche Frame angesprochen wird, unabhängig vom der
		// Erzeugung über new oder gibJFrame
		single = this;
	}
	
	public Zeichnung(String title) {
		this();
		setTitle(title);
	}
	
	public Zeichnung(String title, boolean mitRaster) {
		this();
		setTitle(title);
		if (mitRaster) {
			setzeRasterEin();
		} else {
			setzeRasterAus();
		}
		
	}
	
	public static void setzeFenstergroesse(int breite, int hoehe) {
		JFrame frame = gibJFrame();
		frame.setSize(breite, hoehe);
		Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
		
		// Zeichenfläche zentriert
		// int x = (d.width - getSize().width) / 2;
		// int y = (d.height - getSize().height) / 2;
		
		// Zeichenfläche rechtsbündig
		int x = d.width - frame.getSize().width;
		int y = (d.height - frame.getSize().height) / 2;
		frame.setLocation(x, y);
	}
	
	public static void setzeRasterEin() {
		Zeichenflaeche.mitRaster = true;
		Zeichnung.gibJFrame().repaint();
	}
	
	public static void setzeRasterAus() {
		Zeichenflaeche.mitRaster = false;
		Zeichnung.gibJFrame().repaint();
	}
	
	public static void setzeDeltaX(int deltaX) {
		Zeichenflaeche.deltaX = deltaX;
		Zeichnung.gibJFrame().repaint();
	}
	
	public static void setzeDeltaY(int deltaY) {
		Zeichenflaeche.deltaY = deltaY;
		Zeichnung.gibJFrame().repaint();
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		single = new Zeichnung("Graphik-Fenster-main");
	}
}

class Zeichenflaeche extends JPanel implements IContainer {
	
	public static boolean	mitRaster	= false;
	public static int		deltaX		= 100;
	public static int		deltaY		= 100;
	
	public static void setzeMitRaster(boolean mitRaster) {
		Zeichenflaeche.mitRaster = mitRaster;
		Zeichnung.gibJFrame().repaint();
	}
	
	public static void setzeDeltaX(int deltaX) {
		Zeichenflaeche.deltaX = deltaX;
	}
	
	public static void setzeDeltaY(int deltaY) {
		Zeichenflaeche.deltaY = deltaY;
	}
	
	public Zeichenflaeche() {
		this.setLayout(null);
	}
	
	public void setzeSichtbarkeit(boolean sichtbar) {
		this.getParent().setVisible(sichtbar);
	}
	
	public void setzeKomponentenKoordinaten(JComponent obj, int x, int y,
			int width, int height) {
		obj.setBounds(x, y, width, height);
		repaint();
		Zeichnung.gibJFrame().validate();
	}
	
	public void setzeKomponentenGroesse(JComponent obj, int width, int height) {
		obj.setSize(width, height);
		obj.repaint();
		repaint();
		Zeichnung.gibJFrame().validate();
	}
	
	public void setzeKomponentenPosition(JComponent obj, int x, int y) {
		obj.setLocation(x, y);
		obj.repaint();
		repaint();
		Zeichnung.gibJFrame().validate();
	}
	
	/**
	 * Die Darstellung der Komponente wird hier programmiert.
	 */
	
	public void paintComponentSpezial(Graphics g) {
		if (mitRaster) {
			Graphics2D g2 = (Graphics2D) g;
			// Graphik-Abmessungen
			int breite = getSize().width - 1;
			int hoehe = getSize().height - 1;
			Color farbe = StaticTools.getColor("schwarz");
			g.setColor(farbe);
			
			int hor = deltaX;
			while (hor < breite) {
				g2.drawLine(hor, 0, hor, hoehe);
				hor += deltaX;
			}
			
			int ver = deltaY;
			while (ver < hoehe) {
				g2.drawLine(0, ver, breite, ver);
				ver += deltaY;
			}
		}
	}
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		paintComponentSpezial(g);
	}
}

/**
 * Basisklasse, von der alle Komponenten im Framework abgeleitet sind. </br>
 * 
 * in der Methode paintComponent(Graphics g) wird die Methode
 * paintComponentSpezial(Graphics g) (absstrakt!) ufgerufen. Die von der
 * Basiskomponente abgeleiteten Komponenten können darin ihre Zeichnungen
 * unterbringen.
 */
abstract class BasisComponente extends JPanel {
	// Zustand der Komponente
	
	protected Color		farbe		= StaticTools.leseNormalZeichenfarbe();
	protected int		breite;
	protected int		hoehe;
	protected int		xPos		= 0;
	protected int		yPos		= 0;
	protected boolean	gefuellt	= true;
	protected boolean	sichtbar	= true;
	
	/**
	 * Konstruktor ohne Beschriftung
	 */
	public BasisComponente() {
		setOpaque(false); // Komponenten sind durchsichtig !
	}
	
	/**
	 * Die Darstellung der Komponente wird hier programmiert.
	 */
	public abstract void paintComponentSpezial(Graphics g);
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		paintComponentSpezial(g);
	}
	
	public void setzeBasisfarbe(String farbname) {
		farbe = StaticTools.getColor(farbname);
		repaint();
	}
	
	/**
	 * Mache sichtbar. Wenn es bereits sichtbar ist, tue nichts.
	 */
	public void sichtbarMachen() {
		sichtbar = true;
		setzeGroesse(breite,hoehe);
		// setVisible(true);
		repaint();
	}
	
	/**
	 * Mache diesen Kreis unsichtbar. Wenn es bereits unsichtbar ist, tue
	 * nichts.
	 */
	public void unsichtbarMachen() {
		sichtbar = false;
		setzeGroesse(breite,hoehe);
		// setVisible(false);
		repaint();
	}
	
	public void fuellen() {
		gefuellt = true;
		repaint();
	}
	
	public void rand() {
		gefuellt = false;
		repaint();
	}
	
	public void setzeGroesse(int width, int height) {
		breite = width;
		hoehe = height;
		if (sichtbar) {
			((IContainer) this.getParent()).setzeKomponentenGroesse(this,
					width, height);
		} else {
			((IContainer) this.getParent()).setzeKomponentenGroesse(this, 0, 0);
		}
		// Zeichnung.gibZeichenflaeche().setzeGroesse(this, width, height);
		// Zeichnung.gibJFrame().validate();
	}
	
	public void setzePosition(int x, int y) {
		xPos = x;
		yPos = y;
		((IContainer) this.getParent()).setzeKomponentenPosition(this, x, y);
		// Zeichnung.gibZeichenflaeche().setzePosition(this, x, y);
		// Zeichnung.gibJFrame().validate();
	}
	
	public void setzeDimensionen(int neuesX, int neuesY, int neueBreite,
			int neueHoehe) {
		xPos = neuesX;
		yPos = neuesY;
		breite = neueBreite;
		hoehe = neueHoehe;
		if (sichtbar) {
			((IContainer) this.getParent()).setzeKomponentenKoordinaten(this,
					xPos, yPos, breite, hoehe);
		} else {
			((IContainer) this.getParent()).setzeKomponentenKoordinaten(this,
					xPos, yPos, 0, 0);
		}
	}
}

/**
 * Interface für Recall-Funktion von Button u.ä. Diese Klassen besitzen eine
 * Methode public void setzeLink( ITuWas linkObj ) Diesem Link wird die Klasse
 * übergeben. Die Buttonklasse ruft dann die Funktion tuWas auf.
 * 
 * @author Witt
 * @version: 2 ( 3.8.2008 )
 * 
 */

interface ITuWas {
	
	/**
	 * Das Interface fordert die Recall-Methode tuWas(int ID ) Verschiedene
	 * Komponenten der Klasse erhalten verschiedene IDs
	 * 
	 * ID identifiziert die Komponente Bei mehreren Eventquellen einer
	 * Komponente wird die Eventquelle durch ID + nr identifiziert nr ist die
	 * Nummer der EventQuelle. nr beginnt mit 0
	 * 
	 * @version: 2 ( 3.8.2008 ): Änderung des Call-Back-Mechanismus. Bei
	 *           mehreren Quellen identifikation über ID + nr
	 */
	public void tuWas(int ID);
}

/**
 * Das Interface IContainer fordert Methoden zum Ändern der Größe und Position
 * eingebetteter Komponenten 1. Beispiel: Der Container Zeichenfläche
 * 
 * @version: 3 ( 4.8.2008 ):
 */
interface IContainer {
	
	public Component add(Component comp, int index);
	
	/**
	 * die folgenden Methoden weden von der Basiskomponente aufgerufen obj ist
	 * immer (IContainer) this.getParent() von der Basidkomponente von hier aus
	 * wird die Basiskomponente verändert
	 */
	
	public void setzeKomponentenKoordinaten(JComponent obj, int x, int y,
			int width, int height);
	
	public void setzeKomponentenGroesse(JComponent obj, int width, int height);
	
	public void setzeKomponentenPosition(JComponent obj, int x, int y);
	
	public void validate();
}
