package tc.oc.pgm.match;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import javax.annotation.Nullable;

import tc.oc.commons.core.reflect.Methods;
import tc.oc.pgm.module.ModuleLoadException;

/**
 * Creates a {@link MatchModule} unconditionally for every match. The module
 * must have a public constructor that accepts one {@link Match} parameter.
 */
public class FixtureMatchModuleFactory<T extends MatchModule> implements MatchModuleFactory<T> {

    private final Constructor<T> constructor;

    public FixtureMatchModuleFactory(Class<T> matchModuleClass) {
        if(Modifier.isAbstract(matchModuleClass.getModifiers())) {
            throw new IllegalArgumentException(matchModuleClass.getName() + " is abstract");
        }

        try {
            this.constructor = matchModuleClass.getConstructor(Match.class);
        } catch(NoSuchMethodException e) {
            throw new IllegalArgumentException("Missing constructor " + matchModuleClass.getSimpleName() + "(Match) in class " + matchModuleClass.getName());
        }

        Methods.assertPublicThrows(constructor, ModuleLoadException.class);
    }

    @Override
    public @Nullable T createMatchModule(Match match) throws ModuleLoadException {
        try {
            return constructor.newInstance(match);
        } catch(InvocationTargetException e) {
            if(e.getCause() instanceof ModuleLoadException) {
                throw (ModuleLoadException) e.getCause();
            } else {
                // Already verified that it cannot throw any other checked exceptions
                throw (RuntimeException) e.getCause();
            }
        } catch(InstantiationException | IllegalAccessException e) {
            // This should be pretty much impossible due to the checks in the constructor
            throw new IllegalStateException(e);
        }
    }
}
