package stiftUndCo;

import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.awt.AWTEvent.*;
import javax.swing.*;


/**
 Ein Bildschirm ist das Modell eines rechteckigen Bereiches auf dem
 angeschlossenen Computerbildschirm.
 Auf ihm kann mit Stiften gezeichnet werden. Zu diesem Zweck ist die
 Zeichenebene auf dem Bildschirm mit einem Koordinatensystem versehen,
 dessen Ursprung sich in der oberen linken Ecke der Zeichenebene befindet
 und dessen Achsen horizontal nach rechts und vertikal nach unten
 gerichtet sind. Die Einheit ist ein Pixel.
 Die Implementierung enthält einige Zusätze zum Standard der Stifte und Mäuse,
 etwa die Möglichkeit für double-buffering.

@author Georg Dick
@version 9.12.98

  */
public class Bildschirm extends Frame implements MalFenster, WindowListener ,ComponentListener
{
	/**
	Der >hauptbildschirm< ist das erste Bildschirmobjekt, das von einer Anwendung erzeugt wird.
	Darauf greifen Stift, Maus etc. zu, wenn sie mit den jeweiligen parameterfreien Konstruktoren
	erzeugt werden.
	@see Maus#Maus()
	@see Tastatur#Tastatur()
	@see Stift#Stift()
	@see BuntStift#BuntStift()
	*/
	//public static MalFenster hauptbildschirm=null;

	private Image dbImage;
	    private Graphics dbGraphics;



	protected boolean autoclose=true;
	private Color zhintergrundfarbe=Farbe.WEISS;
	private int zhoehe=0,zbreite=0;
	protected Graphics grafikKontext=null;
	protected Graphics hggrafikkontext=null;
	protected Image hintergrundbild=createImage(1,1);
	protected boolean zhintergrundmodus=false;
	protected EreignisAnwendung meineEreignisAnwendung=null;
	protected boolean andereEreignisseInitialisiert=false;
	protected boolean andereEreignisseErwünscht=false;
	protected boolean laufeAlsApplet=false;
	public void init(){;}
	public void bearbeiteFensterWirdGeschlossen(){;}
	protected boolean laeuftInApplet()

	{

		try
		{
			getToolkit().getSystemEventQueue();
		}
		catch (Exception e) { laufeAlsApplet=true;}
		return laufeAlsApplet;
	}

	protected  void bearbeiteAndereEreignisse()
	{
		if (meineEreignisAnwendung!=null)
		{
			meineEreignisAnwendung.bearbeiteAndereEreignisse();
		//	this.dispatchEvent(new AnderesEreignis(new Event(this,java.awt.AWTEvent.RESERVED_ID_MAX+1,this)));
		}
	}

	protected void addAndereEreignisseListener(EreignisAnwendung ea)
	{
		if (! this.laeuftInApplet())
		{
		this.meineEreignisAnwendung=ea;
		andereEreignisseErwünscht=true;
		this.dispatchEvent(new AnderesEreignis(new Event(this,java.awt.AWTEvent.RESERVED_ID_MAX+1,this)));
		/* Stößt processevent an. Sonst wartet das System auf das erste
		Event, z.B. eine Mausbewegung */
		}
	}


	/**
	Um andere Ereignisse als die Standardereignisse zu bearbeiten, wird
	in die Systemereignisschlange ein neues Ereignis eingefügt. Dessen Bearbeitung
	zieht den Aufruf von BearbeiteAndereEreignisse nach sich und ein erneutes
	Einfügen eines Ereignisses in die Ereignisschlange, so dass dieser Vorgang wiederholt wird.
	Ein eleganterer Weg, direkt per dispatchEvent auf ein "anderes Ereignis"
	zu reagieren, führt zu unschönen Nebeneffekten, insbeondere Netscape
	bearbeitet dann nur noch andere Ereignisse. Offenbar werden diese von der verwendeten VM vorne in die
	Ereignisschlange eingereiht und nicht hinten.
	Ein alternativer Ansatz läuft über Threads: Dazu starte man einen Thread der in kurzen Abständen
	bearbeiteAndereEreignisse aufruft.<br>
	Diesen Ansatz habe ich wieder verworfen, da die Antwortzeiten zu lange sind.<br>
	Für Applets ist er jedoch möglich.
	Noch eleganter wäre es, wenn man weitere Ereignisse dem Java-Event-Delegation-
	Modell entsprechend einfügen würde.
	Ein Problem stellen allerdings Applets dar:
	Der Zugriff auf die System Ereignisschlange ist aus Sicherheitsgründen
	nicht gestattet.

	*/
	protected void processEvent(AWTEvent e)
	{

		if (andereEreignisseErwünscht)
		{
			if (e.getID()==java.awt.AWTEvent.RESERVED_ID_MAX+1)
			{

				this.bearbeiteAndereEreignisse();

				andereEreignisseInitialisiert=true;
				try{
				getToolkit().getSystemEventQueue().postEvent(new AnderesEreignis(new Event(this,java.awt.AWTEvent.RESERVED_ID_MAX+1,this)));
				} catch (Exception ausnahme) {
					System.out.println("!!!"+ausnahme.toString());
					andereEreignisseInitialisiert=false;
				}
			}


			if (!andereEreignisseInitialisiert)
			{
				try
				{
				  getToolkit().getSystemEventQueue().postEvent(new AnderesEreignis(new Event(this,java.awt.AWTEvent.RESERVED_ID_MAX+1,this)));
				  //info("anderes Ereignis erzeugt");Hilfe.warte(100);
				} catch (Exception ausnahme) {System.out.println("!"+ausnahme.toString());}
			}


		}

	/*	if (e.getID()==java.awt.AWTEvent.RESERVED_ID_MAX+1)
		{this.bearbeiteAndereEreignisse();}
	*/
		super.processEvent(e);
	}


	public void info(String s)
	{
		System.out.println(s);
	}
	/**
	Konstruktor	Bildschirm<br>
    nachher:	Der Bildschirm ist initialisiert. Breite 400, Hoehe 300 Pixel
	*/
	public Bildschirm(){
		this(400,300,"Bildschirm");
	}

	/**
	Konstruktor	Bildschirm<br>
    nachher:	Der Bildschirm ist  initialisiert.
	@param breite bestimmt die Breite des Bildschirms
	@param hoehe bestimmt die Hoehe des Bildschirms
	*/
	public Bildschirm (int breite, int hoehe)
	{
	  super();
	// getContentPane().setDoubleBuffered(true);
            addWindowListener(this);
	  addComponentListener(this);
	  this.setLayout(new FlowLayout());
	  this.setBackground(zhintergrundfarbe);
	  this.setVisible(true);
	  //addComponentListener(this);
	  zbreite=breite+this.getInsets().left+this.getInsets().right;
	  zhoehe=hoehe+this.getInsets().top+this.getInsets().bottom;
	  this.setSize(zbreite,zhoehe);
	  //this.setLayout(null);
	  grafikKontext.translate(this.getInsets().left,this.getInsets().top);
	  zhintergrundmodus=false;
	  this.registriere();
	  //this.requestFocus();
	  this.toFront();
	  hintergrundbild = createImage(getSize().width,getSize().height);
	  Hilfe.warte(1000);
	  autoclose=true;

	}


	/**
	Konstruktor	Bildschirm<br>
    nachher:	Der Bildschirm ist  initialisiert.
	@param breite bestimmt die Breite des Bildschirms
	@param hoehe bestimmt die Hoehe des Bildschirms
	@param titel bestimmt den Fenstertitel
	*/
	public Bildschirm (int breite, int hoehe, String titel)
	{
	  this(breite,hoehe);
	  this.setTitle(titel);
	}
	/** Macht einen Grafikkontext für dieses Fenster dauerhaft(!) verfügbar.
	Auch außerhalb von paint! */
	public synchronized void addNotify()
	{
		super.addNotify();
		grafikKontext = this.getGraphics();

	}

	/**
	Das Hintergrundbild wird angezeigt
	@see Bildschirm#setzeHintergrundmodus
	*/
	public void aktualisiere()
	{
		if (zhintergrundmodus)
		{
		    grafikKontext.drawImage(hintergrundbild,0,0,this);
		}
	}

	/**
	stellt einen Grafikkontext für Malwerkzeuge, wie den Stift,
	zur Verfügung.
	*/

	public Graphics aktuellerGrafikkontext()
	{
		if (zhintergrundmodus)
		{
		     return hggrafikkontext;
		} else
		{
		    /*Graphics gr=this.getGraphics();
			gr.translate(this.getInsets().left,this.getInsets().top);
			if (gr!= null)
			{
				return gr;
			} else */ //Möglicherweise Speicherplatzproblem?
			return grafikKontext; // zur Sicherheit in der Testphase
		}
	}

	/**
	liefert den Wert wahr, wenn double-buffering benutzt wird
	*/
	public boolean hintergrundmodus()
	{
		return zhintergrundmodus;
	}

	public void componentShown(ComponentEvent e){
		//info ("Cshow");
	}
	public void componentResized(ComponentEvent e)
	{
		//info("CResized")		;
		bearbeiteFensterverändert();
	}
	public void componentHidden(ComponentEvent e)
	{   //info("CHidden")		;
		bearbeiteFensterverändert();
	}
	public void componentMoved(ComponentEvent e)
	{  //info("CMoved");
		bearbeiteFensterverändert();
	}


	public void windowActivated (WindowEvent e)
	{
		//info("Wactivated");
		//bearbeiteFensterverändert();
		}
	public void windowClosed(WindowEvent e)
	{
		//info("WClosed");
	}
	public void windowDeactivated(WindowEvent e)
	{
		//info("WDeactivated");
		//bearbeiteFensterverändert();
		}
	public void windowDeiconified(WindowEvent e)
	{
		//info("WDeiconified");
		//bearbeiteFensterverändert();
	}
	public void windowIconified(WindowEvent e)
	{
		//info("WIconified");
		//bearbeiteFensterverändert();
	}
	public void windowOpened(WindowEvent e)
	{
		//info("WOpened");
		//bearbeiteFensterverändert();
	}

	public void windowClosing(WindowEvent e)
	{
		//info("WClosing");
		this.bearbeiteFensterWirdGeschlossen();
		if (autoclose) {this.gibFrei();}
	}

	/** Wenn das Fenster durch Druck auf den Schliessen-Button
	geschlossen werden soll, muß setzeFensterschliessen mit dem
	Paramterewert true aufgerufen werden.
	Dies ist auch die Default-Einstellung  */
	public void setzeFensterschliessen(boolean ac)
	{
		autoclose=ac;
	}

	public boolean FensterSchliesstAutomatisch()
	{
		return autoclose;
	}

	/**
	paßt den Fensterinhalt an, wenn das Fenster bewegt wurde
	oder die Größe sich geändert hat. Die Methode ist noch nicht
	"fertig".
	*/
	public void bearbeiteFensterverändert()
	{

		if (zhintergrundmodus)
		{//info("Änderung");
		Image hilf=createImage(getSize().width,getSize().height);
		hilf.getGraphics().drawImage(hintergrundbild,0,0,this);
		hintergrundbild=null;
		hggrafikkontext= null;
		System.gc();
		hintergrundbild=hilf;
		hggrafikkontext= hintergrundbild.getGraphics();
		//hggrafikkontext.translate(this.getInsets().left,this.getInsets().top);
		hilf=null;
		}
	}

	/**
	Anfrage	breite() liefert die Breite der Zeichenebene.
	*/
	public int breite()
	{
		return this.getSize().width-getInsets().left-getInsets().right;
	}

	protected Graphics g() {
	 return grafikKontext;
	}
	/**
	das Fenster wird abgebaut
	*/
	public void gibFrei()
	{
		this.setVisible(false);
		if (this == DasFenster.hauptFenster)
		{
			DasFenster.hauptFenster=null;
		}
		zhintergrundfarbe=null;
		grafikKontext=null;
		hggrafikkontext=null;
		hintergrundbild=null;
		if (! this.laeuftInApplet()) {
			this.dispose();
			System.exit(0);}
		else { this.dispose(); }
	}
	/**
	Anfrage	hintergrundFarbe() liefert die Farbe des Hintergrundes
	*/
	public Color hintergrundFarbe()
	{
		return zhintergrundfarbe ;
	}
	/**
	Anfrage	hoehe()  liefert die Höhe der Zeichenebene.
	*/
	public int hoehe()
	{
	   	return this.getSize().height-getInsets().top-getInsets().bottom;
	}
	/**
	Anfrage	höhe()  liefert die Höhe der Zeichenebene.
	*/	public int höhe()
	{
	   	return this.hoehe();
	}
	/**
	Auftrag	loescheAlles() <br>
	nachher:	Die Zeichenebene ist leer.
	*/
	public void loescheAlles()
	{
		if ( ! zhintergrundmodus)
		{
			grafikKontext.setColor(zhintergrundfarbe);
			grafikKontext.fillRect(0,0,getSize().width,getSize().height);
		} else
		{

			hggrafikkontext.setColor(zhintergrundfarbe);
			hggrafikkontext.fillRect(0,0,getSize().width,getSize().height);
			grafikKontext.drawImage(hintergrundbild,0,0,this);
		}
	}
	/**
	Auftrag	löscheAlles() <br>
	nachher:	Die Zeichenebene ist leer.
	*/
	public void löscheAlles()
	{
		this.loescheAlles();
	}
	/**
	Der erste Bildschirm, den eine Anwendung erzeugt wird als Klassenobjekt
	gespeichert
	@see#hauptbildschirm
	*/
	private void registriere(){
		if (DasFenster.hauptFenster==null) {DasFenster.hauptFenster =this;}
	}
	/**
	Beim Abbau des Fensters wird der Grafikkontext wieder freigegeben
	*/
	public synchronized void removeNotify()
	{
		super.removeNotify();
		grafikKontext = null;
	}
	/**
	Auftrag	setzeHintergrundFarbe(Color c) <br>
	nachher	Der Bildschirm hat die angegebene Hintergrundfarbe
	*/
	public synchronized void setzeHintergrundFarbe(Color c)
	{
		zhintergrundfarbe=c;
		if ( ! zhintergrundmodus)
		{
			this.setBackground(zhintergrundfarbe);
			this.setSize(this.getSize().width-1,this.getSize().height-1);
			this.setSize(this.getSize().width+1,this.getSize().height+1);
		} else
		{
			hggrafikkontext.setColor(zhintergrundfarbe);
			hggrafikkontext.fillRect(0,0,getSize().width,getSize().height);
			grafikKontext.drawImage(hintergrundbild,0,0,this);

		}
	}
	/**
	Schaltet double-Buffering ein oder aus. Bei eingeschaltetem
	Hintergrundmodus werden alle Zeichenoperationen unsichtbar
	auf ein Hintergrundbild angewendet.
	Sie werden erst sichtbar, wenn der Bildschirm aktualisiert wird.
	@see Bildschirm#aktualisiere()
	*/
	public void setzeHintergrundmodus(boolean ein)
	{
		if (ein)
		{
			// neues Hintergrundbild erzeugen
			hggrafikkontext= hintergrundbild.getGraphics();
			//hggrafikkontext.translate(this.getInsets().left,this.getInsets().top);
			this.setzeHintergrundFarbe(zhintergrundfarbe);
			// kopiere Vordergrund auf Hintergrund
			// geht wohl nicht!
		}
		else
		{
			// kopiere gegebenenfalls Hintergund auf Vordergrund
			// wirklich nötig ?
			if (hintergrundbild != null)
			grafikKontext.drawImage(hintergrundbild,0,0,this);
			// gib Hintergrundbild frei
			hintergrundbild=null;
		}
		zhintergrundmodus=ein;
	}
	/**
	erzeugt einen Fenstertitel
	*/
	public void setzeTitel(String titel)
	{
		this.setTitle(titel);
	}


		public void update(Graphics g)
		{
		  //Double-Buffer initialisieren
		 if (dbImage == null) {
		    dbImage = createImage(
		     this.getSize().width,
		     this.getSize().height
		    );
		    dbGraphics = dbImage.getGraphics();
		  }
		  //Hintergrund löschen
		  dbGraphics.setColor(getBackground());
		  dbGraphics.fillRect(
		    0,
		    0,
		    this.getSize().width,
		    this.getSize().height
		  );
		  //Vordergrund zeichnen
		  dbGraphics.setColor(getForeground());
		  paint(dbGraphics);
		  //Offscreen anzeigen
		  g.drawImage(dbImage,0,0,this);


		//info("update");
	//	this.paint(g);
	}
	public void setzeSichtbar(boolean s)
	{
		this.setVisible(s);
	}

}
