package be.iminds.ilabt.jfed.experimenter_gui.slice.jobs;

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.experimenter_gui.slice.QoeDialog;
import be.iminds.ilabt.jfed.highlevel.controller.TaskThread;
import be.iminds.ilabt.jfed.highlevel.jobs.*;
import be.iminds.ilabt.jfed.highlevel.model.Sliver;
import be.iminds.ilabt.jfed.highlevel.tasks.HighLevelTaskFactory;
import be.iminds.ilabt.jfed.highlevel.tasks.ListPendingReputationQuestionnairesTask;
import be.iminds.ilabt.jfed.highlevel.tasks.RegisterReputationTask;
import be.iminds.ilabt.jfed.lowlevel.JFedException;
import be.iminds.ilabt.jfed.util.FXPlatformUtil;

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

/**
 * User: twalcari
 * Date: 1/11/2016
 * Time: 11:51
 */
public class QoeJob extends AbstractJob<Void> {

    private final Collection<SfaExperimentPart> experimentParts;

    QoeJob(Experiment experiment, Collection<SfaExperimentPart> experimentParts, HighLevelTaskFactory hltf, TaskThread tt) {
        super("Registering experiment to reputation service", experiment, hltf, tt);
        this.experimentParts = experimentParts;
    }

    @Override
    public Void execute() throws Exception {


        CheckForQuestionnairesState checkForQuestionnairesState = new CheckForQuestionnairesState();

        setAndRunState(checkForQuestionnairesState);

        if (checkForQuestionnairesState.getTask().getPendingQuestionnaires() == null ||
                checkForQuestionnairesState.getTask().getPendingQuestionnaires().isEmpty()) {
            updateMessage("No questionnaires available");
            return null;
        }

        AskUserForQOEState askUserForQOEState = new AskUserForQOEState();
        setAndRunState(askUserForQOEState);

        if (askUserForQOEState.getStatus() != ExperimentTaskStatus.SUCCESS) {
            updateMessage("User cancelled feedback dialog");
            return null;
        }


        setAndRunState(new SubmitQoeQuestionnaires(askUserForQOEState.getReplies()));

        return null;
    }


    private final class CheckForQuestionnairesState extends SingleCallState<ListPendingReputationQuestionnairesTask> {

        protected CheckForQuestionnairesState() {
            super("Check pending QoE questionnaires",
                    hltf.listPendingReputationQuestionnaires(experiment.getSlice()));
        }
    }

    private final class AskUserForQOEState extends State {

        private List<QoeDialog.QoeReply> replies = null;

        protected AskUserForQOEState() {
            super("Asking user for Quality of Experience scores");
        }

        @Nonnull
        @Override
        protected ExperimentTaskStatus executeState(Job<?> job) throws InterruptedException, JFedException {
            replies = FXPlatformUtil.runAndWait(() -> {
                QoeDialog dialog = new QoeDialog(experiment, experimentParts);
                return dialog.showAndWait().orElse(null);
            });
            return replies != null ? ExperimentTaskStatus.SUCCESS : ExperimentTaskStatus.FAILED;
        }

        public List<QoeDialog.QoeReply> getReplies() {
            return replies;
        }
    }

    private final class SubmitQoeQuestionnaires extends MultipleCallState<RegisterReputationTask> {

        private final List<QoeDialog.QoeReply> replies;


        public SubmitQoeQuestionnaires(List<QoeDialog.QoeReply> replies) {
            super("Submitting QoE questionnaires to reputation service", QoeJob.this);
            this.replies = replies;
        }

        @Override
        public Collection<RegisterReputationTask> getTasks() {
            return replies.stream().map(reply ->
                    hltf.registerReputation(experiment.getSlice(),
                            reply.getExperimentPart().getSlivers().stream().map(Sliver::getUrn).collect(Collectors.toList()),
                            reply.getOverallScore(),
                            reply.getQualityScore(),
                            reply.getAvailabilityScore()))
                    .collect(Collectors.toList());
        }
    }
}
