package be.iminds.ilabt.jfed.experimenter_gui.editor.rspec_validation;

import be.iminds.ilabt.jfed.rspec.model.*;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXModelRspec;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import be.iminds.ilabt.jfed.rspec.rspec_source.RequestRspecSource;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.stage.Window;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * User: twalcari
 * Date: 1/22/2015
 * Time: 16:44
 */
public class DiskImageUrlValidator implements RspecValidator {
    private static final Logger LOG = LoggerFactory.getLogger(DiskImageUrlValidator.class);

    @Override
    public RspecValidationResult validateRSpec(RspecCheckReason reason, RequestRspecSource requestRspecSource, Window dialogOwner) {
        FXModelRspec modelRspec = requestRspecSource.getModelRspec(ModelRspecType.FX);
        assert (modelRspec != null);

        Set<RspecNode> permanentProblemNodes = new HashSet<>();
        List<DiskImage> permanentProblemDiskImages = new ArrayList<>();
        Set<FXRspecNode> fixableProblemNodes = new HashSet<>();
        List<DiskImage> fixableProblemDiskImages = new ArrayList<>();
        for (FXRspecNode node : modelRspec.getNodes()) {
            for (SliverType sliverType : node.getSliverTypes()) {
                for (DiskImage diskImage : sliverType.getDiskImages()) {
                    if (diskImage.getName() == null && diskImage.getUrl() == null) {
                        permanentProblemNodes.add(node);
                        permanentProblemDiskImages.add(diskImage);
                    }
                    if (diskImage.getName() == null && diskImage.getUrl() != null) {
                        fixableProblemNodes.add(node);
                        fixableProblemDiskImages.add(diskImage);
                    }
                }
            }
        }

        String permanentProblemNodesString = "";
        for (RspecNode node : permanentProblemNodes)
            if (permanentProblemNodesString.isEmpty())
                permanentProblemNodesString += "" + node.getClientId();
            else
                permanentProblemNodesString += "," + node.getClientId();

        String fixableProblemNodesString = "";
        for (RspecNode node : fixableProblemNodes)
            if (fixableProblemNodesString.isEmpty())
                fixableProblemNodesString += "" + node.getClientId();
            else
                fixableProblemNodesString += "," + node.getClientId();

        if (!permanentProblemDiskImages.isEmpty()) {
            String abortText = "Abort";
            String ignoreText = "Ignore";
            boolean showIgnore = false;
            switch (reason) {
                case SWITCH_TO_VISUAL_EDITOR: {
                    abortText = "Abort and return to editor";
                    ignoreText = "Ignore";
                    showIgnore = true;
                    break;
                }
                case RUN_EXPERIMENT: {
                    abortText = "Abort and continue editing";
                    ignoreText = "Ignore and Run Experiment Anyway";
                    showIgnore = true;
                    break;
                }
                case CHECK_RSPEC: {
                    abortText = "Ok";
                    showIgnore = false;
                    break;
                }
                case OPENED_FILE: {
                    abortText = "Ok";
                    showIgnore = false;
                    break;
                }
            }
            ButtonType abort = new ButtonType(abortText, ButtonBar.ButtonData.CANCEL_CLOSE);
            ButtonType ignore = new ButtonType(ignoreText, ButtonBar.ButtonData.CANCEL_CLOSE);

            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.initOwner(dialogOwner);
            alert.setTitle("disk_image without name");
            alert.setContentText("Problem in RSpec: The following nodes have a disk_image without a name: " + permanentProblemNodesString);

            alert.getButtonTypes().setAll(abort);
            if (showIgnore)
                alert.getButtonTypes().add(ignore);

            Optional<ButtonType> chosenAction = alert.showAndWait();

            switch (reason) {
                case SWITCH_TO_VISUAL_EDITOR: //fall-through
                case RUN_EXPERIMENT:
                    return new RspecValidationResult(requestRspecSource, chosenAction.isPresent() && chosenAction.get() == abort/*abort*/);
                case CHECK_RSPEC: //fall-through
                case OPENED_FILE:
                    return new RspecValidationResult(requestRspecSource, false/* == ignore*/);
            }
        }
        if (!fixableProblemDiskImages.isEmpty()) {
            String abortText = "Abort";
            String fixText = "Fix and continue";
            String ignoreText = "Keep as is and continue";
            boolean showIgnore = false;
            switch (reason) {
                case SWITCH_TO_VISUAL_EDITOR: {
                    abortText = "Keep as is but return to editor";
                    ignoreText = "Ignore";
                    showIgnore = true;
                    break;
                }
                case RUN_EXPERIMENT: {
                    abortText = "Abort and continue editing";
                    ignoreText = "Ignore and Run Experiment Anyway";
                    fixText = "Fix and Run Experiment";
                    showIgnore = true;
                    break;
                }
                case CHECK_RSPEC: {
                    abortText = "Keep as is";
                    fixText = "Fix";
                    showIgnore = false;
                    break;
                }
                case OPENED_FILE: {
                    abortText = "Keep as is";
                    fixText = "Fix";
                    showIgnore = false;
                    break;
                }
            }


            ButtonType fixButtonType = new ButtonType(fixText, ButtonBar.ButtonData.YES);
            ButtonType ignoreButtonType = new ButtonType(ignoreText, ButtonBar.ButtonData.CANCEL_CLOSE);
            ButtonType abortButtonType = new ButtonType(abortText, ButtonBar.ButtonData.CANCEL_CLOSE);

            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.initOwner(dialogOwner);
            alert.setTitle("disk_image without name");
            alert.setContentText("Problem in RSpec:\nThe following nodes have a disk_image without the required name attribute (instead they have an url attribute):\n  " + fixableProblemNodesString + "\n\n" +
                            "This disk_image tag without name is not strictly valid, and may fail on some AMs.\n\n" +
                            "Do you want to fix this by converting the url to name?"
            );

            if (showIgnore)
                alert.getButtonTypes().setAll(fixButtonType, ignoreButtonType, abortButtonType);
            else
                alert.getButtonTypes().setAll(fixButtonType, abortButtonType);
            ButtonType chosenAction = alert.showAndWait().orElse(null);

            if (chosenAction == fixButtonType) {
                LOG.info("Fixing RSpec disk_image");
                for (FXRspecNode node : fixableProblemNodes) {
                    boolean fixSliverTypes = false;
                    List<SliverType> newSliverTypes = new ArrayList<>();
                    for (SliverType sliverType : node.getSliverTypes()) {
                        List<DiskImage> newDiskImages = new ArrayList<>();
                        boolean fixDiskImage = false;
                        for (DiskImage diskImage : sliverType.getDiskImages()) {
                            if (diskImage.getName() == null && diskImage.getUrl() != null) {
                                newDiskImages.add(new DiskImage(diskImage.getUrl(), null, diskImage.getOs(), diskImage.getVersion(), diskImage.getDescription(), null));
                                fixDiskImage = true;
                                LOG.info("   Fixed disk_image for name " + node.getClientId());
                            } else {
                                newDiskImages.add(diskImage);
                            }
                        }
                        if (fixDiskImage) {
                            SliverType newSliverType =
                                    new SliverTypeBuilder(sliverType).setDiskImages(newDiskImages).build();
                            newSliverTypes.add(newSliverType);
                            fixSliverTypes = true;
                        } else
                            newSliverTypes.add(sliverType);
                    }
                    if (fixSliverTypes)
                        node.getSliverTypes().setAll(newSliverTypes);
                }
                //fixed, so return replacement
                return new RspecValidationResult(modelRspec);
            }

            boolean doAbort = chosenAction == abortButtonType;
            return new RspecValidationResult(requestRspecSource, doAbort);
        }

        //all ok
        return null;
    }
}
