/*
 * Eine Knoten-Klasse für die Implementierung von Bäumen.
 * Hab's mal ganz edel mit Comparables statt einfach mit
 * int-Werten gemacht.
 */
class Node {
	
	/*
	 * Wir definieren alle unsere Bäume in derselben Package,
	 * deswegen sind die Variablen durch das protected-Attribut
	 * vor Zugriff von außen (aus anderen Packages) geschützt.
	 * Man könnte auch "ordentlich" kapseln und Getter- & Setter-
	 * Methoden verwenden, was den Code aber extrem aufblähen
	 * würde.
	 */
	protected Comparable  value;  // Wert
	protected Node        left;   // linker Kind-Knoten
	protected Node        right;  // rechter Kind-Knoten
	
	
	public Node(Comparable value, Node left, Node right) {
		this.value  = value;
		this.left   = left;
		this.right  = right;
	}
	
}


/* 
 * Dieses Interface dient dazu, Baumimplementierungen vor
 * "unqualifizierten" Zugriffen von außen zu schützen. (Damit
 * z.B. nicht irgendwelche Knoten direkt in die Datenstruktur
 * eingefügt werden, ohne die Sortierung zu beachten, etc.
 */
interface BinaryTree {
	
	public void insert(Comparable value);
	
	public void remove(Comparable value);
	
}


/* 
 * Eine Implementierung des BinaryTree-Interface
 */
public class BinTree implements BinaryTree {
	
	Node root;
	
	public BinTree(Node root) {
		this.root = root;
	}
	
	
	/*
	 * Fügt ein Element in den Binärbaum ein.
	 * (Mehrfaches einfügen ist erlaubt.)
	 */
	public void insert(Comparable value) {
		
		Node temp = root;
		Node parent = null;
		
		while (temp != null) {
			
			parent = temp;
			
			if (value.compareTo(temp.value) < 0) {
				temp = temp.left;
			}
			else {
				temp = temp.right;
			}
		}
		
		if (parent != null) {
			if (value.compareTo(parent.value) > 0) {
				parent.right = new Node(value, null, null);
			}
			else {
				parent.left = new Node(value, null, null);
			}
		}
		
	}
	
	
	/*
	 * Ersetzt das zu löschende Element mit dem
	 * größten Element aus dem linken Unterbaum
	 */
	public void remove(Comparable value) {
		
		Node temp = root;
		Node parent = null;
		
		// Knoten suchen
		while ((temp != null) && (temp.value.compareTo(value) != 0)) {
			
			parent = temp;
			
			if (value.compareTo(temp.value) < 0) {
				temp = temp.left;
			}
			else {
				temp = temp.right;
			}
		}
		
		// wenn gefunden, Knoten löschen
		if (temp != null) {
			
			// wenn linker Unterbaum existiert, mit größtem Element daraus ersetzen
			if (temp.left != null) {
				
				Node temp2 = temp.left;  // der Knoten, der nach oben versetzt wird
				Node parent2 = temp;     // der Elternknoten von dem, der nach oben versetzt wird
				
				while (temp2.right != null) {
					parent = temp2;
					temp2 = temp2.right;
				}
				
				if (temp2 != temp.left) temp2.left = temp.left;
				temp2.right = temp.right;
				parent2.right = temp2.left;
				
				if (parent.left == temp) {
					parent.left = temp2;
				}
				else {
					parent.right = temp2;
				}
				
			}
			else { // sonst einfach mit rechtem Unterbaum ersetzen
			
				if (parent.left == temp) {
					parent.left = temp.right;
				}
				else {
					parent.right = temp.right;
				}
			}
		}
	}
	
	
	/*
	 * Gibt den Baum als AVL-Baum zurück
	 */
	public static BinTree getAvlTree(BinTree b) {
		// TODO
		return null;		
	}
	
	
	/*
	 * Wir überschreiben die toString-Methode von
	 * Object, damit sich der Baum leicht ausgeben lässt
	 * (s. main-Methode).
	 *
	 * ACHTUNG: Die Methode is HÖLLE langsam, weil massen-
	 *  haft Strings konkateniert werden. Schnell implementiert,
	 *  aber extrem unperformant.
	 */
	public String toString() {
		
		return getTreeAsString(root);
	}
	
	private String getTreeAsString(Node n) {
		
		if (n != null) {
			//System.out.println(n.value);
			return "(" + getTreeAsString(n.left) + ") " + n.value + " (" + getTreeAsString(n.right) + ")";
		}
		
		return "*";
	}
	
	
	public static void main(String[] args) {
		
		/*
		 * Also - Bau'n wir'n Baum:
		 *
		 *                     10
		 *           .----------'---------.
		 *           7                   12
		 *      .----'----.          .----'----.
		 *      5         9         11        15
		 *    .-'       .-'
		 *    2         8
		 *
		 * ...und dann löschen wir die 7:
		 *
		 *                     10
		 *           .----------'---------.
		 *           5                   12
		 *      .----'----.          .----'----.
		 *      2         9         11        15
		 *             .--'
		 *             8
		 *
		 */
		
		
		BinTree tree = new BinTree( new Node(new Integer(10), null, null) );
		
		tree.insert(new Integer(7));
		tree.insert(new Integer(12));
		tree.insert(new Integer(5));
		tree.insert(new Integer(9));
		tree.insert(new Integer(11));
		tree.insert(new Integer(15));
		tree.insert(new Integer(2));
		tree.insert(new Integer(8));
		
		System.out.println(tree);
		
		tree.remove(new Integer(7));
		
		System.out.println(tree);
		
	}
	
}
