package baum;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;


public class Tree<E> {
	private final E element;
	public final List<Tree<E>> childNodes;

	public Tree(E element, List<Tree<E>> childNodes) {
		super();
		this.element = element;
		this.childNodes = childNodes;
	}

	public Tree() {
		this(null, null);
	}

	public boolean isEmpty() {
		return element == null && childNodes == null;
	}

	public long count() {
		return isEmpty() ? 0 : (1 + childNodes.parallelStream().mapToLong(child -> child.count()).sum());
	}

	public List<E> elements() {
		var result = new ArrayList<E>();
		elements(result);
		return result;
	}

	public void elements(List<E> result) {
		if (!isEmpty()) {
			result.add(element);
			childNodes.forEach(child -> {
				child.elements(result);
			});
		}
	}

	String toLaTeX() {
		var result = new StringBuffer(
				"\\begin{tikzpicture}[sibling distance=10em," + "every node/.style = {shape=rectangle, rounded corners,"
						+ "draw, align=center,top color=white, bottom color=blue!20}]]");

		toLaTeX(result);

		result.append("\\end{tikzpicture}");
		return result.toString();
	}

	void toLaTeX(StringBuffer result) {
		if (!isEmpty()) {
			result.append("node {" + element + "}");
			for (var child : childNodes) {
				result.append("\n  child {");
				child.toLaTeX(result);
				result.append("\n  }");
			}
		}

	}

	String toXML() {
		var result = new StringBuffer();
		result.append("<?xml version=\"1.0\"?>\n");
		toXMLAux(result);
		return result.toString();
	}

	void toXMLAux(StringBuffer result) {
		result.append("<node> <element>" + element + "</element>");
		childNodes.stream().forEach(child -> {
			result.append("\n  <child>");
			child.toXMLAux(result);
			result.append("</child>");
		});
		result.append("</node>");
	}

	public long count2() {
		if (isEmpty())
			return 0;
		var result = 1L;
		for (var child : childNodes) {
			result = result + child.count2();
		}
		return result;
	}

	public long count3() {
		long[] result = { 0L };
		fuerAlle(e -> result[0]++);
		return result[0];
	}

	public void fuerAlle(Consumer<? super E> action) {
		if (isEmpty())
			return;
		action.accept(element);
		childNodes.stream().parallel().forEach(child -> child.fuerAlle(action));
	}

	public void fuerAlle2(Consumer<? super E> action) {
		if (isEmpty())
			return;
		action.accept(element);
		for (var child : childNodes) {
			child.fuerAlle2(action);
		}
	}

	public boolean contains(E el) {
		return !isEmpty() && (element.equals(el) || childNodes.parallelStream().anyMatch(child -> child.contains(el)));
	}

	public boolean contains2(E el) {
		if (isEmpty())
			return false;
		if (element.equals(el))
			return true;
		for (var child : childNodes) {
			if (child.contains2(el))
				return true;
		}
		return false;
	}

	@Override
	public String toString() {
		return element.toString();
	}

	public static void main(String[] args) {
		var windsor = new Tree<>("George", List.of(
				new Tree<>("Elizabeth",
						List.of(new Tree<>("Charles", List.of()), new Tree<>("Anne", List.of()),
								new Tree<>("Edward", List.of()), new Tree<>("Phillip", List.of()))),
				new Tree<>("Margrete", List.of())));
		System.out.println(windsor.elements());
		System.out.println(windsor.toLaTeX());
		System.out.println(windsor.toXML());

		System.out.println(windsor.count());
		System.out.println(windsor.count2());
		System.out.println(windsor.count3());
		System.out.println(windsor.contains("Edward"));
		System.out.println(windsor.contains("Wilhelm"));


	}

}
