package be.iminds.ilabt.jfed.highlevel.jobs.states;

import be.iminds.ilabt.jfed.experiment.Experiment;
import be.iminds.ilabt.jfed.experiment.SfaExperimentPart;
import be.iminds.ilabt.jfed.experiment.tasks.ExperimentTaskStatus;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Server;
import be.iminds.ilabt.jfed.highlevel.controller.TaskExecution;
import be.iminds.ilabt.jfed.highlevel.jobs.Job;
import be.iminds.ilabt.jfed.highlevel.jobs.State;
import be.iminds.ilabt.jfed.highlevel.model.InternalState;
import be.iminds.ilabt.jfed.highlevel.model.Sliver;
import be.iminds.ilabt.jfed.highlevel.tasks.AllocateSliverTask;
import be.iminds.ilabt.jfed.highlevel.tasks.CreateSliverTask;
import be.iminds.ilabt.jfed.highlevel.tasks.HighLevelTaskFactory;
import be.iminds.ilabt.jfed.highlevel.util.AggregateManagerWrapperFactory;
import be.iminds.ilabt.jfed.lowlevel.api.AbstractGeniAggregateManager;
import be.iminds.ilabt.jfed.lowlevel.api.user_spec.UserSpec;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.AggregateManagerWrapper;
import be.iminds.ilabt.jfed.rspec.rspec_source.RequestRspecSource;
import be.iminds.ilabt.jfed.util.GeniUrn;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.stream.Collectors;

/**
 * User: twalcari
 * Date: 12/4/2015
 * Time: 14:37
 */
public class AllocateSliversOnAuthorityState extends State {


    private final Job<?> job;
    private final Experiment experiment;
    private final SfaExperimentPart experimentPart;
    private final List<UserSpec> userSpecs;

    @Nullable
    private final RequestRspecSource overridingRequestRspecSource;

    private final HighLevelTaskFactory hltf;
    private final AggregateManagerWrapperFactory aggregateManagerWrapperFactory;


    private boolean success = false;
    private List<GeniUrn> createdSlivers = null;
    private AbstractGeniAggregateManager.AggregateManagerReply rawReply;

    private Throwable exception;

    protected AllocateSliversOnAuthorityState(Job<?> job, SfaExperimentPart experimentPart,
                                              List<UserSpec> userSpecs, @Nullable RequestRspecSource overridingRequestRspecSource,
                                              HighLevelTaskFactory hltf,
                                              AggregateManagerWrapperFactory aggregateManagerWrapperFactory) {
        super(String.format("Allocating resources on %s", experimentPart.getName()));
        this.job = job;
        this.userSpecs = userSpecs;
        this.overridingRequestRspecSource = overridingRequestRspecSource;
        this.hltf = hltf;
        this.aggregateManagerWrapperFactory = aggregateManagerWrapperFactory;

        this.experiment = experimentPart.getExperiment();
        this.experimentPart = experimentPart;
    }

    @Nonnull
    @Override
    public ExperimentTaskStatus executeState(Job<?> job) throws InterruptedException {
        experimentPart.setState(InternalState.ALLOCATING);

        if (!experimentPart.getConnectSfaAuthority().hasFlag(Server.Flag.featureReservationsNitos) &&
                supportsAllocateProvision()) {
            //do allocate call
            AllocateSliverTask ast = hltf.allocateSliver(experiment.getSlice(), experiment.getRequestedStartTime(),
                    experiment.getRequestedEndTime(), experiment.getUserSpecs(), experimentPart.getConnectSfaAuthority(), overridingRequestRspecSource);
            TaskExecution taskExecution = this.job.submitTaskAndWait(ast);

            if (taskExecution.getState() == TaskExecution.TaskState.SUCCESS) {
                rawReply = ast.getRawReply();
                success = true;
                experimentPart.setState(InternalState.ALLOCATED);

                assert ast.getCreatedSlivers() != null;
                createdSlivers =
                        ast.getCreatedSlivers().stream().map(Sliver::getUrn).collect(Collectors.toList());
            } else {
                exception = taskExecution.getException();
            }
        } else {
            //do CreateSliver-call

            CreateSliverTask cst = hltf.createSliver(experiment.getSlice(), experiment.getRequestedStartTime(),
                    experiment.getRequestedEndTime(),
                    experimentPart.getConnectSfaAuthority(),
                    userSpecs, overridingRequestRspecSource);
            TaskExecution taskExecution = this.job.submitTaskAndWait(cst);

            if (taskExecution.getState() == TaskExecution.TaskState.SUCCESS) {
                rawReply = cst.getRawReply();
                success = true;
                //we don't need to do provisioning, so we can directly wait for the resources to become ready
                experimentPart.setState(InternalState.PROVISIONED);

                createdSlivers =
                        cst.getCreatedSlivers().stream().map(Sliver::getUrn).collect(Collectors.toList());
            } else {
                exception = taskExecution.getException();
            }
        }
        return success ? ExperimentTaskStatus.SUCCESS : ExperimentTaskStatus.FAILED;
    }

    public boolean isSuccess() {
        return success;
    }

    public List<GeniUrn> getCreatedSlivers() {
        return createdSlivers;
    }

    private boolean supportsAllocateProvision() {
        AggregateManagerWrapper amWrapper =
                aggregateManagerWrapperFactory.getAggregateManagerWrapper(experimentPart.getConnectSfaAuthority());

        return amWrapper.hasSeperateAllocateAndProvision();
    }

    public AbstractGeniAggregateManager.AggregateManagerReply getRawReply() {
        return rawReply;
    }

    public Throwable getException() {
        return exception;
    }
}
