package com.xxx.amp.magenta.livecodebench;

import com.xxx.amp.magenta.livecodebench.model.TestRun;
import org.json.JSONArray;
import org.json.JSONObject;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * A base abstract implementation for a test suite runner.
 *
 * @author Alexey Kharlamov <aih1013@xxx.com>
 */
public abstract class ProblemTestSuite {
    public static final String KEY_METHOD_NAME = "fn_name";
    private final Class<?> solutionClass;
    public List<String> inputs;
    public List<String> outputs;
    private Method method;

    public ProblemTestSuite(Class<?> solutionClass) {
        this.solutionClass = solutionClass;
    }

    public void load(JSONObject serialized) throws LiteralSyntaxException {
        String methodName = "main";
        if (serialized.has(KEY_METHOD_NAME) && !serialized.isNull(KEY_METHOD_NAME)) {
            methodName = serialized.getString(KEY_METHOD_NAME);
        }

        this.setMethod(findMethod(getSolutionClass(), methodName));
        this.inputs = jsonArrayToList(serialized.getJSONArray("inputs"));
        this.outputs = jsonArrayToList(serialized.getJSONArray("outputs"));
        assert inputs.size() == outputs.size();
    }

    protected Method findMethod(Class<?> cls, String name) {
        for (Method m : cls.getMethods()) {
            if (m.getName()
                    .equals(name)) {
                return m;
            }
        }
        throw new RuntimeException("Unknown method name: " + name);
    }

    public Class<?> getSolutionClass() {
        return solutionClass;
    }

    private List<String> jsonArrayToList(JSONArray array) {
        List<String> result = new ArrayList<>(array.length());
        for (int i = 0; i < array.length(); i++) {
            result.add(array.getString(i));
        }
        return result;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public abstract List<TestRun> run(long testTimeoutMills);
}
