package com.github.czyzby.autumn.processor.event.impl;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.reflect.Method;
import com.github.czyzby.autumn.annotation.OnEvent;
import com.github.czyzby.autumn.context.Context;
import com.github.czyzby.autumn.context.impl.method.MethodInvocation;
import com.github.czyzby.autumn.processor.event.EventListener;
import com.github.czyzby.kiwi.util.common.Strings;
import com.github.czyzby.kiwi.util.gdx.asset.lazy.provider.ObjectProvider;
/** Wraps around a reflected method, turning it into a listener.
*
* @author MJ */
public class ReflectionEventListener extends MethodInvocation implements EventListener<Object> {
private final boolean removeAfterInvocation;
private final boolean strict;
private final ObjectProvider<?>[] providers;
public ReflectionEventListener(final Method method, final Object methodOwner, final Context context,
final boolean removeAfterInvocation, final boolean strict) {
super(method, methodOwner, getParameters(method, context));
this.removeAfterInvocation = removeAfterInvocation;
this.strict = strict;
providers = getProviders(method, context);
}
private static Object[] getParameters(final Method method, final Context context) {
final Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes == null || parameterTypes.length == 0) {
return Strings.EMPTY_ARRAY;
}
final Object[] parameters = new Object[parameterTypes.length];
for (int index = 0, length = parameterTypes.length; index < length; index++) {
final Class<?> parameterType = parameterTypes[index];
if (context.isPresent(parameterType)) {
parameters[index] = context.getComponent(parameterType);
}
}
return parameters;
}
private static ObjectProvider<?>[] getProviders(final Method method, final Context context) {
final Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes == null || parameterTypes.length == 0) {
return new ObjectProvider<?>[0];
}
final ObjectProvider<?>[] providers = new ObjectProvider<?>[parameterTypes.length];
for (int index = 0, length = parameterTypes.length; index < length; index++) {
final Class<?> parameterType = parameterTypes[index];
if (context.isProviderPresentFor(parameterType)) {
providers[index] = context.getProvider(parameterType);
}
}
return providers;
}
@Override
public boolean processEvent(final Object event) {
replaceParameters(event);
try {
final Object result = invoke();
if (removeAfterInvocation) {
return OnEvent.REMOVE;
} else if (result instanceof Boolean) {
return ((Boolean) result).booleanValue();
}
return OnEvent.KEEP;
} catch (final Exception exception) {
if (strict) {
// Gdx applications seem to ignore exceptions in posted runnables. This is bad.
Gdx.app.error("ERROR", "Exception occured on event listener.", exception);
throw new GdxRuntimeException("Unable to invoke event listener.", exception);
}
return OnEvent.KEEP;
}
}
private void replaceParameters(final Object event) {
final Class<?>[] parameterTypes = getMethod().getParameterTypes();
if (parameterTypes == null || parameterTypes.length == 0) {
return;
}
final Object[] parameters = getParameters();
final Class<?> eventType = event.getClass();
for (int index = 0, length = parameterTypes.length; index < length; index++) {
if (eventType.equals(parameterTypes[index])) {
parameters[index] = event;
} else if (providers[index] != null) {
parameters[index] = providers[index].provide();
}
}
}
}