package net.querz.event;

import java.util.*;
import java.util.function.Consumer;

class EventTree {

	private TreeElement root;

	EventTree() {
		root = new TreeElement(Event.class);
	}

	<T extends Event> EventFunction<T> add(Consumer<T> function, Class<T> eventClass, int priority) {
		EventTree.TreeElement closest = getClosest(eventClass);
		EventTree.TreeElement elem;
		//get closest connection in tree
		if (closest.children != null && closest.children.containsKey(eventClass)) {
			elem = closest.children.get(eventClass);
		} else {
			elem = new EventTree.TreeElement(eventClass);
		}

		EventFunction<T> fData = new EventFunction<>(function, eventClass, priority);
		elem.functions.add(fData);
		Collections.sort(elem.functions);

		//create branch
		Class<?> clazz = eventClass;
		while ((clazz = clazz.getSuperclass()) != Event.class && clazz != closest.eventClass) {
			EventTree.TreeElement parent = new EventTree.TreeElement(clazz);
			parent.addChild(elem);
			elem = elem.parent = parent;
		}
		elem.parent = closest;
		closest.addChild(elem);
		return fData;
	}

	<T extends Event> void remove(EventFunction<T> functionData) {
		EventTree.TreeElement elem = getClosest(functionData.eventClass);
		if (elem.children.get(functionData.eventClass) != null) {
			elem.children.get(functionData.eventClass).removeFunction(functionData);
		}
	}

	//get leaf or closest element
	//T can be any child of Event at this point
	private <T extends Event> TreeElement getClosest(Class<T> eventClass) {

		//? can be different types of Event
		Stack<Class<?>> branch = new Stack<>();
		Class<?> current = eventClass;

		while ((current = current.getSuperclass()) != Event.class) {
			branch.push(current);
		}

		//root elem will change its type
		TreeElement elem = root;


		while (!branch.isEmpty()) {
			if (elem.children != null && elem.children.containsKey(branch.peek())) {
				elem = elem.children.get(branch.pop());
			} else {
				break;
			}
		}
		return elem;
	}

	void execute(Event event) {
		TreeElement elem = getClosest(event.getClass());
		if (elem.children != null && elem.children.containsKey(event.getClass())) {
			elem.children.get(event.getClass()).execute(event);
		}
	}

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

	//each TreeElement has its own type
	class TreeElement {
		TreeElement parent;
		Map<Class<?>, TreeElement> children;
		Class<?> eventClass;
		List<EventFunction<?>> functions = new ArrayList<>();

		TreeElement(Class<?> eventClass) {
			this.eventClass = eventClass;
		}

		void removeFunction(EventFunction function) {
			functions.removeIf(r -> r.equals(function));
		}

		void addChild(TreeElement elem) {
			if (children == null) {
				children = new HashMap<>();
			}
			children.put(elem.eventClass, elem);
		}

		void execute(Event event) {
			functions.forEach(function -> executeEventFunction(function.function, event));
			if (parent != null) {
				parent.execute(event);
			}
		}

		@SuppressWarnings("unchecked")
		<T extends Event> void executeEventFunction(Consumer<T> function, Event event) {
			if (event.isAsync()) {
				new Thread(() -> function.accept((T) event)).start();
			} else {
				function.accept((T) event);
			}
		}

		@Override
		public int hashCode() {
			return eventClass.hashCode();
		}

		@Override
		public String toString() {
			return toString(0);
		}

		String toString(int depth) {
			StringBuilder d = new StringBuilder(depth);

			for (int i = 0; i < depth; i++) {
				d.append(" ");
			}

			StringBuilder out = new StringBuilder(d + "parent=" + (parent == null ? "null" : parent.eventClass.getSimpleName())
					+ "\n" + d + "eventClass=" + eventClass.getSimpleName()
					+ "\n" + d + "functions=" + Arrays.toString(functions.toArray())
					+ "\n" + d + "children:");
			if (children == null) {
				out.append("null\n");
			} else {
				out.append("\n");
				for (Map.Entry<Class<?>, TreeElement> entry : children.entrySet()) {
					out.append(entry.getValue().toString(depth + 1));
				}
			}
			return out.toString();
		}
	}
}

@@ -372,43 +372,9 @@
 em;%0A
-%09%09//get closest connection in tree
 %0A
+
 %09%09if
@@ -705,26 +705,10 @@
 s);%0A
-%0A
 %09%09
-//create branch
 %0A%09%09C
@@ -1306,84 +1306,8 @@
 %09%7D%0A%0A
-%09//get leaf or closest element%0A%09//T can be any child of Event at this point%0A
 %09pri
@@ -1378,47 +1378,8 @@
 s) %7B
-%0A%0A%09%09//? can be different types of Event
 %0A%09%09S
@@ -1548,43 +1548,8 @@
 %09%7D%0A%0A
-%09%09//root elem will change its type%0A
 %09%09Tr
@@ -1572,17 +1572,16 @@
  root;%0A%0A
-%0A
 %09%09while 
@@ -2067,45 +2067,8 @@
 %09%7D%0A%0A
-%09//each TreeElement has its own type%0A
 %09cla

remove comments