package eu.dnetlib.validator2.validation.task;

import eu.dnetlib.validator2.engine.Reporter;
import eu.dnetlib.validator2.engine.Rule;
import eu.dnetlib.validator2.engine.RuleEngine;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

class ExecutorBasedValidationTask<T, R extends Rule<T>> extends AbstractValidationTask<T, R> {

    protected final ExecutorService executor;

    protected ExecutorBasedValidationTask(T subject, Collection<R> rules, ExecutorService executor) {
        super(subject, rules);
        this.executor = executor;
    }

    @Override
    protected void applyRulesAndReport(final ValidatorDiagnostics<T, R> outputs) {
        try {
            Map<String, Future<?>> rulesToFutures = new HashMap<>();
            Reporter<T, R> reporter = new Reporter<>(outputs);
            for(R rule: rules) {
                Future<?> future = executor.submit(
                        //TODO: Don't cast to Rule<T>
                    () -> RuleEngine.applyAndReport(rule, subject, reporter)
                );
                rulesToFutures.put(rule.getContext().getIdProperty().getValue(), future);
            }
            //TODO: Allow users to customize expiration time
            //TODO: The following loop will block the current thread waiting for each future to complete.
            //      Do we need to avoid it and offer non-blocking behavior to the user?
            for(Map.Entry<String, Future<?>> ruleToFuture: rulesToFutures.entrySet()) {
                try {
                    // Wait for the future to complete
                    ruleToFuture.getValue().get();
                }
                catch (InterruptedException ie) {
                    outputs.setInterrupted(ruleToFuture.getKey());
                }
            }
        }
        catch (Throwable t) {
            // Catch all possible errors:
            // - OutOfMemory: new calls above fail
            // - Rejected: executor submissions get rejected
            // - ExecutionException: may be thrown while waiting for the futures,
            //   should never occur in theory (reporter consumes all errors)
            throw new RuntimeException("Internal error", t);
        }
    }
}
