package de.wwwu.awolf.presenter.algorithms;

import de.wwwu.awolf.model.Line;
import de.wwwu.awolf.presenter.Presenter;
import de.wwwu.awolf.presenter.util.Logging;

import javax.annotation.Nullable;
import java.util.EnumMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;

public class AlgorithmHandler {

    private static AlgorithmHandler instance;
    private Map<Algorithm.Type, Algorithm> algorithmMapping;


    private AlgorithmHandler() {
        //lookup
        lookUp();
    }

    /**
     * Singleton getter
     * @return instance of the singleton
     */
    public static AlgorithmHandler getInstance() {
        if (instance == null) {
            instance = new AlgorithmHandler();
            // Default Constructor
        }

        return instance;
    }

    @Nullable
    public Line runAlgorithmByType(final Algorithm.Type type, final Set<Line> setOfLines) {
        //get the instance
        Algorithm algorithm = algorithmMapping.get(type);
        algorithm.setPresenter(Presenter.getInstance());
        algorithm.setInput(setOfLines);
        Logging.logDebug("run Algorithm: " + algorithm.getClass().getSimpleName());

        //get the executor
        ExecutorService executor = Presenter.getInstance().getExecutor();
        ExecutorCompletionService<Line> completionService = new ExecutorCompletionService<>(executor);

        completionService.submit(algorithm);
        try {
            return completionService.take().get();
        } catch (InterruptedException e) {
            Logging.logError("Interrupt Exception while waiting for result of Algorithm: " + algorithm.getType(), e);
            Thread.currentThread().interrupt();
        } catch (ExecutionException e) {
            Logging.logError("Execution Exception while computing the result of Algorithm: " + algorithm.getType(), e);
        }
        return null;
    }

    private void lookUp() {
        Logging.logDebug("Lookup for Algorithm Classes.");
        ServiceLoader<Algorithm> load = ServiceLoader.load(Algorithm.class);
        load.reload();
        algorithmMapping = new EnumMap<>(Algorithm.Type.class);
        load.forEach(algorithm -> {
            algorithmMapping.put(algorithm.getType(), algorithm);
            Logging.logDebug("Found: " + algorithm.getClass().getSimpleName());
        });
    }

}
