package be.iminds.ilabt.jfed.experimenter_gui.timeline.view.dialogs;

import be.iminds.ilabt.jfed.experimenter_gui.timeline.controller.TimelineController;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.view.TimeTextField;
import be.iminds.ilabt.jfed.experimenter_gui.util.ui.UIUtils;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentBarrierSegment;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentCommand;
import be.iminds.ilabt.jfed.rspec.model.controller.Timestamp;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import be.iminds.ilabt.jfed.ui.javafx.FXMLUtil;
import be.iminds.ilabt.jfed.ui.javafx.GlyphUtils;
import javafx.beans.binding.BooleanBinding;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.stage.Window;
import org.controlsfx.glyphfont.FontAwesome;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class AddMultipleCommandDialog extends GridPane {
    private final TimelineController controller;
    @FXML
    private TextField commandTextField;
    @FXML
    private TimeTextField startTextField;
    @FXML
    private TextField tagTextField;
    @FXML
    private TreeView<String> treeView;

    private final Map<CheckBoxTreeItem<String>, FXRspecNode> nodesMap = new HashMap<>();
    private final Map<CheckBoxTreeItem<String>, ExperimentBarrierSegment> barriersMap = new HashMap<>();

    private final Dialog<ButtonType> dialog;


    private AddMultipleCommandDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes) {
        this.controller = controller;

        //initialize the Scene
        FXMLUtil.injectFXML(this);

        CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<>("Your experiment");
        rootItem.setExpanded(true);
        treeView.setRoot(rootItem);
        treeView.setEditable(false);

        treeView.setCellFactory(CheckBoxTreeCell.forTreeView());

        for (ExperimentBarrierSegment b : controller.getModel().getCommandModelRspec().getBarrierSegments()) {
            CheckBoxTreeItem<String> barrierItem = new CheckBoxTreeItem<>(b.getTag());
            rootItem.getChildren().add(barrierItem);
            barriersMap.put(barrierItem, b);
            for (FXRspecNode n : nodes) {
                CheckBoxTreeItem<String> cb = new CheckBoxTreeItem<>(n.getClientId());
                barrierItem.getChildren().add(cb);
                nodesMap.put(cb, n);
            }
            barrierItem.setExpanded(true);
        }

        tagTextField.setText("Command " + controller.getModel().nextCommandNumber());

        //initialize dialog
        dialog = new Dialog<>();
        dialog.initOwner(parent);
        dialog.setTitle("Add a command");
        dialog.setHeaderText("Add a command to the selected nodes");
        dialog.setGraphic(GlyphUtils.createDialogGlyph(FontAwesome.Glyph.PLUS, Color.GREEN));
        dialog.getDialogPane().setContent(UIUtils.wrapWithDecorationPane(this));

        ButtonType addCommandButtonType = new ButtonType("Add command", ButtonBar.ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().setAll(addCommandButtonType, ButtonType.CANCEL);

        dialog.resultProperty().addListener(observable -> {
            if (dialog.getResult() == addCommandButtonType) {
                onAddCommandAction();
            }
        });

        //initialize validation support
        ValidationSupport validationSupport = new ValidationSupport();
        validationSupport.registerValidator(tagTextField, Validator.createEmptyValidator("Tag is a required field!"));
        validationSupport.registerValidator(commandTextField, Validator.createEmptyValidator("Command is a required field!"));

        Node addCommandButton = dialog.getDialogPane().lookupButton(addCommandButtonType);

        BooleanBinding atLeastOneItemSelected = rootItem.selectedProperty().or(rootItem.indeterminateProperty());

        addCommandButton.disableProperty().bind(validationSupport.invalidProperty().or(atLeastOneItemSelected.not()));
    }

    private void onAddCommandAction() {
        //noinspection RedundantCast
        nodesMap.keySet().stream()
                .filter(CheckBoxTreeItem::isSelected)
                .forEach(cbti -> {
                    //noinspection RedundantCast
                    controller.addCommand(tagTextField.getText(), nodesMap.get(cbti).getUniqueId(),
                            barriersMap.get((CheckBoxTreeItem<String>) cbti.getParent()).getOrderNumber()
                            , new Timestamp(startTextField.toMillis())
                            , new Timestamp(0)
                            , commandTextField.getText());
                });
    }

    private AddMultipleCommandDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes, ExperimentCommand c) {
        this(parent, controller, nodes);

        tagTextField.setText(c.getTag());
        startTextField.setTime(c.getStartingTime().toString());
        commandTextField.setText(c.getCommand());

        dialog.setTitle("Duplicate command");
    }

    private AddMultipleCommandDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes, FXRspecNode n, ExperimentBarrierSegment b) {
        this(parent, controller, nodes);

        if (b != null) {
            barriersMap.keySet().stream()
                    .filter(cb -> Objects.equals(barriersMap.get(cb), b))
                    .forEach(cb -> {
                        //noinspection SuspiciousMethodCalls
                        cb.getChildren().stream()
                                .filter(nodeCB -> Objects.equals(nodesMap.get(nodeCB), n))
                                .forEach(nodeCB -> ((CheckBoxTreeItem<String>) nodeCB).setSelected(true));
                    });
        }

    }

    private AddMultipleCommandDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes, FXRspecNode n, ExperimentBarrierSegment b, Timestamp t) {
        this(parent, controller, nodes, n, b);

        startTextField.setText(t.toString());
    }


    public void showDialog() {
        dialog.show();
    }

    public static void showSimpleAddDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes) {
        AddMultipleCommandDialog npd = new AddMultipleCommandDialog(parent, controller, nodes);
        npd.showDialog();

    }

    public static void showSimpleAddDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes, ExperimentCommand c) {
        AddMultipleCommandDialog npd = new AddMultipleCommandDialog(parent, controller, nodes, c);
        npd.showDialog();
    }

    public static void showSimpleAddDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes, FXRspecNode n, ExperimentBarrierSegment b) {
        AddMultipleCommandDialog npd = new AddMultipleCommandDialog(parent, controller, nodes, n, b);
        npd.showDialog();
    }

    public static void showSimpleAddDialog(Window parent, TimelineController controller, List<FXRspecNode> nodes, FXRspecNode n, ExperimentBarrierSegment b, Timestamp t) {
        AddMultipleCommandDialog npd = new AddMultipleCommandDialog(parent, controller, nodes, n, b, t);

        npd.showDialog();
    }
}
