package speiger.src.coreengine.core.events.utility;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.core.events.api.Event;
import speiger.src.coreengine.core.events.api.EventPriority;

public class Listeners
{
	@SuppressWarnings("unchecked")
	Queue<Consumer<Event>>[] unsortedListeners = new Queue[EventPriority.values().length];
	@SuppressWarnings("unchecked")
	Consumer<Event>[] listeners = new Consumer[0];
	volatile boolean rebuild = true;
	Listeners parent;
	List<Listeners> childs = null;
	
	public Listeners() {
		for(int i = 0;i<unsortedListeners.length;i++) {
			unsortedListeners[i] = new ConcurrentLinkedQueue<>();
		}
	}
	
	public Listeners(Listeners parent) {
		this();
		this.parent = parent;
		this.parent.addChild(this);
	}
	
	public void addChild(Listeners listener) {
		if(childs == null) childs = new ObjectArrayList<>();
		childs.add(listener);
	}
	
	public void addListener(EventPriority priority, Consumer<Event> listener) {
		if(unsortedListeners[priority.ordinal()].add(listener)) markDirty();
	}
	
	public void removeListeners(Consumer<Event> listener) {
		for(int i = 0,m=unsortedListeners.length;i<m;i++) {
			if(unsortedListeners[i].remove(listener)) markDirty();
		}
	}
	
	public void getListeners(EventPriority entry, List<Consumer<Event>> events) {
		events.addAll(unsortedListeners[entry.ordinal()]);
		if(parent != null) parent.getListeners(entry, events);
	}
	
	public Consumer<Event>[] getListeners() {
		if(rebuild) {
			synchronized(this) {
				if(rebuild) rebuildListeners();
			}
		}
		return listeners;
	}
	
	private void markDirty() {
		if(rebuild) return;
		rebuild = true;
		if(childs != null) {
			for(int i = 0,m=childs.size();i<m;i++) {
				childs.get(i).markDirty();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public void rebuildListeners() {
		List<Consumer<Event>> result = new ObjectArrayList<>();
		for(EventPriority entry : EventPriority.values()) {
			getListeners(entry, result);
		}
		listeners = result.toArray(new Consumer[result.size()]);
		rebuild = false;
	}
}
