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

import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfig;
import be.iminds.ilabt.jfed.experimenter_gui.editor.rspec_validation.RspecCheckReason;
import be.iminds.ilabt.jfed.experimenter_gui.editor.rspec_validation.RspecValidationEngine;
import be.iminds.ilabt.jfed.experimenter_gui.editor.rspec_validation.RspecValidationResult;
import be.iminds.ilabt.jfed.experimenter_gui.editor.views.EditorViewType;
import be.iminds.ilabt.jfed.experimenter_gui.editor.views.RawRspecEditor;
import be.iminds.ilabt.jfed.experimenter_gui.ui.ribbon.RibbonButton;
import be.iminds.ilabt.jfed.experimenter_gui.util.ui.CustomChoiceDialog;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Server;
import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import be.iminds.ilabt.jfed.rspec.convert.RspecConverter;
import be.iminds.ilabt.jfed.rspec.model.ModelRspecType;
import be.iminds.ilabt.jfed.rspec.model.RspecFactory;
import be.iminds.ilabt.jfed.rspec.model.RspecFactoryFactory;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXModelRspec;
import be.iminds.ilabt.jfed.rspec.rspec_source.RequestRspecSource;
import be.iminds.ilabt.jfed.ui.javafx.FXMLUtil;
import be.iminds.ilabt.jfed.ui.javafx.GlyphUtils;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import javafx.fxml.FXML;
import javafx.scene.paint.Color;
import javafx.util.StringConverter;
import org.controlsfx.dialog.CommandLinksDialog;
import org.controlsfx.glyphfont.FontAwesome;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * User: twalcari
 * Date: 7/1/2014
 * Time: 11:10
 */
public class RspecEditorRibbonTab extends EditorRibbonTab {
    private static final Logger LOG = LoggerFactory.getLogger(RspecEditorRibbonTab.class);

    private final RspecValidationEngine validationEngine = new RspecValidationEngine();

    private JFedGuiConfig config;
    private TestbedInfoSource testbedInfoSource;

    @FXML
    private RibbonButton formatCodeButton;
    @FXML
    private RibbonButton verifyCodeButton;
    @FXML
    private RibbonButton bindButton;
    @FXML
    private RibbonButton searchCodeButton;
    @FXML
    private RibbonButton replaceCodeButton;

    public RspecEditorRibbonTab() {
        FXMLUtil.injectFXML(this, aClass -> {
            if (aClass == GenericEditorRibbonComponentGroupController.class) {
                return new GenericEditorRibbonComponentGroupController(RspecEditorRibbonTab.this);
            }
            throw new RuntimeException("Unexpected class in ControllerFactory: " + aClass.getName());
        });

        setOnSelectionChanged(event -> {
            if (isSelected() && getActiveExperimentEditor().getCurrentEditorViewType() != EditorViewType.RAW) {
                getActiveExperimentEditor().switchToEditorViewByType(EditorViewType.RAW);
            }
        });

    }

    public void setJFedGuiConfig(JFedGuiConfig conf) {
        this.config = conf;
    }

    public void setAuthorityList(TestbedInfoSource testbedInfoSource) {
        this.testbedInfoSource = testbedInfoSource;
    }

    private RawRspecEditor getActiveRawRspecEditor() {
        assert (getActiveExperimentEditor().getCurrentEditorViewType() == EditorViewType.RAW);

        return (RawRspecEditor) getActiveExperimentEditor().getCurrentEditableRspecView();
    }


    @FXML
    private void onFormatCodeAction() {
        assert formatCodeButton != null;
        if (!getActiveRawRspecEditor().formatXmlCode())
            JFDialogs.create()
                    .owner(formatCodeButton)
                    .message("Formatting your Rspec failed. Please check if the XML is valid")
                    .title("The Rspec could not be formatted").showWarning();
    }

    @FXML
    private void onVerifyRspecAction() {
        RequestRspecSource source = getActiveExperimentEditor().getCurrentRequestRspecSource();
        RspecValidationResult verifyRspecResult =
                validationEngine.validateRSpec(RspecCheckReason.CHECK_RSPEC, source, verifyCodeButton.getScene().getWindow());


        assert verifyCodeButton != null;
        if (verifyRspecResult == null) {
            JFDialogs.create()
                    .owner(verifyCodeButton)
                    .title("Rspec Verify Result")
                    .message("No problems found in RSpec.").showInformation();
        } else {
            getActiveExperimentEditor().getEditableRspec().setRequestRspecSource(verifyRspecResult.getReplacementRequestRspecSource());
        }
    }

    @FXML
    private void onBindRspecAction() {
        RequestRspecSource source = getActiveExperimentEditor().getCurrentRequestRspecSource();

        //first verify RSpec!
        FXModelRspec model = source.getModelRspec(ModelRspecType.FX);
        if (model == null) {
            JFDialogs.create()
                    .owner(bindButton)
                    .title("Rspec Parse Error")
                    .message("The RSpec does not correctly parse.").showError();
            return;
        }

        Boolean isLosingData = source.isLosingData(ModelRspecType.FX);
        if (isLosingData == null || isLosingData) {
            JFDialogs.create()
                    .owner(bindButton)
                    .title("Rspec is losing data")
                    .message("This operation will cause data loss. Try \"Verify\" first to solve the problems.").showError();
            return;
        }


        CommandLinksDialog.CommandLinksButtonType bindButtonType =
                new CommandLinksDialog.CommandLinksButtonType("Bind resources","Bind all the nodes and links in this RSpec to a new component_manger",true);

        CommandLinksDialog.CommandLinksButtonType unbindButtonType =
                new CommandLinksDialog.CommandLinksButtonType("Unbind resources","Unbind all nodes and links, making the entire RSpec unbound.",false);

        CommandLinksDialog dialog = new CommandLinksDialog(bindButtonType, unbindButtonType);
        dialog.setTitle("Binding or Unbinding an RSpec");
        dialog.setHeaderText("Binding or Unbinding an RSpec");
        dialog.setContentText("Choose your action:");



        dialog.showAndWait().ifPresent(chosenAction -> {
            if(chosenAction == bindButtonType.getButtonType()){
                assert config != null;
                assert testbedInfoSource != null;

                List<Server> bindableAuthorities = new ArrayList<>(config.getBindableServers());

                bindableAuthorities.sort(Comparator.comparing(auth -> auth.getName().toLowerCase()));

                CustomChoiceDialog<Server> unboundNodesDialog = new CustomChoiceDialog<>();
                unboundNodesDialog.setStringConverter(new StringConverter<Server>() {
                    @Override
                    public String toString(Server object) {
                        return object.getName();
                    }

                    @Override
                    public Server fromString(String string) {
                        throw new UnsupportedOperationException();
                    }
                });
                unboundNodesDialog.setComboboxBelowText(true);
                unboundNodesDialog.initOwner(bindButton.getScene().getWindow());
                unboundNodesDialog.setGraphic(GlyphUtils.createDialogGlyph(FontAwesome.Glyph.PUZZLE_PIECE, Color.CHOCOLATE));
                unboundNodesDialog.setHeaderText("Unbound nodes detected");
                unboundNodesDialog.setContentText("Please select the testbed to which the unbound nodes and links must be assigned:");
                unboundNodesDialog.getItems().setAll(bindableAuthorities);


                Optional<Server> chosenAuthorityInfo = unboundNodesDialog.showAndWait();

                if (chosenAuthorityInfo.isPresent()) {
                    RspecConverter converter = new RspecConverter();
                    RspecFactory rspecFactory = RspecFactoryFactory.getRspecFactoryInstance(ModelRspecType.FX);
                    FXModelRspec newModelRspec =
                            (FXModelRspec) converter.forceComponentManagerBindings(model, rspecFactory, chosenAuthorityInfo.get().getDefaultComponentManagerAsGeniUrn());
                    getActiveExperimentEditor().getEditableRspec().setRequestRspecSource(new RequestRspecSource(newModelRspec));
                }
            } else if(chosenAction == unbindButtonType.getButtonType()){
                RspecConverter converter = new RspecConverter();
                RspecFactory rspecFactory = RspecFactoryFactory.getRspecFactoryInstance(ModelRspecType.FX);

                FXModelRspec newModelRspec = (FXModelRspec) converter.removeComponentManagerBindings(model, rspecFactory);
                getActiveExperimentEditor().getEditableRspec().setRequestRspecSource(new RequestRspecSource(newModelRspec));
            }
        });
    }

    @FXML
    private void onSearchAction() {
        getActiveRawRspecEditor().showSearch();
    }

    @FXML
    private void onReplaceAction() {
        getActiveRawRspecEditor().showReplace();
    }

}
