package lsystem;

import java.awt.*;
import java.util.Stack;
/**
 * Lindenmayer-System <br>
 * Belegarbeit AlgoDat WS99/SS2000, Aufgabe 8
 * <p>
 * Turtle-Klasse.
 *
 * @author Christian Semrau<br>
 * Christian.Semrau@student.uni-magdeburg.de<br>
 */
public final class Turtle {

	/** Koordinaten der Schildkroete */
	private float x, y;
	/** aktueller Winkel in Grad, 0 = rechts, 90 = unten, usw. */
	private float alpha;
	/** +1: positiv = Uhrzeigersinn, -1: positiv = Gegenuhrzeigersinn */
	private int drehsinn;
	/** pen<=0: zeichnen, pen>0: nicht zeichnen */
	private int pen;
	/** der Grafikkontext fuer die Ausgabe, oder null fuer keine Ausgabe*/
	private Graphics g;
	/** Variablen zum Bestimmen des gezeichneten Bereichs */
	float startX, startY, Xmin, Xmax, Ymin, Ymax;
	/** Stacks fuer Position und Richtung */
	private Stack xStack, yStack, aStack;
/**
 * Legt eine neue Schildkroete an mit den uebergebenen Startwerten.
 */
public Turtle(Graphics graph, float x, float y, float a) {
	g = graph;
	if (g!=null)
		// Zeichenfarbe setzen
		g.setColor(Color.black);
	this.x = x;
	this.y = y;
	drehsinn = 1;
	pen = 0;
	alpha = 0;
	turn(a); // noetig, um Bereichsueberschreitungen abzufangen

	Xmin = Xmax = startX = x;
	Ymin = Ymax = startY = y;
	xStack = new Stack();
	yStack = new Stack();
	aStack = new Stack();
}
/**
 * Geht <code>n</code> Pixel rueckwaerts.
 */
public void backward (float n) {
	forward(-n);
}
/**
 * Geht <code>n</code> Pixel vorwaerts.
 * Zeichnet dabei eine Linie, wenn <code>pen &lt;= 0</code>.
 * Aktualisiert die Grenzmarker, wenn <code>g==null</code>.
 */
public void forward (float n) {
/*
	double bogen = alpha * Math.PI / 180;
	float dx = (float)(n * Math.cos(bogen)); 
	float dy = (float)(n * Math.sin(bogen));
*/
	int alphaindex = (int)(alpha*Utils.winkelSplit);
	float dx = n*Utils.cosTable[alphaindex];
	float dy = n*Utils.sinTable[alphaindex];

	float nx = x + dx, ny = y + dy;
	if (g!=null){
		// Grafik-Kontext vorhanden
		if (pen<=0)
			// wenn der Stift unten ist, zeichnen
			g.drawLine( (int) x, (int) y, (int) nx, (int) ny );
		x = nx; y = ny;
	}else{
		// keine Grafik, also zu zeichnenden Bereich abstecken
		if (pen<=0) includePt();
		x = nx; y = ny;
		if (pen<=0) includePt();
	}
}
/**
 * Grenzen des Bereichs abstecken.
 */
private void includePt() {
	if(x<Xmin) Xmin = x;
	if(x>Xmax) Xmax = x;
	if(y<Ymin) Ymin = y;
	if(y>Ymax) Ymax = y;
}
/**
 * Springt vorwaerts (geht vorwaerts, ohne zu zeichnen).
 */
public void jump(float n) {
	penUp();
	forward(n);
	penDown();
}
/**
 * Dreht den Drehsinn der Schildkroete um.
 */
public void mirror() {
	drehsinn = -drehsinn;
}
/**
 * Senkt den Stift, wenn <code>pen &lt;= 0</code>, werden Linien wieder gezeichnet.
 */
public void penDown() {
	pen--;
}
/**
 * Hebt den Stift, wenn <code>pen &gt; 0</code>, werden Linien nicht gezeichnet.
 */
public void penUp() {
	pen++;
}
/**
 * Stellt die zuletzt gesicherte Position und Richtung der Schildkroete wieder her.
 */
public void pop() {
	// die float-Werte muessen
	// aus den Wrapper-Objekten wieder
	// ausgepackt werden
	x = ((Float)xStack.pop()).floatValue();
	y = ((Float)yStack.pop()).floatValue();
	alpha = ((Float)aStack.pop()).floatValue();
}
/**
 * Sichert die Position und Richtung der Schildkroete.
 */
public void push() {
	// der Stack kann keine primitiven Datentypen
	// aufnehmen, also werden Wrapper-Objekte
	// verwendet
	xStack.push(new Float(x));
	yStack.push(new Float(y));
	aStack.push(new Float(alpha));
}
/**
 * Dreht die Schildkroete um den in Grad angegebenen Winkel.
 * Der Drehsinn wird von <code>drehsinn</code> angegeben.
 */
public void turn (float a) {
	alpha += drehsinn*a;
	while (alpha>=360) alpha -= 360;  // Ersatz fuer den modulo-Operator
	while (alpha<0) alpha += 360;
}
}