package com.cardshifter.modapi.events;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import com.cardshifter.modapi.base.CancellableEvent;
public class EventExecutor implements EventExecution {
private static final Logger logger = LogManager.getLogger(EventExecutor.class);
protected final Map<Class<? extends IEvent>, Collection<EventHandler<?>>> bindings;
public EventExecutor() {
this.bindings = new HashMap<Class<? extends IEvent>, Collection<EventHandler<?>>>();
}
private <T extends IEvent> T executeEventInternal(T event, Predicate<EventHandler<?>> predicate) {
Collection<EventHandler<?>> handlers = this.bindings.get(event.getClass());
if (handlers != null) {
List<EventHandler<?>> interestedHandlers = handlers.stream().filter(predicate).collect(Collectors.toList());
ListIterator<EventHandler<?>> iterator = interestedHandlers.listIterator();
while (iterator.hasNext()) {
EventHandler<?> performer = iterator.next();
// int index = iterator.nextIndex();
// logger.info("Handling " + event + " " + index + " / " + interestedHandlers.size() + ": " + performer);
performer.execute(event);
}
}
return event;
}
@Override
public <T extends IEvent> T executePostEvent(T event) {
logger.debug("Execute pre event " + event);
return executeEventInternal(event, eh -> eh.isAfter());
}
@Override
public <T extends IEvent> T executePreEvent(T event) {
logger.debug("Execute post event " + event);
return executeEventInternal(event, eh -> !eh.isAfter());
}
/**
* Execute a pre-event, perform something, then execute a post-event.
*
* @param event Event to execute
* @param runInBetween What to do between pre- and post- events.
* @return The event that was executed
*/
@Override
public <T extends IEvent> T executeEvent(T event, Runnable runInBetween) {
executePreEvent(event);
runInBetween.run();
executePostEvent(event);
return event;
}
public <T extends IEvent> T executeEvent(T event, Consumer<T> runInBetween) {
executePreEvent(event);
runInBetween.accept(event);
executePostEvent(event);
return event;
}
@Override
public <T extends CancellableEvent> T executeCancellableEvent(T event, Runnable runInBetween) {
executePreEvent(event);
if (!event.isCancelled()) {
runInBetween.run();
executePostEvent(event);
}
return event;
}
public <T extends IEvent> void registerHandler(Class<T> realParam, EventHandler<T> handler) {
if (!this.bindings.containsKey(realParam)) {
this.bindings.put(realParam, createCollection());
}
Collection<EventHandler<?>> eventHandlersForEvent = this.bindings.get(realParam);
eventHandlersForEvent.add(handler);
}
protected Collection<EventHandler<?>> createCollection() {
return new ArrayList<EventHandler<?>>();
}
public void clearListeners() {
this.bindings.clear();
}
public void removeHandler(EventHandler<?> listener) {
for (Entry<Class<? extends IEvent>, Collection<EventHandler<?>>> ee : bindings.entrySet()) {
Iterator<EventHandler<?>> it = ee.getValue().iterator();
while (it.hasNext()) {
EventHandler<?> curr = it.next();
if (curr == listener) {
it.remove();
}
}
}
}
public void removeListenersWithIdentifier(Object identifier) {
for (Entry<Class<? extends IEvent>, Collection<EventHandler<?>>> ee : bindings.entrySet()) {
ee.getValue().removeIf(eh -> eh.getIdentifier() == identifier);
}
}
public <T extends IEvent> EventHandler<T> registerHandlerAfter(Object identifier, Class<T> realParam, Consumer<T> handler) {
EventHandler<T> listener = new EventHandler<T>(identifier, handler, true);
registerHandler(realParam, listener);
return listener;
}
public <T extends IEvent> EventHandler<T> registerHandlerBefore(Object identifier, Class<T> realParam, Consumer<T> handler) {
EventHandler<T> listener = new EventHandler<T>(identifier, handler, false);
registerHandler(realParam, listener);
return listener;
}
}