package ru.kirillius.pf.sdn.api.flow;

import lombok.Getter;
import ru.kirillius.pf.sdn.entity.ActionConfig;
import ru.kirillius.pf.sdn.entity.FlowConfig;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

public class Flow {
    public Flow(FlowConfig config, Function<ActionConfig, FlowAction> actionFactory) {
        this.config = config;
        graph = new ExecutionGraph(config.getActions(), config.getConnections(), actionFactory);
    }

    @Getter
    private final FlowConfig config;
    private final ExecutionGraph graph;

    private final AtomicBoolean running = new AtomicBoolean(false);
    private final AtomicBoolean interrupted = new AtomicBoolean(false);

    private AtomicInteger currentStep = new AtomicInteger(0);
    private AtomicReference<FlowAction> currentAction = new AtomicReference<>(null);

    public boolean isRunning() {
        return running.get();
    }

    public int getStepCount() {
        return graph.size();
    }

    public int getCurrentStep() {
        return currentStep.get();
    }

    public FlowAction getCurrentAction() {
        return currentAction.get();
    }

    @Getter
    private final List<ExecutionGraph.ExecutionResult> results = new ArrayList<>();

    public void interrupt() {
        interrupted.set(false);
    }

    public void execute() throws InterruptedException {
        interrupted.set(false);
        running.set(true);
        currentStep.set(0);
        results.clear();
        try {
            var listener = graph.getStartExecutingEvent().add(action -> currentAction.set(action));

            var iterator = graph.execute();

            while (iterator.hasNext()) {
                if (interrupted.get()) {
                    throw new InterruptedException();
                }
                currentStep.incrementAndGet();
                results.add(iterator.next());
            }

            graph.getStartExecutingEvent().remove(listener);

        } finally {
            interrupted.set(false);
            running.set(false);
        }
    }
}
