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

import be.iminds.ilabt.jfed.experiment.ExperimentController;
import be.iminds.ilabt.jfed.experimenter_gui.slice.events.SCPEvent;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import be.iminds.ilabt.jfed.ui.javafx.GlyphUtils;
import com.google.common.eventbus.EventBus;
import impl.org.controlsfx.skin.DecorationPane;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.geometry.HPos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import org.controlsfx.glyphfont.FontAwesome;
import org.controlsfx.validation.ValidationMessage;
import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.file.InvalidPathException;
import java.nio.file.Paths;

/**
 * User: twalcari
 * Date: 8/19/2016
 * Time: 17:26
 */
public class SCPAction extends ExperimentAction {
    private static final Logger LOG = LoggerFactory.getLogger(SCPAction.class);

    private final FXRspecNode node;
    private final EventBus eventBus;

    public SCPAction(Window parentWindow, ExperimentController experimentController, FXRspecNode node, EventBus eventBus) {
        super(parentWindow, experimentController);
        this.node = node;
        this.eventBus = eventBus;
    }

    @Override
    public void run() {
        assert Platform.isFxApplicationThread();

        Dialog<ButtonType> dialog = new Dialog<>();
        dialog.initOwner(parentWindow);
        dialog.setTitle("Upload file to " + node.getClientId());
        dialog.setHeaderText("Upload file to " + node.getClientId());
        dialog.setGraphic(GlyphUtils.createDialogGlyph(FontAwesome.Glyph.UPLOAD, Color.BROWN));

        GridPane dialogContent = new GridPane();
        dialogContent.setHgap(5);
        dialogContent.setVgap(10);

        dialogContent.add(new Label("Action:"), 0, 0);

        final ToggleGroup typeToggleGroup = new ToggleGroup();
        HBox typeHBox = new HBox(5);
        RadioButton uploadRadioButton = new RadioButton("Upload");
        RadioButton downloadRadioButton = new RadioButton("Download");

        typeToggleGroup.getToggles().addAll(uploadRadioButton, downloadRadioButton);
        typeToggleGroup.selectToggle(uploadRadioButton);

        typeHBox.getChildren().addAll(uploadRadioButton, downloadRadioButton);
        dialogContent.add(typeHBox, 1, 0);

        //from fields
        Label fromLabel = new Label();
        fromLabel.textProperty().bind(Bindings.createStringBinding(
                () -> typeToggleGroup.getSelectedToggle() == uploadRadioButton ? "Location:" : "Remote location:",
                typeToggleGroup.selectedToggleProperty()));

        dialogContent.add(fromLabel, 0, 1);

        TextField fromTextField = new TextField();
        fromTextField.setPrefWidth(300);
        dialogContent.add(fromTextField, 1, 1);

        MenuButton localBrowseButton = new MenuButton("Browse ...");
        localBrowseButton.visibleProperty().bind(typeToggleGroup.selectedToggleProperty().isEqualTo(uploadRadioButton));
        dialogContent.add(localBrowseButton, 2, 1);

        MenuItem localFileMenuItem = new MenuItem("File");
        localFileMenuItem.setOnAction(event -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("Select file to upload");

            File chosenFile = fileChooser.showOpenDialog(null);

            if (chosenFile != null)
                fromTextField.setText(chosenFile.getAbsolutePath());
        });

        MenuItem localDirMenuItem = new MenuItem("Directory");
        localDirMenuItem.setOnAction(event -> {
            DirectoryChooser dirChooser = new DirectoryChooser();
            dirChooser.setTitle("Select directory to upload");

            File chosenFile = dirChooser.showDialog(null);

            if (chosenFile != null)
                fromTextField.setText(chosenFile.getAbsolutePath());
        });

        localBrowseButton.getItems().addAll(localFileMenuItem, localDirMenuItem);

        //remote fields
        Label toLabel = new Label();
        toLabel.textProperty().bind(Bindings.createStringBinding(
                () -> typeToggleGroup.getSelectedToggle() == uploadRadioButton ? "Remote location:"
                        : "Download folder:",
                typeToggleGroup.selectedToggleProperty()));
        dialogContent.add(toLabel, 0, 2);

        TextField toTextField = new TextField();
        dialogContent.add(toTextField, 1, 2);

        Button toBrowseButton = new Button("Browse ...");
        toBrowseButton.visibleProperty().bind(typeToggleGroup.selectedToggleProperty().isEqualTo(downloadRadioButton));
        toBrowseButton.setOnAction(event -> {
            DirectoryChooser dirChooser = new DirectoryChooser();
            dirChooser.setTitle("Select directory to download to");

            File chosenFile = dirChooser.showDialog(null);

            if (chosenFile != null)
                toTextField.setText(chosenFile.getAbsolutePath());
        });
        dialogContent.add(toBrowseButton, 2, 2);

        //wrap in decorationPane to make ValidationSupport work
        final DecorationPane decorationPane = new DecorationPane();
        decorationPane.getChildren().add(dialogContent);
        dialog.getDialogPane().setContent(decorationPane);

        //setup validation-support
        ValidationSupport validationSupport = new ValidationSupport();
        validationSupport.registerValidator(fromTextField, true, (Validator<String>) (control, s) -> {
            ValidationResult result = new ValidationResult();
            if (typeToggleGroup.getSelectedToggle() == uploadRadioButton) {
                File localFile = new File(s);
                result.addErrorIf(control, "Not a valid file", !localFile.exists());
            } else {
                result.addErrorIf(control, "A remote path is required", s.isEmpty());
                try {
                    Paths.get(s);
                } catch (InvalidPathException ex) {
                    result.add(ValidationMessage.error(control, "Not a valid remote path"));
                }
            }


            return result;
        });

        validationSupport.registerValidator(toTextField, true, (Validator<String>) (control, s) -> {
            ValidationResult result = new ValidationResult();
            if (typeToggleGroup.getSelectedToggle() == downloadRadioButton) {
                File localFile = new File(s);
                result.addErrorIf(control, "Not a valid directory", !localFile.exists());
                result.addErrorIf(control, "Not a directory", !localFile.isDirectory());
            } else {
                result.addErrorIf(control, "A remote path is required", s.isEmpty());
                try {
                    Paths.get(s);
                } catch (InvalidPathException ex) {
                    result.add(ValidationMessage.error(control, "Not a valid remote path"));
                }
            }
            return result;
        });


        //register uploadRadioButton to validationsupport to ensure invalidation when selection changes
        validationSupport.registerValidator(uploadRadioButton, (Validator<Boolean>) (control, aBoolean) -> {
            return new ValidationResult(); /*no-op*/
        });


        final ColumnConstraints firstColumnConstraints = new ColumnConstraints();
        firstColumnConstraints.setPrefWidth(100);
        firstColumnConstraints.setHalignment(HPos.RIGHT);
        dialogContent.getColumnConstraints().add(firstColumnConstraints);

        //setup buttons
        dialog.getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
        Node okButton = dialog.getDialogPane().lookupButton(ButtonType.OK);
        okButton.disableProperty().bind(validationSupport.invalidProperty());

        dialog.showAndWait().filter(buttonType -> buttonType == ButtonType.OK).ifPresent(buttonType -> eventBus.post(new SCPEvent(experiment, node,
                typeToggleGroup.getSelectedToggle() == uploadRadioButton,
                fromTextField.getText(), toTextField.getText())));


    }

}
