package jalse.entities.functions;
import static jalse.entities.functions.Functions.checkNoParams;
import static jalse.entities.functions.Functions.checkNotDefault;
import static jalse.entities.functions.Functions.firstGenericTypeArg;
import static jalse.entities.functions.Functions.hasReturnType;
import static jalse.entities.functions.Functions.returnTypeIs;
import static jalse.entities.functions.Functions.toClass;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.TimeUnit;
import jalse.actions.Action;
import jalse.actions.ActionContext;
import jalse.entities.DefaultEntityProxyFactory;
import jalse.entities.Entity;
import jalse.entities.annotations.ScheduleForActor;
import jalse.entities.methods.ScheduleForActorMethod;
/**
* This is a method function for {@link ScheduleForActor} annotation. It will resolve an
* {@link ScheduleForActorMethod} to be used by the entity typing system. This will resolve the zero
* argument constructor for the {@link Action}. <br>
* <br>
*
* <pre>
* <code>
* {@code @ScheduleForAction(action = Haunt.class)}
* {@code ActionContext<Entity>} scheduleHaunting();
*
* {@code @ScheduleForAction(action = Haunt.class)}
* void scheduleHaunting();
*
* {@code @ScheduleForAction(action = Haunt.class, initialDelay = 50, unit = TimeUnit.MILLISECONDS)}
* {@code ActionContext<Entity>} scheduleHaunting();
*
* {@code @ScheduleForAction(action = Haunt.class, initialDelay = 50, unit = TimeUnit.MILLISECONDS)}
* void scheduleHaunting();
*
*
* {@code @ScheduleForAction(action = Haunt.class, initialDelay = 50, period = 200, unit = TimeUnit.MILLISECONDS)}
* {@code ActionContext<Entity>} scheduleHaunting();
*
* {@code @ScheduleForAction(action = Haunt.class, initialDelay = 50, period = 200, unit = TimeUnit.MILLISECONDS)}
* void scheduleHaunting();
* </code>
* </pre>
*
*
* NOTE: This function will throw exceptions if {@link ScheduleForActor} is present but the method
* signature is invalid.
*
* @author Elliot Ford
*
* @see DefaultEntityProxyFactory
*
*/
public class ScheduleForActorFunction implements EntityMethodFunction {
@Override
public ScheduleForActorMethod apply(final Method m) {
// Check for annotation
final ScheduleForActor annonation = m.getAnnotation(ScheduleForActor.class);
if (annonation == null) {
return null;
}
// Annotation info
final long initialDelay = annonation.initialDelay();
final long period = annonation.period();
final TimeUnit unit = annonation.unit();
// Check annotation info
if (initialDelay < 0) {
throw new IllegalArgumentException("Initial delay cannot be negative");
} else if (period < 0) {
throw new IllegalArgumentException("Period cannot be negative");
}
// Basic check method signature
checkNoParams(m);
checkNotDefault(m);
// Get constructor for action
Constructor<?> constructor = null;
for (final Constructor<?> con : annonation.action().getConstructors()) {
// Check for zero arg
if (con.getParameterCount() == 0) {
constructor = con;
break;
}
}
// Check constructor
if (constructor == null) {
throw new IllegalArgumentException(
String.format("Action type %s has no public statically accessible zero argument constructor",
annonation.action()));
}
// Check return type
if (hasReturnType(m)) {
// Check context
if (!returnTypeIs(m, ActionContext.class)) {
throw new IllegalArgumentException("Return type must be void or context");
}
// Check context type
final Type contextType = firstGenericTypeArg(m.getGenericReturnType());
if (!Entity.class.equals(toClass(contextType))) {
throw new IllegalArgumentException("Context type must be entity");
}
}
// Create new schedule action method
return new ScheduleForActorMethod(constructor, initialDelay, period, unit);
}
}