package lsystem;

import java.net.*;
import java.io.*;
import java.util.Vector;
import java.awt.*;
/**
 * Lindenmayer-System <br>
 * Belegarbeit AlgoDat WS99/SS2000, Aufgabe 8
 * <p>
 * Klasse fuer Hilfsfunktionen.
 *
 * @author Christian Semrau<br>
 * Christian.Semrau@student.uni-magdeburg.de<br>
 */
public final class Utils {
	// Hilfsvariable fuer letterString
	static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	// Unterteilung eines Grads
	static final int winkelSplit = 10;
	static final int vollWinkel = 360*winkelSplit;
	// Sinus- und Cosinus-Tabellen
	static float[] sinTable, cosTable;
	// dieser Code wird ausgefuehrt, wenn die Klasse geladen wird
	static{
		makeTables();
	}
/** keine Instanzen diese Klasse erlauben. */
private Utils() {}
/**
 * Vergleicht zwei Strings, sortiert unabhaengig von Gross/Kleinschreibung.
 * negativ: X1 kleiner als X2, positiv:X1 groesser als X2
 */
public static int compare(String X1, String X2) {
	boolean z1 = (X1==null||X1.length()<=0), z2 = (X2==null||X2.length()<=0);
	if (z1 || z2) return z1 ? (z2 ? 0 : -1) : (z2 ? +1 : 0);
	int c1 = X1.toUpperCase().compareTo(X2.toUpperCase());
	int c2 = X1.compareTo(X2);
	return (c1==0) ? c2 : c1;
}
/**
 * Liefert den cosinus des Winkels.
 * Der Winkel muss in Grad angegeben sein.
 * @return float
 * @param grad float
 */
public static float cos(float grad) {
//	if (cosTable==null) makeTables();
	return cosTable[sincosIndex(grad)];
}
/**
 * Liefert den cosinus des Winkels.
 * Der Winkel muss in Grad angegeben sein und unbedingt
 * zwischen 0 und 360 Grad liegen.
 * @return float
 * @param grad float
 */
public static float cos2(float grad) {
//	if (cosTable==null) makeTables();
	return cosTable[(int)(grad*winkelSplit)];
}
/**
 * Zaehlt das Auftreten des Zeichens im String.
 * @return int
 * @param S Zu durchsuchender String
 * @param c Zu zaehlendes Zeichen
 */
public static int countChar(String S, char c) {
	int p = 0;
	for (int i=0; i<S.length(); )
		if (S.charAt(i++)==c) p++;
	return p;
}
/**
 * Zaehlt die Buchstaben im String.
 * @return int
 * @param S Zu durchsuchender String
 */
public static int countLetter(String S) {
	int p = 0;
	for (int i=0; i<S.length(); )
		if (isLatinLetter(S.charAt(i++))) p++;
	return p;
}
/**
 * Wandelt ein double in einen String mit gegebener Anzahl Nachkommastellen um.
 * Liefert fuer zu kleine Zahlen 0, fuer zu grosse Zahlen die E-Darstellung
 * (in diesem Fall ist die Stellenzahl nicht garantiert).
 *
 * @return String
 * @param d double - der Wert, der umgewandelt werden soll
 * @param ges int - die Gesamtzahl an Zeichen (wird links mit Leerzeichen aufgefuellt)
 * @param nach int - die Zahl der Nachkommastellen
 */
public static String fdouble(double d, int ges, int nach) {
  d=Math.floor(d*Math.pow(10,nach)+0.5d)/Math.pow(10,nach);
  return right(Double.toString(d), ges);
}
/**
 * Entfernt aus einem String alle Leerzeichen.
 * @return String
 * @param s String aus dem die Leerzeichen entfernt werden sollen
 */
public static String filterSpaces(String s) {
	StringBuffer sb = new StringBuffer(s.length()); char c;
	for (int i=0; i<s.length(); )
		if ((c=s.charAt(i++)) != ' ') sb.append(c);
	return sb.toString();
}
/**
 * Liefert die Ziffer, die das Zeichen darstellt oder -1, wenns keine Ziffer ist.
 * @return int
 * @param c Ziffern-Zeichen
 */
public static short getDigit(char c) {
	return c>='0'&&c<='9' ? (short)(c-'0') : -1;
}
/**
 * Liefert den Index des Buchstaben ('A','a'->0, 'B','b'->1, usw) oder -1,
 * wenns kein Buchstabe ist.
 * @return int
 * @param c Buchstabe, dessen Index im Alphabet ermittelt werden soll
 */
public static short index(char c) {
	return (c>='A'&&c<='Z') ? (short)(c-'A') : (c>='a'&&c<='z') ? (short)(c-'a') : -1;
}
/**
 * Liefert true, wenn c eine ISO-LATIN-1 Ziffer ist ('0' bis '9').
 * (Ich weiss selbst, dass es keine LATEINISCHEN ZIFFERN sind, sondern arabische!)
 * @return boolean
 * @param c char
 */
public static boolean isLatinDigit(char c) {
	return c>='0'&&c<='9';
}
/**
 * Liefert true, wenn c ein ISO-LATIN-1 Buchstabe ist ('A' bis 'Z' oder 'a' bis 'z').
 * @return boolean
 * @param c char
 */
public static boolean isLatinLetter(char c) {
	return (c>='a'&&c<='z')||(c>='A'&&c<='Z');
}
/**
 * Liefert den Buchstaben zum Index (0->'A', 1->'B', usw).
 * @return char
 * @param i int
 */
public static char letter(int i) {
	return (i>=0 && i<26) ? (char)('A'+i) : 'F';
}
/**
 * Liefert den Buchstaben zum Index (0->"A", 1->"B", usw) als String.
 * @return String
 * @param i int
 */
public static String letterString(int i) {
	return (i>=0&&i<=25) ? ALPHABET.substring(i,i+1) : "F";
}
/**
 * 
 * @param args java.lang.String[]
 */
public static void main(String args[]) {
/*
	for (int i=0; i<=vollWinkel; i++)
		System.out.println(sinTable[i]);
*/
}
/**
 * Liefert ein GridBagConstraints-Objekt mit den uebergebenen Parametern.
 * @return java.awt.GridBagConstraints
 * @param x Linke obere Ecke
 * @param y Linke obere Ecke
 * @param w Ausdehnung
 * @param h Ausdehnung
 * @param fill Fuellmodus
 */
public static GridBagConstraints makegbc(int x, int y, int w, int h, int fill) {
	GridBagConstraints gbc = new GridBagConstraints();
	gbc.gridx = x;
	gbc.gridy = y;
	gbc.gridwidth = w;
	gbc.gridheight = h;
	gbc.fill = fill;
	gbc.insets = new Insets(1, 1, 1, 1);
	return gbc;
}
/**
 * Legt die Sinus- und Cosinus-Tabelle an.
 */
private static void makeTables() {
	if (sinTable==null||cosTable==null){
		// Arrays anlegen
		sinTable = new float[vollWinkel+1];
		cosTable = new float[vollWinkel+1];
		// einen Viertelkreis berechnen
		for(int i=0; i<=90*winkelSplit; i++){
			double w = (Math.PI*i/180/winkelSplit);
			sinTable[i] = (float)Math.sin(w);
			cosTable[i] = (float)Math.cos(w);
		}
		// zweiten Viertelkreis ermitteln
		for(int i=0; i<=90*winkelSplit; i++){
			sinTable[180*winkelSplit-i] = sinTable[i];
			cosTable[180*winkelSplit-i] = -cosTable[i];
		}
		// zweiten Halbkreis ermitteln
		for(int i=0; i<=180*winkelSplit; i++){
			sinTable[360*winkelSplit-i] = -sinTable[i];
			cosTable[360*winkelSplit-i] = cosTable[i];
		}
	}
}
/**
 * Liest eine Datei ein, die ueber eine Basisadresse und den Dateinamen gegeben ist.
 * @return String[] Inhalt der Datei, jede Zeile in einem String
 * @param base URL des Verzeichnisses
 * @param n Name der Datei
 */
public static String[] readFile(URL base, String n) {
	try{
		URL url = new URL(base, n);
		InputStream is = url.openStream();
		return readStream(is);

	}catch(SecurityException e){
		System.out.println("Security Exception: "+e.getMessage());
		return null;

	}catch(MalformedURLException e){
		System.out.println("Malformed URL Exception: "+e.getMessage());
		return null;
	}catch(IOException e){
		System.out.println("IOException: "+e.getMessage());
		return null;
	}
}
/**
 * Liest einen InputStream ein und liefert ein String-array mit einem String je Zeile.
 * @return String[]
 * @param is InputStream
 */
public static String[] readStream(java.io.InputStream  is) {
	String[] result = null;
	Vector values = new Vector ();
	try {
		BufferedReader reader =
			new BufferedReader (new InputStreamReader(is));
		while (reader.ready ()) {
			String value = reader.readLine ();
			if (value==null) value = "";
			values.addElement (value);
		}
		reader.close ();
		result = new String[values.size ()];
		for (int i = 0; i < values.size (); i++)
			result[i] = (String)values.elementAt(i);
	}
	catch (IOException e) {
		System.err.println ("I/O Exception: " + e.getMessage ());
	}
	return result;
}
/**
 * Liest eine angegebene Textdatei ein
 * und gibt diese in einem String-Feld zur&uuml;ck.
 * Im Fehlerfall wird <code>null</code> geliefert.
 *
 * @return String[] jede Zeile in einem String
 * @param filename Der Name (inkl. Pfad) der einzulesenden Datei
 */
public static String[] readStringArray (String filename) {
  String[] result = null;
  Vector values = new Vector ();
  try {
	BufferedReader reader =
	  new BufferedReader (new FileReader (filename));
	while (reader.ready ()) {
	  String value = reader.readLine ();
	  if (value==null) value = "";
	  values.addElement (value);
	}
	reader.close ();
	result = new String[values.size ()];
	for (int i = 0; i < values.size (); i++)
	  result[i] = (String) values.elementAt (i);
  }
  catch (IOException e) {
	System.err.println ("I/O Exception: " + e.getMessage ());
  }
  return result;
}
/**
 * Gibt einen rechtsbuendig auf n Zeichen formatieren <code>String</code> zurueck.
 * Ist der <code>String</code> s zu lang, wird er unveraendert zurueckgegeben.
 * @return String - rechtsbuendig formatierter String
 * @param s String - der zu formatierende String
 * @param n int - die Anzahl der Zeichen (wird links mit Leerzeichen aufgefuellt)
 */
public static String right(String s, int n) {
	return (spc(n-s.length(), ' ') + s);
}
/**
 * Liefert den sinus des Winkels.
 * Der Winkel muss in Grad angegeben sein.
 * @return float
 * @param grad float
 */
public static float sin(float grad) {
//	if (sinTable==null) makeTables();
	return sinTable[sincosIndex(grad)];
}
/**
 * Liefert den sinus des Winkels.
 * Der Winkel muss in Grad angegeben sein und unbedingt
 * zwischen 0 und 360 Grad liegen.
 * @return float
 * @param grad float
 */
public static float sin2(float grad) {
//	if (sinTable==null) makeTables();
	return sinTable[(int)(grad*winkelSplit)];
}
/**
 * Liefert den Index fuer einen Zugriff auf die sinus- und cosinus-Tabelle.
 * @return int
 * @param grad float
 */
public static int sincosIndex(float grad) {
	int index = (int)(grad*winkelSplit);
	while (index>vollWinkel) index -= vollWinkel;
	while (index<0) index += vollWinkel;
	return index;
}
/**
 * Liefert einen <code>String</code>, der <code>len</code> Mal das Zeichen <code>ch</code> enthaelt.
 * @return String
 * @param len int - Die gewuenschte Laenge der Zeichenkette
 * @param ch char - Das zu wiederholende Zeichen
 */
public static String spc(int len, char ch) {
  if (len<=0) return "";
  char[] c = new char[len];
  for (int i=0; i<len; c[i++] = ch);
  return new String(c);
}
/**
 * Wandelt eine String in eine Form um, in der er im Quelltext eingefuegt
 * werden kann (hoehere Unicode-Zeichen und Steuerzeichen werden noch nicht
 * ersetzt).
 * @return String
 * @param S String
 */
public static String specString(String S) {
	StringBuffer sb = new StringBuffer();
	sb.append("\"");
	for(int i=0; i<S.length(); i++){
		char c = S.charAt(i);
		switch(c){
			case '\"': sb.append("\\\""); break;
			case '\\': sb.append("\\\\"); break;
			case '\r': sb.append("\\r"); break;
			case '\n': sb.append("\\n"); break;
			case '\t': sb.append("\\t"); break;
			default: sb.append(c); break;
		}
	}
	sb.append("\"");
	return sb.toString();
}
/**
 * Zerlegt einen String mit Zeilenwechseln in ein String-array.
 * @return String[]
 * @param S String
 */
public static String[] splitIntoLines(String S) {
	if (S==null) return new String[0];
	if (S.length()<=0) return new String[0];
	Vector v = new Vector();
	StringBuffer sb = new StringBuffer();
	int index = 0;
	char pre = 0;
	while (index<S.length()){
		char c = S.charAt(index++);
		switch(c){
			case '\n':
				if (pre=='\r') break; // else fall through
			case '\r':
				v.addElement(sb.toString()); sb.setLength(0);
				break;
			default: sb.append(c); break;
		}
		pre = c;
	}
	v.addElement(sb.toString());

	String[] result = new String[v.size ()];
	for (int i = 0; i < v.size (); i++)
	  result[i] = (String)v.elementAt(i);
	return result;
}
/**
 * Liefert den Grossbuchstaben.
 * @return char
 * @param c char
 */
public static char upCase(char c) {
	return (c>='a'&&c<='z') ? (char)(c+'A'-'a') : c;
}
}