/*
Datei............: Aufg1431.java
Projekt..........: Einführung in die Java-Programmierung
Erstellt.........: 29.11.97, Guido Krüger
Geändert.........: --
Aufgabe..........: Musterlösung zu Aufgabe 14.31
Kommentare.......:

Die Musterlösung zu dieser Aufgabe erweitert das Konstruktionsschema
der anderen Aufgaben dieses Abschnitts, indem es für die Grafikausgabe
einen Hintergrund-Thread verwendet. Da die Erzeugung der Grafik und
damit das repainting immerfort andauert, wurde dies nötig, um im 
Vordergrund weiterhin ein brauchbares Eventhandling zu gewährleisten.
Die Klasse implementiert dazu die Methode startThread, die ein neues
Thread-Objekt erzeugt und sich selbst an dieses übergibt. Die eigentliche
Bildschirmausgabe erfolgt dann nicht mehr in paint(), sondern in dem
Threadbody run(). paint ist nur noch dafür verantwortlich, die drei 
weißen Kreuze an den Ecken des Dreiecks zu zeichnen und den Thread
durch Aufruf von startThread zu starten.
*/
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Aufg1431
extends Frame
implements Runnable
{
  //Instanzmerkmale 
  Thread  thread;
  Random  random;
  Point   corners[];

  public static void main(String[] args)
  {
	Aufg1431 wnd = new Aufg1431();
	wnd.setSize(400,300);
	wnd.setVisible(true);
  }

  public Aufg1431()
  {
	super("Aufg1431");
	addWindowListener(
	  new WindowAdapter() {
	    public void windowClosing(WindowEvent event) {
		  stopThread();
		  System.exit(0);
		}
	  }
    );
	setBackground(Color.black);
    corners    = new Point[3];
	random     = new Random();
	thread     = null;
  }

  /**
   * Startet den Thread zur Bildschirmausgabe.
   */
  void startThread()
  {
	stopThread();
	thread = new Thread(this);
	thread.start();
  }

  /**
   * Stoppt den Thread zur Bildschirmausgabe.
   */
  void stopThread()
  {
	if (thread != null) {
	  thread.stop();
	  thread = null;
	}
  }

  public void paint(Graphics g)
  {
	int width = getSize().width;
	int height = getSize().height;

	//Hier werden nur die drei Ecken gezeichnet
	corners[0] = new Point(width / 2, getInsets().top + 10);
	corners[1] = new Point(getInsets().left + 5, height - 20);
	corners[2] = new Point(width - 10, height - 10);
	g.setColor(Color.white);
	for (int i = 0; i < 3; ++i) {
	  drawCross(g, corners[i].x, corners[i].y);
	}
	//Der Hintergrundthread erledigt in run() den Rest
	startThread();
  }

  /**
   * Diese Methode repräsentiert den Threadbody zur Bildschirmausgabe.
   * Hier wird durch Aufruf von getGraphics() zunächst ein Grafikkontext
   * besorgt und dann in einer Schleife immerwährend folgende Aktionen
   * durchgeführt:
   * <ul>
   * <li>In 0.01 % aller Fälle, jedoch frühestens nach 7.5 Sekunden wird
   *     die Vorzugsfarbe geändert. Diese dient als "Musterfarbe", um die
   *     aktuelle Zeichenfarbe zu bestimmen.
   * <li>In 0.2 % aller Fälle wird aus der Musterfarbe eine neue Zeichenfarbe
   *     generiert, indem die aktuelle Zeichenfarbe leicht variiert wird.
   * <li>Bei jedem Schleifendurchlauf wird ein neuer Bildpunkt gezeichnet,
   *     indem genau auf die Mitte der Strecke zwischen dem zuletzt 
   *     gezeichneten Bildpunkt und einer zufällig ausgewählten Ecke des
   *     Dreiecks ein neuer Bildpunkt gezeichnet wird.
   * </ul>
   */
  public void run()
  {
	int x = 0, y = 0;
	long millis = System.currentTimeMillis();
	Graphics g = getGraphics();
	Color color = changeColor(g, createPreferredColor());
	while (true) {
	  //Ändern der Musterfarbe
	  if (Math.abs(random.nextInt()) % 100000 < 10) {
		if (System.currentTimeMillis() - millis >= 7500) {
		  color = changeColor(g, createPreferredColor());
		  millis = System.currentTimeMillis();
		}
	  }
	  //Ändern der Farbe
	  if (Math.abs(random.nextInt()) % 1000 < 2) {
		color = new Color(
		  (color.getRed() + (Math.abs(random.nextInt()) % 10)) % 256,
		  (color.getGreen() + (Math.abs(random.nextInt()) % 10)) % 256,
		  (color.getBlue() + (Math.abs(random.nextInt()) % 10)) % 256
    	);
	    color = changeColor(g, color);
	  }
	  //Nächsten Bildpunkt zeichnen
	  int corner = Math.abs(random.nextInt()) % 3;
	  x = (x + corners[corner].x) / 2;
	  y = (y + corners[corner].y) / 2;
	  drawDot(g, x, y);
	}
  }

  /**
   * Zeichnet einen einzelnen Punkt mit Hilfe einer Linie der Länge 1.
   */
  private void drawDot(Graphics g, int x, int y)
  {
    g.drawLine(x,y,x,y);
  }

  /**
   * Zeichnet ein Kreuz an der angegebenen Position.
   */
  private void drawCross(Graphics g, int x, int y)
  {
    g.drawLine(x-2,y,x+2,y);
    g.drawLine(x,y-2,x,y+2);
  }

  /**
   * Ändert die Farbe des übergebenen Grafikkontexts und zeichnet ein
   * kleines Rechteck zur Farbvorschau in die linke obere Ecke.
   */
  private Color changeColor(Graphics g, Color col)
  {
	g.setColor(col);
	g.fillRect(getInsets().left + 2, getInsets().top + 2, 20, 10);
	return col;
  }

  /**
   * Erzeugt ein zufälliges Farbobjekt.
   */
  private Color createPreferredColor()
  {
	return new Color(
	  Math.abs(random.nextInt()) % 256,
	  Math.abs(random.nextInt()) % 256,
	  Math.abs(random.nextInt()) % 256
	);
  }

}

