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

import be.iminds.ilabt.jfed.experiment.Experiment;
import be.iminds.ilabt.jfed.experiment.ExperimentPart;
import be.iminds.ilabt.jfed.experiment.ExperimentState;
import be.iminds.ilabt.jfed.highlevel.controller.TaskThread;
import be.iminds.ilabt.jfed.highlevel.jobs.parts.ExperimentPartController;
import be.iminds.ilabt.jfed.highlevel.jobs.parts.ExperimentPartControllerManager;
import be.iminds.ilabt.jfed.highlevel.model.InternalState;
import be.iminds.ilabt.jfed.highlevel.model.Sliver;
import be.iminds.ilabt.jfed.highlevel.tasks.HighLevelTaskFactory;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.StatusDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * User: twalcari
 * Date: 11/25/2015
 * Time: 17:40
 */
public class UpdateExperimentJob extends AbstractJob<Void> {
    private static final Logger LOG = LoggerFactory.getLogger(UpdateExperimentJob.class);

    private final ExperimentPartControllerManager experimentPartControllerManager;


    public UpdateExperimentJob(Experiment experiment, HighLevelTaskFactory hltf, TaskThread tt,
                               ExperimentPartControllerManager experimentPartControllerManager) {
        super("Check expiration of " + experiment.getName(), experiment, hltf, tt);
        this.experimentPartControllerManager = experimentPartControllerManager;
    }

    @Override
    public Void execute() throws Exception {
        assert experiment.getSlice() != null;
        assert experiment.getSlice().getExpirationDate() != null;

        //first check if expiration time has changed without we knowing it.
        setAndRunState(new UpdateExperimentSlicedState());

        //this will be the case when this job was started because one or more resources have reached their expiration time
        if (experiment.getExperimentState() == ExperimentState.EXPIRING) {
            boolean allResourcesExpired = true;
            boolean hasChangingExperimentParts = false;


            for (ExperimentPart part : experiment.getParts()) {
                if (part.getState() != InternalState.UNALLOCATED
                        && part.getState() != InternalState.DELETED
                        && part.getState() != InternalState.FAILED)
                    allResourcesExpired = false;

                if (part.getState() == InternalState.CHANGING)
                    hasChangingExperimentParts = true;
            }

            for (Sliver sliver : experiment.getSlice().getSlivers()) {
                if (sliver.getStatus().getGlobalStatus() != StatusDetails.SliverStatus.UNALLOCATED &&
                        sliver.getStatus().getGlobalStatus() != StatusDetails.SliverStatus.FAIL)
                    allResourcesExpired = false;
            }

            if (allResourcesExpired) {
                experiment.setExperimentState(ExperimentState.EXPIRED);
            } else if (!hasChangingExperimentParts) {
                //check if one or more experimentparts are changing
                experiment.setExperimentState(ExperimentState.READY);
            } else {
                //TODO: do we start updating the experimentpart that is changing here? Or is this done elsewere?
            }
        } else {
            //check if there is still at least one active resource left
            boolean hasActiveResources = false;
            for (ExperimentPart part : experiment.getParts()) {
                if (InternalState.isActiveState(part.getState()))
                    hasActiveResources = true;
            }

            if (!hasActiveResources)
                experiment.setExperimentState(ExperimentState.EMPTY);
        }

        return null;
    }

    private class UpdateExperimentSlicedState extends ExperimentPartsSlicedState<ExperimentPartStateSlice> {
        public UpdateExperimentSlicedState() {
            super("Updating experiment status", experiment, experimentPartControllerManager);
        }

        @Override
        public <EP extends ExperimentPart> ExperimentPartStateSlice createJobSlice(ExperimentPartController<EP> controller, EP experimentPart) {
            return controller.update(UpdateExperimentJob.this, experimentPart);
        }
    }


}
