 
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.KeyboardFocusManager;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

/**
 * Class ZeichenFensterMaxi - Eine Klasse, die komplexere grafische Zeichnungen 
 * in einem Programmfenster ermoeglicht.
 * erweiterte Version von ZEICHENFENSTER
 * 
 * @author Michael Kolling (mik)
 * @author Bruce Quig
 * @author Christian Heidrich
 * @author Daniel Keitel
 *
 * @version 2015.12.18
 */

public class ZeichenFensterMaxi{
    private JFrame frame;
    private CanvasPane canvas;
    private JPanel steuerungOst,steuerungSued;
    private Color hintergrundFarbe;
    private Color vordergrundFarbe;
    private float rotationsWinkel;
    private Font schriftArt;
    private boolean autoMalen;
    private boolean spezialFarben;
	private Random zufallsGenerator;
    
    private static ZeichenFensterMaxi singleton;

    /**
     * Erzeugt eine Zeichenfenster mit Standardmassen 600*500 und Hintergrundfarbe weiss 
     * @param titel  Titel des Fensters     
     */
    public ZeichenFensterMaxi(String titel){
        this(titel, 600, 500, Color.white,true,false,true);        
    }

    /**
     * Erzeugt ein Zeichenfenster mit weissem Hintergrund.
     * @param titel  Fensterueberschrift
     * @param breite  Breite der Zeichenflaeche
     * @param hoehe  Hoehe der Zeichenflaeche
     */
    public ZeichenFensterMaxi(String titel, int breite, int hoehe){
        this(titel, breite, hoehe, Color.white,true,false,true);
    }
    
    /**
     * Erzeugt ein Zeichenfenster mit weissem Hintergrund.
     * @param titel  Fensterueberschirft
     * @param breite  Breite der Zeichenflaeche
     * @param hoehe  Hoehe der Zeichenflaeche
     * @param autoMalen Parameter, ob das gezeichnete automatisch sichtbar wird,
     * zum manuellen Zeichnen male(); aufrufen
     * @param spezialFarben werden Spezialfarben aktiviert
     * @param laesst sich die Fenstergroesse nachtroeglich Aendern
     */
    public ZeichenFensterMaxi(String titel, int breite, int hoehe,boolean autoMalen,boolean spezialFarben,boolean resizable){
        this(titel, breite, hoehe, Color.white, autoMalen, spezialFarben, resizable);
    }

    /**
     * Erzeugt ein Zeichenfenster.
     * @param titel  Fensterueberschrift
     * @param breite  Breite der Zeichenflaeche
     * @param hoehe  Hoehe der Zeichenflaeche
     * @param hintergrundFarbe  Hintergrundfarbe des Zeichenfensters
     * @param autoMalen Parameter, ob das gezeichnete automatisch sichtbar wird,
     * zum manuellen Zeichnen male(); aufrufen
     * @param spezialFarben werden Spezialfarben aktiviert
     * @param laesst sich die Fenstergroesse nachtroeglich Aendern
     */
    private ZeichenFensterMaxi(String titel, int breite, int hoehe, Color hintergrundFarbe,boolean autoMalen,boolean spezialFarben,boolean resizeable){
        frame = new JFrame();
        if(resizeable){
        	canvas = new CanvasPane();
        }else{
        	canvas = new CanvasPane(breite,hoehe,hintergrundFarbe);
        }
        canvas.setPreferredSize(new Dimension(breite, hoehe));
        frame.getContentPane().add(canvas,BorderLayout.CENTER);
        JPanel p1=new JPanel();
        p1.setLayout(new BorderLayout());
        steuerungOst = new JPanel();
        steuerungSued = new JPanel();
        steuerungOst.setLayout(new BoxLayout(steuerungOst,BoxLayout.Y_AXIS));
        steuerungSued.setLayout(new BoxLayout(steuerungSued,BoxLayout.X_AXIS));
        p1.add(steuerungOst,BorderLayout.NORTH);
        frame.getContentPane().add(p1,BorderLayout.EAST);
        frame.getContentPane().add(steuerungSued,BorderLayout.SOUTH);
        frame.setTitle(titel);
        vordergrundFarbe = Color.BLACK;
        schriftArt = new Font(Font.SERIF, Font.PLAIN, 15);
        this.hintergrundFarbe = hintergrundFarbe;
        this.autoMalen = autoMalen;
        frame.pack();
        frame.setResizable(resizeable);
        rotationsWinkel = 0f;
        canvas.addClear(hintergrundFarbe);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        zufallsGenerator = new Random();
        this.spezialFarben = spezialFarben;
        male();
        zeige();
    }
    
    /**
     * Gibt ein Standart Zeichenfenster mit dem Titel "Das Zeichenfenster" zurueck
     * @return instanz des Fensters
     */
    @Deprecated
    public static ZeichenFensterMaxi gibFenster(){
        if (singleton==null){singleton=new ZeichenFensterMaxi("Das Zeichenfenster");}
        singleton.zeige();
        return singleton;
    }

    /**
     * Macht das Zeichenfenster sichtbar bzw. setzt es in den Vordergrund,
     * falls es bereits sichtbar ist.
     */
    public void zeige(){
        frame.setVisible(true);
    }
    
    /**
     * Zeigt die aktuell gepufferten Zeichen befehle an
     * wird bei deaktivirten AutoMalen benoetigt
     */
    public void male(){
    	canvas.draw();
	}

    /**
     * Gibt Information ueber die Sichtbarkeit.
     * @return  true falls das Fenster sichtbar ist.
     */
    public boolean istSichtbar(){
        return frame.isVisible();
    }
    
    /**
     * Setz den Parameter Automalen
     * @param autoMalen Parameter, ob das gezeichnete automatisch sichtbar wird,
     * zum manuellen Zeichnen male(); aufrufen
     */
    public void setzeAutomalen(boolean autoMalen){
    	this.autoMalen = autoMalen;
    }
    
    /**
     * Zeichnet einen Ellipsenbogen (Siehe Graphics.drawArc)
     * @param x x-Koordinate des Ellipsenmittelpunkts
     * @param y y-Koordinate des Ellipsenmittelpunkts
     * @param halbachseX Halbachse der Ellipse in x-Richtung
     * @param halbachseY Halbachse der Ellipse in y-Richtung
     * @param startWinkel Polarwinkel, an dem der Bogen anfaengt
     * @param winkel Polarwinkel, welchen der Bogen durchlaeuft
     */
    public void zeichneBogen(int x, int y, int halbachseX, int halbachseY, int startWinkel, int winkel){
    	Shape s = new Arc2D.Float(x-halbachseX,y-halbachseY,2*halbachseX,2*halbachseY,startWinkel,winkel, Arc2D.OPEN);
    	canvas.addShape(s, rotationsWinkel, false, vordergrundFarbe);
        if(autoMalen)male();
    }
    
    /**
     * Zeichnet eine Ellipse (Siehe Graphics.drawOval)
     * @param x x-Koordinate
     * @param y y-Koordinate
     * @param breite breite
     * @param hoehe hoehe
     * @param radius Kreisradius
     */
    public void zeichneEllipse(int x, int y, int breite, int hoehe){
    	canvas.addShape(new Ellipse2D.Float(x, y , breite, hoehe), rotationsWinkel, false, vordergrundFarbe);
        if(autoMalen)male();
    }
    
    /**
     * Fuellt eine Ellipse (Siehe Graphics.drawOval)
     * @param x x-Koordinate
     * @param y y-Koordinate
     * @param breite breite
     * @param hoehe hoehe
     * @param radius Kreisradius
     */
    public void fuelleEllipse(int x, int y, int breite, int hoehe, int farbnr){
    	canvas.addShape(new Ellipse2D.Float(x, y , breite, hoehe), rotationsWinkel, true, farbeZuColor(farbnr));
        if(autoMalen)male();
    }
    
    /**
     * Fuellt eine Ellipse (Siehe Graphics.drawOval)
     * @param x x-Koordinate
     * @param y y-Koordinate
     * @param breite breite
     * @param hoehe hoehe
     * @param radius Kreisradius
     */
    public void fuelleEllipse(int x, int y, int breite, int hoehe, String farbe){
    	canvas.addShape(new Ellipse2D.Float(x, y , breite, hoehe), rotationsWinkel, false, farbeZuColor(farbe));
        if(autoMalen)male();
    }
    
    /**
     * Loescht eine Ellipse (Siehe Graphics.drawOval)
     * @param x x-Koordinate
     * @param y y-Koordinate
     * @param breite breite
     * @param hoehe hoehe
     * @param radius Kreisradius
     */
    public void loescheEllipse(int x, int y, int breite, int hoehe){
    	loesche(new Ellipse2D.Float(x, y , breite, hoehe));
        if(autoMalen)male();
    }

    /**
     * Zeichnet einen Kreis (Siehe Graphics.drawOval)
     * @param x x-Koordinate des Mittelpunkts
     * @param y y-Koordinate des Mittelpunkts
     * @param radius Kreisradius
     */
    public void zeichneKreis(int x, int y, int radius){
    	canvas.addShape(new Ellipse2D.Float(x-radius,y-radius,2*radius,2*radius), rotationsWinkel, false, vordergrundFarbe);
        if(autoMalen)male();
    }

    /**
     * Fuellt das Innere eines Kreises mit der angegebenen Farbe. 
     * @param x x-Koordinate des Mittelpunkts
     * @param y y-Koordinate des Mittelpunkts
     * @param radius Kreisradius
     * @param  farbe  Fuellfarbe fuer den Kreis, erlaubt sind "weiss" "schwarz" "rot"
     * "gruen" "blau" "gelb" "magenta" "cyan" "grau"
     */
    public void fuelleKreis(int x, int y, int radius, String farbe){
    	canvas.addShape(new Ellipse2D.Float(x-radius,y-radius,2*radius,2*radius), rotationsWinkel, true, farbeZuColor(farbe));
        if(autoMalen)male();
    }
    
    /**
     * Fuellt das Innere eines Kreises mit der angegebenen Farbe. 
     * @param x x-Koordinate des Mittelpunkts
     * @param y y-Koordinate des Mittelpunkts
     * @param radius Kreisradius
     * @param  farbnr  Fuellfarbnummer fuer den Kreis (0 bis 8)
     */
    public void fuelleKreis(int x, int y, int radius, int farbnr){
    	canvas.addShape(new Ellipse2D.Float(x-radius,y-radius,2*radius,2*radius), rotationsWinkel, true, farbeZuColor(farbnr));
        if(autoMalen)male();
    }

	/**
     * Loescht das Innere eines Kreises 
     * @param x x-Koordinate des Mittelpunkts
     * @param y y-Koordinate des Mittelpunkts
     * @param radius Kreisradius
     */
    public void loescheKreis(int x, int y, int radius){
        Ellipse2D.Double circle = new Ellipse2D.Double(x-radius, y-radius, 2*radius, 2*radius);
        loesche(circle);
    }

    /**
     * Zeichnet den Umriss eines Shape-Objekts.
     * @param  shape  das Shape-Object, welches gezeichnet werden soll
     */
    public void zeichne(Shape shape){
    	canvas.addShape(shape, rotationsWinkel, false, vordergrundFarbe);
        if(autoMalen)male();
    }
 
    /**
     * Fuellt das Innere eines Shape-Objekts mit der angegebenen Farbe. 
     * @param  shape  das Shape-Objekt, welches gefuellt werden soll 
     * @param  farbe  Fuellfarbe fuer das Shape-Objekt, erlaubt sind "weiss" "schwarz" "rot"
     * "gruen" "blau" "gelb" "magenta" "cyan" "grau"
     */
    public void fuelle(Shape shape, String farbe){
    	canvas.addShape(shape, rotationsWinkel, true, farbeZuColor(farbe));
        if(autoMalen)male();
    }

    /**
     * Fuellt das Innere eines Shape-Objekts mit der angegebenen Farbe. 
     * @param  shape  das Shape-Objekt, welches gefuellt werden soll 
     * @param  farbnr  Fuellfarbnummer fuer das Shape-Objekt (0 bis 8)
     */
    public void fuelle(Shape shape, int farbnr){
    	canvas.addShape(shape, rotationsWinkel, true, farbeZuColor(farbnr));
        if(autoMalen)male();
    }

    /**
     * Loescht das Innere eines Shape-Objekts.
     * @param  shape  das Shape-Object, welches geloescht werden soll 
     */
    public void loesche(Shape shape){
    	canvas.addShape(shape, rotationsWinkel, true, hintergrundFarbe);
        if(autoMalen)male();
    }

    /**
     * Zeichnet den Rand des Rechtecks mit der aktuellen Farbe.
     * @param xPos,yPos Koordinaten der linken oberen Ecke 
     * @param breite, hoehe Breite und Hoehe des Rechtecks
     */
    public void zeichneRechteck(int xPos, int yPos, int breite, int hoehe){
    	canvas.addShape(new Rectangle(xPos, yPos, breite, hoehe), rotationsWinkel, false, vordergrundFarbe);
        if(autoMalen)male();
    }

    /**
     * Fuellt das Innere des Rechtecks mit der angegebenen Farbe.
     * @param xPos,yPos Koordinaten der linken oberen Ecke 
     * @param breite, hoehe Breite und Hoehe des Rechtecks
     * @param  farbe  Fuellfarbe fuer das Rechteck, erlaubt sind "weiss" "schwarz" "rot"
     * "gruen" "blau" "gelb" "magenta" "cyan" "grau"
     */
    public void fuelleRechteck(int xPos, int yPos, int breite, int hoehe, String farbe){
    	canvas.addShape(new Rectangle(xPos, yPos, breite, hoehe), rotationsWinkel, true, farbeZuColor(farbe));
        if(autoMalen)male();
    }

    /**
     * Fuellt das Innere des Rechtecks mit der angegebenen Farbe.
     * @param xPos,yPos Koordinaten der linken oberen Ecke 
     * @param breite, hoehe Breite und Hoehe des Rechtecks
     * @param  farbnr  Fuellfarbnummer fuer das Rechteck (0 bis 8)
     */
    public void fuelleRechteck(int xPos, int yPos, int breite, int hoehe, int farbnr){
    	canvas.addShape(new Rectangle(xPos, yPos, breite, hoehe), rotationsWinkel, true, farbeZuColor(farbnr));
        if(autoMalen)male();
    }

    /**
     * Loescht das Innere eines Rechtecks.
     * @param xPos,yPos Koordinaten der linken oberen Ecke 
     * @param breite, hoehe Breite und Hoehe des Rechtecks
     */
    public void loescheRechteck(int xPos, int yPos, int breite, int hoehe){
        loesche(new Rectangle(xPos, yPos, breite, hoehe));
    }
    
    /**
     * Zeichnet den Rand eines Dreiecks mit der aktuellen Farbe.
     * @param x1,y1 Koordinaten des ersten Eckpunkts 
     * @param x2,y2 Koordinaten des zweiten Eckpunkts 
     * @param x3,y3 Koordinaten des dritten Eckpunkts 
     */
    public void zeichneDreieck(int x1, int y1, int x2, int y2, int x3, int y3){
    	zeichnePolygon(x1,y1,x2,y2,x3,y3);
    }

    /**
     * Fuellt das Innere eines Dreiecks mit der angegebenen Farbe.
     * @param x1,y1 Koordinaten des ersten Eckpunkts 
     * @param x2,y2 Koordinaten des zweiten Eckpunkts 
     * @param x3,y3 Koordinaten des dritten Eckpunkts 
     * @param  farbe  Fuellfarbe fuer das Dreieck, erlaubt sind "weiss" "schwarz" "rot"
     * "gruen" "blau" "gelb" "magenta" "cyan" "grau"
     */
    public void fuelleDreieck(int x1, int y1, int x2, int y2, int x3, int y3, String farbe){
    	fuellePolygon(farbe,x1,y1,x2,y2,x3,y3);
    }

    /**
     * Fuellt das Innere eines Dreiecks mit der angegebenen Farbe.
     * @param x1,y1 Koordinaten des ersten Eckpunkts 
     * @param x2,y2 Koordinaten des zweiten Eckpunkts 
     * @param x3,y3 Koordinaten des dritten Eckpunkts 
     * @param  farbnr  Fuellfarbnummer fuer das Dreieck (0 bis 8)
     */
    public void fuelleDreieck(int x1, int y1, int x2, int y2, int x3, int y3, int farbnr){
    	fuellePolygon(farbnr,x1,y1,x2,y2,x3,y3);
    }

    /**
     * Loescht das Innere eines Dreicks
     * @param x1,y1 Koordinaten des ersten Eckpunkts 
     * @param x2,y2 Koordinaten des zweiten Eckpunkts 
     * @param x3,y3 Koordinaten des dritten Eckpunkts 
     */
    public void loescheDreieck(int x1, int y1, int x2, int y2, int x3, int y3){
    	loeschePolygon(x1,y1,x2,y2,x3,y3);
    }
    
    /**
     * Zeichnet den Rand eines Polygons
     * @param cords Eckpunkte x und y abwechselnd mindestens 3 Eckpunkte
     * @return ist Polygon gueltig
     */
    public boolean zeichnePolygon(int... cords){
    	if(cords.length < 6 || !(cords.length%2 == 0))return false;
    	Polygon polygon = new Polygon();
    	for (int i = 0; i+1 < cords.length; i+=2) {
			polygon.addPoint(cords[i], cords[i+1]);
		}
    	canvas.addShape(polygon, rotationsWinkel, false, vordergrundFarbe);
    	return true;
    }
    
    /**
     * Fuellt ein Polygon
     * @param farbnr farbe des Polygons
     * @param cords Eckpunkte x und y abwechselnd mindestens 3 Eckpunkte
     * @return ist Polygon gueltig
     */
    public boolean fuellePolygon(int farbnr,int... cords){
    	if(cords.length < 6 || !(cords.length%2 == 0))return false;
    	Polygon polygon = new Polygon();
    	for (int i = 0; i+1 < cords.length; i+=2) {
			polygon.addPoint(cords[i], cords[i+1]);
		}
    	canvas.addShape(polygon, rotationsWinkel, true, farbeZuColor(farbnr));
    	if(autoMalen)male();
    	return true;
    }
    
    /**
     * Fuellt ein Polygon
     * @param farbe farbe des Polygons
     * @param cords Eckpunkte x und y abwechselnd mindestens 3 Eckpunkte
     * @return ist Polygon gueltig
     */
    public boolean fuellePolygon(String farbe,int... cords){
    	if(cords.length < 6 || !(cords.length%2 == 0))return false;
    	Polygon polygon = new Polygon();
    	for (int i = 0; i+1 < cords.length; i+=2) {
			polygon.addPoint(cords[i], cords[i+1]);
		}
    	canvas.addShape(polygon, rotationsWinkel, true, farbeZuColor(farbe));
    	if(autoMalen)male();
    	return true;
    }
    
    /**
     * Zeichnet den Rand eines Polygons
     * @param cords Eckpunkte x und y abwechselnd mindestens 3 Eckpunkte
     * @return ist Polygon gueltig
     */
    public boolean loeschePolygon(int... cords){
    	if(cords.length < 6 || !(cords.length%2 == 0))return false;
    	Polygon polygon = new Polygon();
    	for (int i = 0; i+1 < cords.length; i+=2) {
			polygon.addPoint(cords[i], cords[i+1]);
		}
    	loesche(polygon);
    	return true;
    }

    /**
     * Loescht den Inhalt des Zeichenfensters.
     */
    public void loescheAlles(){
        canvas.addClear(hintergrundFarbe);
        if(autoMalen)male();
    }

    /**
     * Loescht den Umriss eines Shape-Objekts.
     * @param  shape  das Shape-Object, dessen Umriss geloescht werden soll 
     */
    public void loescheRand(Shape shape){
       canvas.addShape(shape, rotationsWinkel, false, hintergrundFarbe);
        if(autoMalen)male();
    }

    /**
     * Zeichnet ein Bild in das Zeichnenfenster .
     * @param  bild    das anzuzeigende Bild 
     * @param  x       x-Koordinate des linken Bildrands 
     * @param  y       y-Koordinate des oberen Bildrands 
     * @return  gibt eines booleschen Wert zurueck, der angibt, ob das Bild vollstaendig geladen 
     *          werden konnte 
     */
    public boolean zeichneBild(Image bild, int x, int y){
    	if(bild.getWidth(null) == 0 || bild.getHeight(null) == 0)return false;
    	canvas.addImage(bild, rotationsWinkel, new Rectangle(x, y, bild.getWidth(null), bild.getHeight(null)));
        if(autoMalen)male();
        return true;
    }
    
    /**
     * Zeichnet ein Bild in das Zeichnenfenster .
     * @param  bild    das anzuzeigende Bild 
     * @param  x       x-Koordinate der oberen linken Ecke
     * @param  y       y-Koordinate der oberen linken Ecke 
     * @param  breite  breite des Bildes
     * @param  hoehe   hoehe des Bildes
     * @param winkel   Rotationswinkel des Bildes
     * @return  gibt eines booleschen Wert zurueck, der angibt, ob das Bild vollstaendig geladen 
     *          werden konnte 
     */
    public boolean zeichneBild(Image bild, int x, int y,int breite,int hoehe,float winkel){
    	if(bild.getWidth(null) == 0 || bild.getHeight(null) == 0)return false;
    	canvas.addImage(bild, winkel, new Rectangle(x, y, bild.getWidth(null), bild.getHeight(null)));
        if(autoMalen)male();
        return true;
    }

    /**
     * Zeichnet einen Text.
     * @param  text    die anzuzeigende Zeichenkette 
     * @param  x       x-Koordinate des linken Rands 
     * @param  y       y-Koordinate des oberen Rands 
     */
    public void zeichneText(String text, int x, int y){
    	canvas.addString(text, rotationsWinkel, schriftArt, vordergrundFarbe, x, y);
        if(autoMalen)male();
    }

    /**
     * Loescht einen Text vom Zeichenfenster.
     * @param  text    die zu loeschende Zeichenkette 
     * @param  x       x-Koordinate des linken Rands 
     * @param  y       y-Koordinate des oberen Rands 
     */
    public void loescheText(String text, int x, int y){
    	canvas.addString(text, rotationsWinkel, schriftArt, hintergrundFarbe, x, y);
        if(autoMalen)male();
    }

    /**
     * Zeichnet eine Strecke ins Zeichenfenster.
     * @param  x1   x-Koordinate des Anfangspunkts der Strecke 
     * @param  y1   y-Koordinate des Anfangspunkts der Strecke
     * @param  x2   x-Koordinate des Endpunkts der Strecke 
     * @param  y2   y-Koordinate des Endpunkts der Strecke  
     */
    public void zeichneStrecke(int x1, int y1, int x2, int y2){
        canvas.addShape(new Line2D.Float(x1, y1, x2, y2), rotationsWinkel, false, vordergrundFarbe);
        if(autoMalen)male();
    }
    
    /**
     * Setzt den RotationsWinkel fuer alle Objekte
     * @param winkel in grad
     */
    public void setzeRotationsWinkel(float winkel){
    	rotationsWinkel = winkel;
    }
    
    /**
     * Liest den Rotationswinkel winkel aus
     * @return winkel in grad
     */
    public float gibRotationsWinkel(){
    	return rotationsWinkel;
    }

    /**
     * Setzt die Vordergrundfarbe des Zeichenfensters.
     * @param  neueFarbe   neue Vordergrundfarbe 
     */
    public void setzeVordergrundFarbe(String neueFarbe){
        vordergrundFarbe = farbeZuColor(neueFarbe);
    }
    
    /**
     * Setzt die Vordergrundfarbe des Zeichenfensters.
     * @param  neueFarbe   neue Vordergrundfarbe 
     */
    public void setzeVordergrundFarbe(int neueFarbe){
        vordergrundFarbe = farbeZuColor(neueFarbe);
    }
    
    private Color farbeZuColor(int farbnr){
    	if(spezialFarben)return new Color(farbnr,true);
        switch (farbnr)
        {
            case 0: return Color.black;
            case 1: return Color.blue;
            case 2: return Color.green;
            case 3: return Color.cyan;
            case 4: return Color.red;
            case 5: return Color.magenta;
            case 6: return Color.yellow;
            case 7: return Color.gray;
            case 8: return Color.white;
            default: return vordergrundFarbe;
        }
        
    }
    
    private Color farbeZuColor(String farbe){
        if (farbe=="weiss") return Color.white;
        if (farbe=="schwarz") return Color.black;
        if (farbe=="rot") return Color.red;
        if (farbe=="gruen") return Color.green;
        if (farbe=="blau") return Color.blue;
        if (farbe=="gelb") return Color.yellow;
        if (farbe=="magenta") return Color.magenta;
        if (farbe=="cyan") return Color.cyan;
        if (farbe=="grau") return Color.gray;
        return vordergrundFarbe;
    }
    
    private String colorZuFarbe(Color color){
        if (color==Color.white) return "weiss";
        if (color==Color.black) return "schwarz";
        if (color==Color.red) return "rot";
        if (color==Color.green) return "gruen";
        if (color==Color.blue) return "blau";
        if (color==Color.yellow) return "gelb";
        if (color==Color.magenta) return "magenta";
        if (color==Color.cyan) return "cyan";
        if (color==Color.gray) return "grau";
        return color.toString();
    }
      
    /**
     * Aktiviert Spezialfarben
     * farbnummern werden nun wie rgb Integer behandelt,
     * diese koennnen mit den Methoden "spezilaFarbe" generiert werden.
     */
    public void aktiviereSpezialfarben(){
    	spezialFarben = true;
    }
    
    /**
     * Deaktiviert Spezialfarben
     * nun werden die Standart farbnummern verwendet
     */
    public void deaktiviereSpezialfarben(){
    	spezialFarben = false;
    }
    
    /**
     * Generiert Spezialfarbe(rgba Integer) 
     * @param rot rot Wert 0-1 float werte
     * @param gruen gruen Wert 0-1 float werte
     * @param blau blau Wert 0-1 float werte
     * @return rgb Integer kann bei aktivierten Spezialfarben verwendet werden.
     */
    public int spezialFarbe(float rot,float gruen,float blau){
    	return new Color(rot, gruen, blau, 1f).getRGB();
    }
    
    /**
     * Generiert Spezialfarbe(rgba Integer) 
     * @param rot rot Wert 0-255 int werte
     * @param gruen gruen Wert 0-255 int werte
     * @param blau blau Wert Wert 0-255 int werte
     * @return rgb Integer kann bei aktivierten Spezialfarben verwendet werden.
     */
    public int spezialFarbe(int rot,int gruen,int blau){
    	return new Color(rot, gruen, blau, 255).getRGB();
    }
    
    /**
     * Generiert Spezialfarbe(rgba Integer) 
     * @param rot rot Wert 0-1 float werte
     * @param gruen gruen Wert 0-1 float werte
     * @param blau blau Wert 0-1 float werte
     * @param alpha Dekkraft 0-255
     * @return rgb Integer kann bei aktivierten Spezialfarben verwendet werden.
     */
    public int spezialFarbe(float rot,float gruen,float blau,float alpha){
    	return new Color(rot, gruen, blau, alpha).getRGB();
    }
    
    /**
     * Generiert Spezialfarbe(rgba Integer) 
     * @param rot rot Wert 0-255 int werte
     * @param gruen gruen Wert 0-255 int werte
     * @param blau blau Wert Wert 0-255 int werte
     * @param alpha Dekkraft 0-255
     * @return rgb Integer kann bei aktivierten Spezialfarben verwendet werden.
     */
    public int spezialFarbe(int rot,int gruen,int blau,int alpha){
    	return new Color(rot, gruen, blau, alpha).getRGB();
    }

    /**
     * Gibt die aktuelle Vordergrundfarbe des Zeichenfensters zurueck.
     * @return   die aktuelle Vordergrundfarbe 
     */
    public String gibVordergrundFarbe(){
        return colorZuFarbe(vordergrundFarbe);
    }

    /**
     * Setzt die Hintergrundfarbe des Zeichenfensters.
     * @param  neueFarbe   neue Hintergrundfarbe 
     */
    public void setzeHintergrundFarbe(String neueFarbe){
        hintergrundFarbe = farbeZuColor(neueFarbe);   
    }
    
    /**
     * Setzt die Hintergrundfarbe des Zeichenfensters.
     * @param  neueFarbe   neue Hintergrundfarbe 
     */
    public void setzeHintergrundFarbe(int neueFarbe){
        hintergrundFarbe = farbeZuColor(neueFarbe);   
    }

    /**
     * Gibt die aktuelle Hintergrundfarbe des Zeichenfensters zurueck.
     * @return   die aktuelle Hintergrundfarbe 
     */
    public String gibHintergrundFarbe(){
        return colorZuFarbe(hintergrundFarbe);
    }

    /**
     * Aendert den aktuellen Zeichensatz des Zeichenfensters.
     * @param  neuerZeichensatz   Zeichensatz, der kuenftig fuer Zeichenkettenausgaben verwendet wird
     */
    public void setzeZeichensatz(Font neuerZeichensatz){
        schriftArt = neuerZeichensatz;
    }

    /**
     * Gibt den aktuellen Zeichensatz des Zeichenfensters zurueck.
     * @return     den aktuellen Zeichensatz
     **/
    public Font gibZeichensatz(){
        return schriftArt;
    }

    /**
     * Aendert die Abmessungen des Zeichenfensters.
     * @param  breite    neue Breite 
     * @param  hoehe     neue Hoehe 
     */
    @Deprecated
    public void setzeMasse(int breite, int hoehe){
        canvas.setPreferredSize(new Dimension(breite, hoehe));
        frame.pack();
    }
    
    /**
     * Aendert die Abmessungen des Zeichenfensters.
     * @param  breite    neue Breite 
     * @param  hoehe     neue Hoehe 
     */
    public void setzeAbmessung(int breite, int hoehe){
        canvas.setPreferredSize(new Dimension(breite, hoehe));
        frame.pack();
    }

    /**
     * Gibt die Abmessungen des Zeichenfensters zurueck.
     * @return  die aktuellen Abmessungen des Zeichenfensters
     */
    @Deprecated
    public Dimension gibMasse(){
        return canvas.getSize();
    }
    
    /**
     * Gibt die Abmessungen des Zeichenfensters zurueck.
     * @return  die aktuellen Abmessungen des Zeichenfensters
     */
    public Dimension gibAbmessungen(){
        return canvas.getSize();
    }

    /**
     * Wartet eine bestimmte Zeit.
     * Eine kurze Verzoegerung kann z. B. fuer Animationen verwendet werden.
     * @param  zeit  Wartezeit in Millisekunden 
     */
    public void warte(int zeit){
        try
        {
            Thread.sleep(zeit);
        } 
        catch (InterruptedException e)
        {
            // sehr unwarschenliche Ausnahme
        }
    }
    
    /**
     * Erzeugt eine zufaellige ganzzahl (auch negativ)
     * @param grenzwert1
     * @param grenzwert2
     * @return zufalszahl zwischen grenzwert1 und grenzwert2 eingeschlossen
     */
    public int zufallsZahl(int grenzwert1 ,int grenzwert2){
    	if(grenzwert1 > grenzwert2){
    		int t = grenzwert1;
    		grenzwert1 = grenzwert2;
    		grenzwert2 = t;
    	}
    	int temp = zufallsGenerator.nextInt(1+grenzwert2-grenzwert1)+grenzwert1;
    	return temp;
    }

    /**
     * Fuegt ein weiteres Steuerungselement in die rechte Steuerungsleiste ein.
     * @param  element  Das einzufuegende Steuerungselement muss aus JComponent abgeleitet
     * sein. z. B. JButton, JComboBox. 
     */
    public void komponenteHinzufuegen(JComponent element, String position){
        if (position.equals("rechts")) steuerungOst.add(element);
        else if (position.equals("unten")) steuerungSued.add(element);
        frame.pack();
    }
    
    /**
     * Gibt das frame Object zurueck
     * ermoeglicht z.B. das Verschtecken des Fensters
     * @return frame Object
     */
    public JFrame gibRahmen(){
    	return frame;
    }
    
    /**
     * Gibt das CanvasPane Object als JPanel zurueck
     * ermoeglicht z.B. das Registrieren von Mousevents
     * @return frame Object
     */
    public JPanel gibZeichenflaeche(){
		return canvas;
    }
    
    /**
     * Beschriftet den Titel des Zeichenfensters neu.
     * @param  titelNeu  Text der neuen Fensterueberschrift
     */
    public void setzeTitel(String titelNeu){
        frame.setTitle(titelNeu);
    }
     
    /************************************************************************
     * Verschachtelte Klasse CanvasPane - 
     * Element (JPanel) auf dem gezeichnet werden kann
     * @author Daniel Keitel
     * @version 2015.12.20
     */
    @SuppressWarnings("unused")
    private class CanvasPane extends JPanel{
		private static final long serialVersionUID = -7848726259938668008L;
		private List<Shape2D> commands = new ArrayList<Shape2D>();
		private List<Shape2D> buffer = new ArrayList<Shape2D>();
		private Graphics2D graphics;
		private BufferedImage image;
		private int w,h;
		private Color backgroundColor = Color.DARK_GRAY;
		private Lock lock = new ReentrantLock();
		
		public CanvasPane(){
			w = Toolkit.getDefaultToolkit().getScreenSize().width;
			h = Toolkit.getDefaultToolkit().getScreenSize().height;
		}
		
		public CanvasPane(int maxW , int maxH,Color backgrundColor){
			this.backgroundColor = backgrundColor;
			w = Math.min(maxW, Toolkit.getDefaultToolkit().getScreenSize().width);
			h = Math.min(maxH, Toolkit.getDefaultToolkit().getScreenSize().height);
		}
		
		public void setMax(int maxW , int maxH,Color backgrundColor){
			this.backgroundColor = backgrundColor;
			w = Math.min(maxW, Toolkit.getDefaultToolkit().getScreenSize().width);
			h = Math.min(maxH, Toolkit.getDefaultToolkit().getScreenSize().height);
		}
		
		public void setBackgroundColor(Color color){
			backgroundColor = color;
		}
		
		public void addShape(Shape shape,float angle,boolean fill,Color color){
			buffer.add(new Shape2D(shape, angle, fill, color));
		}
		
		public void addClear(Color backgroundColor){
			buffer.add(new Shape2D(new Rectangle(0, 0, w, h), 0f,true,backgroundColor));
		}
		
		public void addImage(Image image, float angle,Rectangle bounds){
			if(image.getWidth(null) < 1 || image.getHeight(null) < 1)return;
			buffer.add(new Shape2D(image, angle, bounds));
		}
		
		public void addString(String text,float angle,Font font,Color color,int x,int y){
			buffer.add(new Shape2D(text, angle, color, font, x, y));
		}

		public Dimension getSize(){
			return new Dimension(w, h);
		}
		
		public void draw(){
			lock.lock();
			commands.addAll(buffer);
			buffer.clear();
			lock.unlock();
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			repaint();
		}
		long time = 0;
		@Override
		public void paint(Graphics g) {
			System.out.println("A: "+(System.nanoTime() - time)/1000000f);
			time = System.nanoTime();
			if(image == null){
				image = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
			}
			if(image.getWidth(null) != w || image.getHeight(null) != h){
				Image oldImage = image;
				image = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
		        graphics = (Graphics2D)image.getGraphics();
		        graphics.setColor(backgroundColor);
		        graphics.fillRect(0, 0, w, h);
		        graphics.drawImage(oldImage, 0, 0, null);
			}
			graphics = (Graphics2D) image.getGraphics();
			lock.lock();
			while(commands.size() > 0){
				Shape2D s = commands.remove(0);
				Graphics2D temp;
				switch (s.o) {
				case 0:
					temp = (Graphics2D) graphics.create();
					temp.rotate(Math.toRadians(s.angle), s.shape.getBounds().getX(), s.shape.getBounds().getY());
					temp.setColor(s.color);
					temp.draw(s.shape);
					break;
				case 1:
					temp = (Graphics2D) graphics.create();
					temp.rotate(Math.toRadians(s.angle), s.shape.getBounds().getX(), s.shape.getBounds().getY());
					temp.setColor(s.color);
					temp.fill(s.shape);
					break;
				case 2:
					temp = (Graphics2D) graphics.create();
					temp.rotate(Math.toRadians(s.angle), s.bounds.x, s.bounds.y);
					temp.drawImage(s.image, s.bounds.x, s.bounds.y, s.bounds.width, s.bounds.height, null);
					break;
				case 3:
					temp = (Graphics2D) graphics.create();
					temp.rotate(Math.toRadians(s.angle), s.bounds.x, s.bounds.y);
					temp.setColor(s.color);
					temp.setFont(s.font);
					temp.drawString(s.text, s.bounds.x, s.bounds.y);
					break;
				}
			}
			lock.unlock();
			g.drawImage(image, 0, 0, null);
			graphics.dispose();
			System.out.println("B: "+(System.nanoTime() - time)/1000000f);
			time = System.nanoTime();
		}
		
		private class Shape2D{
			public Shape shape;
			public Rectangle bounds;
			public Image image;
			public String text;
			public Font font;
			public int o;//Operation: 0:draw 1:fill 2:image 3:Text
			public float angle;
			public Color color;
			public Shape2D(Shape shape,float angle, boolean fill,Color color){
				this.shape = shape;
				this.color = color;
				this.angle = angle;
				o = fill ? 1 : 0;
			}
			public Shape2D(Image image,float angle,Rectangle bounds){
				this.image = image;
				this.bounds = bounds;
				this.angle = angle;
				o = 2;
			}
			public Shape2D(String text,float angle, Color color,Font font,int x,int y){
				this.text = text;
				this.font = font;
				this.color = color;
				this.angle = angle;
				bounds = new Rectangle(x, y, 0, 0);
				o = 3;
			}
		}
	}
    
  //Eingabe Elemente*********************************************************************
    private boolean hoch,links,runter,rechts = false;
    private int x,y = 0;
    private boolean rechtsklick,linksklick = false;
    /**
     * Initialisiert die Maus events
     * ermoeglicht den Zugrif auf Methoden zum abfragen des Maus Status
     * istLinksKlick()
     * istRechtsKlick()
     * gibMausX()
     * gibMausX()
     */
    public void initialisiereMausEvents(){
    	canvas.addMouseListener(new MouseListener() {
			@Override
			public void mouseReleased(MouseEvent e) {
				x = Math.max(0, Math.min(e.getX(), canvas.getWidth()));
				y = Math.max(0, Math.min(e.getY(), canvas.getHeight()));
				if(e.getButton() == MouseEvent.BUTTON1){
					linksklick = false;
				}
				if(e.getButton() == MouseEvent.BUTTON3){
					rechtsklick = false;
				}
			}
			@Override
			public void mousePressed(MouseEvent e) {
				x = Math.max(0, Math.min(e.getX(), canvas.getWidth()));
				y = Math.max(0, Math.min(e.getY(), canvas.getHeight()));
				if(e.getButton() == MouseEvent.BUTTON1){
					linksklick = true;
				}
				if(e.getButton() == MouseEvent.BUTTON3){
					rechtsklick = true;
				}
			}
			@Override
			public void mouseExited(MouseEvent e) {
				x = Math.max(0, Math.min(e.getX(), canvas.getWidth()));
				y = Math.max(0, Math.min(e.getY(), canvas.getHeight()));
			}
			@Override
			public void mouseEntered(MouseEvent e) {
				x = Math.max(0, Math.min(e.getX(), canvas.getWidth()));
				y = Math.max(0, Math.min(e.getY(), canvas.getHeight()));
			}
			@Override
			public void mouseClicked(MouseEvent e) {
				x = Math.max(0, Math.min(e.getX(), canvas.getWidth()));
				y = Math.max(0, Math.min(e.getY(), canvas.getHeight()));
			}
		});
    	
    	canvas.addMouseMotionListener(new MouseMotionListener() {
			@Override
			public void mouseMoved(MouseEvent e) {
				x = Math.max(0, Math.min(e.getX(), canvas.getWidth()));
				y = Math.max(0, Math.min(e.getY(), canvas.getHeight()));			
			}
			@Override
			public void mouseDragged(MouseEvent e) {
				x = Math.max(0, Math.min(e.getX(), canvas.getWidth()));
				y = Math.max(0, Math.min(e.getY(), canvas.getHeight()));
			}
		});
    }
    
    /**
     * Initialisiert Tastatur events
     * ermoeglicht richtungs kontrolle Hoch Links Runter Rechts ueber
     * WASD oder die Pfeiltasten (gleichzeitig nutzbar)
     * funktioniert im gesamten frame auch bei selektierten Komponenten
     * istLinks()
     * istRechts()
     * istHoch()
     * istRunter()
     */
    public void initialisiereTastaturEvents(){
    	KeyboardFocusManager focusManager = new DefaultFocusManager(){
    		@Override
    		public boolean dispatchKeyEvent(KeyEvent e) {
    			if(e.getID() == KeyEvent.KEY_RELEASED){
    				if(e.getKeyCode() == KeyEvent.VK_W || e.getKeyCode() == KeyEvent.VK_UP){
    					hoch = false;
    					return true;
    				}
    				if(e.getKeyCode() == KeyEvent.VK_D || e.getKeyCode() == KeyEvent.VK_RIGHT){
    					rechts = false;
    					return true;
    				}
    				if(e.getKeyCode() == KeyEvent.VK_S || e.getKeyCode() == KeyEvent.VK_DOWN){
    					runter = false;
    					return true;
    				}
    				if(e.getKeyCode() == KeyEvent.VK_A || e.getKeyCode() == KeyEvent.VK_LEFT){
    					links = false;
    					return true;
    				}
    			}
    			if(e.getID() == KeyEvent.KEY_PRESSED){
    				if(e.getKeyCode() == KeyEvent.VK_W || e.getKeyCode() == KeyEvent.VK_UP){
    					hoch = true;
    					return true;
    				}
    				if(e.getKeyCode() == KeyEvent.VK_D || e.getKeyCode() == KeyEvent.VK_RIGHT){
    					rechts = true;
    					return true;
    				}
    				if(e.getKeyCode() == KeyEvent.VK_S || e.getKeyCode() == KeyEvent.VK_DOWN){
    					runter = true;
    					return true;
    				}
    				if(e.getKeyCode() == KeyEvent.VK_A || e.getKeyCode() == KeyEvent.VK_LEFT){
    					links = true;
    					return true;
    				}
    			}
    			return false;
    		}
    	};
    	KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(focusManager);
    }
    
    /**
     * Initialisiert einen Keybord focus Manager
     * @param focusManager instanz eines KeyboardFocusManagers
     */
    public void initialisiereKeyboardFocusManager(KeyboardFocusManager focusManager){
    	KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(focusManager);
    }
    
    /**
     * Ueber prueft ob Tasten gedrueckt sind
     * siehe initialisiereTastaturEvents()
     * @return ist W oder Pfeiltaste Hoch gedrueckt
     */
    public boolean istHoch(){
		return hoch;
    }
    
    /**
     * Ueberprueft ob Tasten gedrueckt sind
     * siehe initialisiereTastaturEvents()
     * @return ist S oder Pfeiltaste Runter gedrueckt
     */
    public boolean istRunter(){
		return runter;
    }
    
    /**
     * Ueberprueft ob Tasten gedrueckt sind
     * siehe initialisiereTastaturEvents()
     * @return ist A oder Pfeiltaste Links gedrueckt
     */
    public boolean istLinks(){
		return links;
    }
    
    /**
     * Ueberprueft ob Tasten gedrueckt sind
     * siehe initialisiereTastaturEvents()
     * @return ist D oder Pfeiltaste Rechts gedrueckt
     */
    public boolean istRechts(){
		return rechts;
    }
    
    /**
     * Ueberpruefung des Zustands des Linken Mausbuttons
     * @return ist der linke Mausbutton gedrueckt
     * Weiteres siehe initialisiereMausEvents()
     */
    public boolean istLinksKlick(){
		return linksklick;
    }
    
    /**
     * Ueberpruefung des Zustands des Rechten Mausbuttons
     * @return ist der rechte Mausbutton gedrueckt
     * Weiteres siehe initialisiereMausEvents()
     */
    public boolean istRechtsKlick(){
		return rechtsklick;
    }
    
    /**
     * Ueberpruefung der Mausposition
     * @return letzter x wert der Maus auf der Zeichenflaeche
     * Weiteres siehe initialisiereMausEvents()
     */
	public int gibMausX() {
		return x;
	}
	
	 /**
     * Ueberpruefung der Mausposition
     * @return letzter y wert der Maus auf der Zeichenflaeche
     * Weiteres siehe initialisiereMausEvents()
     */
	public int gibMausY() {
		return y;
	}
	
	//Updateschleife
	private final boolean GENAUEDELAYS = true;
	private final String SCHLEIFENMETHODENNAME = "schleife";
	private boolean schleifeLaeuft = false;
	private int schleifenZeit = 0;//In nanoSekunden
	private float letzteDauer = 0;
	
	/**
	 * Startet eine schleife mit festgelegter Wiederholungs rate
	 * fuehrt die public Methode mit dem Namen, der in der Konstante SCHLEIFENMETHODENNAME ("schleife") gespeichert ist
	 * aus der klasse klasse mit einer Wiederholungs Frequenz aktualisierungenProSekunde aus
	 * @param klasse instanz einer Klasse mit der Methode public void schleife()
	 * @param aktualisierungenProSekunde maximale Wiederholungsfrequenz in Hz
	 * @return konnte die Methode zum ausfuehren gefunden werden
	 */
	public boolean starteSchleife(Object klasse,float aktualisierungenProSekunde){
		if(aktualisierungenProSekunde == 0){
			schleifenZeit = 0;
		}else{
			schleifenZeit = (int)(1000000000f / aktualisierungenProSekunde);
		}
		if(schleifeLaeuft)return true;
		try {
			java.lang.reflect.Method m = klasse.getClass().getMethod(SCHLEIFENMETHODENNAME);
			Thread t = new Thread(new Runnable() {
				@Override
				public void run() {
					while(schleifeLaeuft){
						try {
							if(schleifenZeit == 0){
								Thread.sleep(10);
							}else{
								long systemZeit = System.nanoTime();
								m.invoke(klasse);
								letzteDauer = (System.nanoTime() - systemZeit)/1000000f;
								long delayZeit = schleifenZeit - (System.nanoTime() - systemZeit);
								if(delayZeit > 0){
									if(GENAUEDELAYS){
										while(0 < schleifenZeit - (System.nanoTime() - systemZeit));
									}else{
										warte((int)(delayZeit/1000000f));
									}
								}
							}
							
						} catch (Exception e) {
							System.out.println("Es hat sich ein fehler in der Schleife Ereignet:");
							e.printStackTrace();
						    schleifeLaeuft = false;
						}
					}
				}
			});
			schleifeLaeuft = true;
			t.start();
		} catch (Exception e) {
			schleifeLaeuft = false;
			return false;
		}
		return true;
	}
	
	/**
	 * Bricht die Laufende Schleife ab
	 */
	public void stopeSchleife(){
		schleifeLaeuft = false;
	}
	
	/**
	 * Ueberprueft ob eine Schleife laeuft
	 * @return laeuft schleife
	 */
	public boolean istSchleifeGestarted(){
		return schleifeLaeuft;
	}
	
	/**
	 * Aendert die Wiederholugs frequenz (kann auf 0 gesetzt werden um die Schleife zu Pausieren)
	 * @param aktualisierungenProSekunde maximale wiederholungs frequenz in Hz
	 */
	public void setzeAktualisierungenProSekunde(float aktualisierungenProSekunde){
		if(aktualisierungenProSekunde == 0){
			schleifenZeit = 0;
		}else{
			schleifenZeit = (int)(1000000000f / aktualisierungenProSekunde);
		}
	}
	
	/**
	 * Gibt die Dauer der Berechnung der letzen erfolgreich ausgefuehrten Schleife zurueck
	 * @return letze dauer in Millisecunden
	 */
	public float letzteDauer(){
		return letzteDauer;
	}
}