/**
 * Copyright (c) 2013, 2015 ControlsFX
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *     * Neither the name of ControlsFX, any associated website, nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.controlsfx.samples.dialogs;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextInputDialog;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;

import org.controlsfx.ControlsFXSample;
import org.controlsfx.dialog.CommandLinksDialog;
import org.controlsfx.dialog.CommandLinksDialog.CommandLinksButtonType;
import org.controlsfx.dialog.WizardPane;
import org.controlsfx.dialog.ExceptionDialog;
import org.controlsfx.dialog.FontSelectorDialog;
import org.controlsfx.dialog.LoginDialog;
import org.controlsfx.dialog.ProgressDialog;
import org.controlsfx.dialog.Wizard;
import org.controlsfx.dialog.Wizard.LinearFlow;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;

public class HelloDialogs extends ControlsFXSample {
    
	@Override
	public String getSampleName() {
		return "Dialogs";
	}

	@Override
	public String getJavaDocURL() {
//		return Utils.JAVADOC_BASE + "org/controlsfx/dialog/Dialogs.html";
	    return null;
	}

	@Override
	public String getSampleDescription() {
		return "";
	}

	private final ComboBox<StageStyle> styleCombobox = new ComboBox<>();
    private final ComboBox<Modality> modalityCombobox = new ComboBox<>();
    private final CheckBox cbUseBlocking = new CheckBox();
    private final CheckBox cbCloseDialogAutomatically = new CheckBox();
    private final CheckBox cbShowMasthead = new CheckBox();
    private final CheckBox cbSetOwner = new CheckBox();
    private final CheckBox cbCustomGraphic = new CheckBox();
    
    private Stage stage;

    @Override
    public Node getPanel(Stage stage) {
        this.stage = stage;

        GridPane grid = new GridPane();
        grid.setPadding(new Insets(10, 10, 10, 10));
        grid.setHgap(10);
        grid.setVgap(10);

        int row = 0;
        
        Label javafxDialogs = new Label("JavaFX Dialogs:");
        javafxDialogs.setFont(Font.font(25));
        grid.add(javafxDialogs, 0, row++, 2, 1);

        // *******************************************************************
        // Information Dialog
        // *******************************************************************

        grid.add(createLabel("Information Dialog: "), 0, row);

        final Button Hyperlink2 = new Button("Show");
        Hyperlink2.setOnAction( (ActionEvent e) -> {
        		
                Alert dlg = createAlert(AlertType.INFORMATION);
                dlg.setTitle("Custom title");
                String optionalMasthead = "Wouldn't this be nice?";
                dlg.getDialogPane().setContentText("A collection of pre-built JavaFX dialogs?\nSeems like a great idea to me...");
                configureSampleDialog(dlg, optionalMasthead);
                
                // lets get some output when events happen
                dlg.setOnShowing(evt -> System.out.println(evt));
                dlg.setOnShown(evt -> System.out.println(evt));
                dlg.setOnHiding(evt -> System.out.println(evt));
                dlg.setOnHidden(evt -> System.out.println(evt));
                
//              dlg.setOnCloseRequest(evt -> evt.consume());
                
                showDialog(dlg);
        });

        final Button Hyperlink2a = new Button("2 x Buttons (no cancel)");
        Hyperlink2a.setOnAction( (ActionEvent e) -> {
                Alert dlg = createAlert(AlertType.INFORMATION);
                dlg.setTitle("Custom title");
                String optionalMasthead = "Wouldn't this be nice?";
                dlg.getDialogPane().setContentText("A collection of pre-built JavaFX dialogs?\nSeems like a great idea to me...");
                configureSampleDialog(dlg, optionalMasthead);
                dlg.getButtonTypes().add(ButtonType.NEXT);

//              dlg.setOnCloseRequest(evt -> evt.consume());

                showDialog(dlg);
            
        });

        grid.add(new HBox(10, Hyperlink2, Hyperlink2a), 1, row);

        row++;

        // *******************************************************************
        // Confirmation Dialog
        // *******************************************************************

        grid.add(createLabel("Confirmation Dialog: "), 0, row);

        final CheckBox cbShowCancel = new CheckBox("Show Cancel Button");
        cbShowCancel.setSelected(true);

        final Button Hyperlink3 = new Button("Show");
        Hyperlink3.setOnAction(e -> {
            Alert dlg = createAlert(AlertType.CONFIRMATION);
            dlg.setTitle("You do want dialogs right?");
            String optionalMasthead = "Just Checkin'";
            dlg.getDialogPane().setContentText("I was a bit worried that you might not want them, so I wanted to double check.");

            if (!cbShowCancel.isSelected()) {
                dlg.getDialogPane().getButtonTypes().remove(ButtonType.CANCEL);
            }

            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });
        grid.add(new HBox(10, Hyperlink3, cbShowCancel), 1, row);

        row++;

        // *******************************************************************
        // Warning Dialog
        // *******************************************************************

        grid.add(createLabel("Warning Dialog: "), 0, row);

        final Button Hyperlink6a = new Button("Show");
        Hyperlink6a.setOnAction(e -> {
            Alert dlg = createAlert(AlertType.WARNING);
            dlg.setTitle("I'm warning you!");
            String optionalMasthead = "This is a warning";
            dlg.getDialogPane().setContentText("I'm glad I didn't need to use this...");
            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });
        grid.add(new HBox(10, Hyperlink6a), 1, row);

        row++;

        // *******************************************************************
        // Error Dialog
        // *******************************************************************

        grid.add(createLabel("Error Dialog: "), 0, row);

        final Button Hyperlink7a = new Button("Show");
        Hyperlink7a.setOnAction(e -> {
            Alert dlg = createAlert(AlertType.ERROR);
            dlg.setTitle("It looks like you're making a bad decision");
            String optionalMasthead = "Exception Encountered";
            dlg.getDialogPane().setContentText("Better change your mind - this is really your last chance! (Even longer text that should probably wrap)");
            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });
        grid.add(new HBox(10, Hyperlink7a), 1, row);

        row++;


        // *******************************************************************
        // Input Dialog (with header)
        // *******************************************************************

        grid.add(createLabel("Input Dialog: "), 0, row);

        final Button Hyperlink8 = new Button("TextField");
        Hyperlink8.setOnAction(e -> {
            TextInputDialog dlg = new TextInputDialog("");
            dlg.setTitle("Name Check");
            String optionalMasthead = "Please type in your name";
            dlg.getDialogPane().setContentText("What is your name?");
            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });

        final Button Hyperlink9 = new Button("Initial Value Set");
        Hyperlink9.setOnAction(e -> {
            TextInputDialog dlg = new TextInputDialog("Jonathan");
            dlg.setTitle("Name Guess");
            String optionalMasthead = "Name Guess";
            dlg.getDialogPane().setContentText("Pick a name?");
            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });

        final Button Hyperlink10 = new Button("Set Choices (< 10)");
        Hyperlink10.setOnAction(e -> {
            ChoiceDialog<String> dlg = new ChoiceDialog<>("Jonathan",
                                                          "Matthew", "Jonathan", "Ian", "Sue", "Hannah");
            dlg.setTitle("Name Guess");
            String optionalMasthead = "Name Guess";
            dlg.getDialogPane().setContentText("Pick a name?");
            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });

        final Button Hyperlink11 = new Button("Set Choices (>= 10)");
        Hyperlink11.setOnAction(e -> {
            ChoiceDialog<String> dlg = new ChoiceDialog<>("Jonathan",
                                                          "Matthew", "Jonathan", "Ian", "Sue",
                                                          "Hannah", "Julia", "Denise", "Stephan",
                                                          "Sarah", "Ron", "Ingrid");
            dlg.setTitle("Name Guess");
            String optionalMasthead = "Name Guess";
            dlg.getDialogPane().setContentText("Pick a name?");
            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });

        grid.add(new HBox(10, Hyperlink8, Hyperlink9, Hyperlink10, Hyperlink11), 1, row);
        row++;
        
        
        
        
        
        
        // ---------  ControlsFX-specific Dialogs
        
        Label controlsfxDialogs = new Label("ControlsFX Dialogs:");
        controlsfxDialogs.setFont(Font.font(25));
        grid.add(controlsfxDialogs, 0, row++, 2, 1);
        

        // *******************************************************************
        // Command links
        // *******************************************************************

        grid.add(createLabel("Pre-built dialogs: "), 0, row);
        final Button Hyperlink12 = new Button("Command Links");
        Hyperlink12.setOnAction(e -> {
            List<CommandLinksButtonType> links = Arrays
                    .asList(new CommandLinksButtonType(
                            "Add a network that is in the range of this computer",
                            "This shows you a list of networks that are currently available and lets you connect to one.", false),
                            new CommandLinksButtonType(
                            "Manually create a network profile",
                            "This creates a new network profile or locates an existing one and saves it on your computer",
                             true /*default*/),
                             new CommandLinksButtonType("Create an ad hoc network",
                            "This creates a temporary network for sharing files or and Internet connection", false));

            CommandLinksDialog dlg = new CommandLinksDialog(links);
            dlg.setTitle("Manually connect to wireless network");
            String optionalMasthead = "Manually connect to wireless network";
            dlg.getDialogPane().setContentText("How do you want to add a network?");
            configureSampleDialog(dlg, optionalMasthead);
            showDialog(dlg);
        });

        final Button Hyperlink12a = new Button("Font Selector");
        Hyperlink12a.setOnAction(e -> {
            FontSelectorDialog dlg = new FontSelectorDialog(null);
            configureSampleDialog(dlg, "Please select a font!");
            showDialog(dlg);
        });
        
        final Button Hyperlink12b = new Button("Progress");
        Hyperlink12b.setOnAction((ActionEvent e) -> {
                Task<Object> worker = new Task<Object>() {
                    @Override
                    protected Object call() throws Exception {
                        for (int i = 0; i <= 100; i++) {
                            updateProgress(i, 99);
                            updateMessage("progress: " + i);
                            System.out.println("progress: " + i);
                            Thread.sleep(100);
                        }
                        return null;
                    }
                };

                ProgressDialog dlg = new ProgressDialog(worker);
                configureSampleDialog(dlg, "");

                Thread th = new Thread(worker);
                th.setDaemon(true);
                th.start();
        });
        
        final Button Hyperlink12c = new Button("Login");
        Hyperlink12c.setOnAction((ActionEvent e) -> {
                LoginDialog dlg = new LoginDialog(null, null);
                configureSampleDialog(dlg, "");
                showDialog(dlg);
        });
        
        final Button Hyperlink12d = new Button("Exception");
        Hyperlink12d.setOnAction((ActionEvent e) -> {
                ExceptionDialog dlg = new ExceptionDialog(new Exception("ControlsFX is _too_ awesome!"));
                configureSampleDialog(dlg, "");
                showDialog(dlg);
        });

        grid.add(new HBox(10, Hyperlink12, Hyperlink12a, Hyperlink12b, Hyperlink12c, Hyperlink12d), 1, row);
        row++;

        
        // *******************************************************************
        // wizards
        // *******************************************************************

        grid.add(createLabel("Wizard: "), 0, row);
        final Button Hyperlink15a = new Button("Linear Wizard");
        Hyperlink15a.setOnAction(e -> showLinearWizard());
        
        final Button Hyperlink15b = new Button("Branching Wizard");
        Hyperlink15b.setOnAction(e -> showBranchingWizard());
        
        final Button Hyperlink15c = new Button("Validated Linear Wizard");
        Hyperlink15c.setOnAction(e -> showValidatedLinearWizard());
        
        grid.add(new HBox(10, Hyperlink15a, Hyperlink15b, Hyperlink15c), 1, row++);
        
        return grid;
    }

    private Alert createAlert(AlertType type) {
        Window owner = cbSetOwner.isSelected() ? stage : null;
        Alert dlg = new Alert(type, "");
        dlg.initModality(modalityCombobox.getValue());
        dlg.initOwner(owner);
        return dlg;
    }
    
    private void configureSampleDialog(Dialog<?> dlg, String header) {
        Window owner = cbSetOwner.isSelected() ? stage : null;
        if (header != null && cbShowMasthead.isSelected()) {
            dlg.getDialogPane().setHeaderText(header);
        }

        if (cbCustomGraphic.isSelected()) {
            dlg.getDialogPane().setGraphic(new ImageView(new Image(getClass().getResource("/org/controlsfx/samples/controlsfx-logo.png").toExternalForm())));
        }
        
        dlg.initStyle(styleCombobox.getValue());
        dlg.initOwner(owner);
    }
    
    private void showDialog(Dialog<?> dlg) {
        Window owner = cbSetOwner.isSelected() ? stage : null;
        if (cbCloseDialogAutomatically.isSelected()) {
            new Thread(() -> {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Attempting to close dialog now...");
                Platform.runLater(() -> dlg.close());
            }).start();
        }
        dlg.initOwner(owner);

        if (cbUseBlocking.isSelected()) {
            dlg.showAndWait().ifPresent(result -> System.out.println("Result is " + result));
        } else {
            dlg.show();
            dlg.resultProperty().addListener(o -> System.out.println("Result is: " + dlg.getResult()));
            System.out.println("This println is _after_ the show method - we're non-blocking!");
        }
    }

    @Override public Node getControlPanel() {
        GridPane grid = new GridPane();
        grid.setVgap(10);
        grid.setHgap(10);
        grid.setPadding(new Insets(30, 30, 0, 30));

        int row = 0;

        // stage style
        grid.add(createLabel("Style: ", "property"), 0, row);
        styleCombobox.getItems().setAll(StageStyle.values());
        styleCombobox.setValue(styleCombobox.getItems().get(0));
        grid.add(styleCombobox, 1, row);
        row++;

        // modality
        grid.add(createLabel("Modality: ", "property"), 0, row);
        modalityCombobox.getItems().setAll(Modality.values());
        modalityCombobox.setValue(modalityCombobox.getItems().get(Modality.values().length-1));
        grid.add(modalityCombobox, 1, row);
        row++;
        
        // use blocking
        cbUseBlocking.setSelected(true);
        grid.add(createLabel("Use blocking: ", "property"), 0, row);
        grid.add(cbUseBlocking, 1, row);
        row++;

        // close dialog automatically
        grid.add(createLabel("Close dialog after 2000ms: ", "property"), 0, row);
        grid.add(cbCloseDialogAutomatically, 1, row);
        row++;

        // show header
        grid.add(createLabel("Show custom header text: ", "property"), 0, row);
        grid.add(cbShowMasthead, 1, row);
        row++;

        // set owner
        grid.add(createLabel("Set dialog owner: ", "property"), 0, row);
        grid.add(cbSetOwner, 1, row);
        row++;
        
        // custom graphic
        grid.add(createLabel("Use custom graphic: ", "property"), 0, row);
        grid.add(cbCustomGraphic, 1, row);
        row++;

        return grid;
    }
    
//    private CommandLinksButtonType buildCommandLink( String text, String comment, boolean isDefault ) {
//            return new CommandLinksButtonType(text, comment, isDefault);
//    }


    public static void main(String[] args) {
        Application.launch(args);
    }

    private Node createLabel(String text, String... styleclass) {
        Label label = new Label(text);

        if (styleclass == null || styleclass.length == 0) {
            label.setFont(Font.font(13));
        } else {
            label.getStyleClass().addAll(styleclass);
        }
        return label;
    }

    private void showLinearWizard() {
        Window owner = cbSetOwner.isSelected() ? stage : null;
        // define pages to show
        Wizard wizard = new Wizard(owner);
        wizard.setTitle("Linear Wizard");
        
        // --- page 1
        int row = 0;

        GridPane page1Grid = new GridPane();
        page1Grid.setVgap(10);
        page1Grid.setHgap(10);

        page1Grid.add(new Label("First Name:"), 0, row);
        TextField txFirstName = createTextField("firstName");
//        wizard.getValidationSupport().registerValidator(txFirstName, Validator.createEmptyValidator("First Name is mandatory"));  
        page1Grid.add(txFirstName, 1, row++);

        page1Grid.add(new Label("Last Name:"), 0, row);
        TextField txLastName = createTextField("lastName");
//        wizard.getValidationSupport().registerValidator(txLastName, Validator.createEmptyValidator("Last Name is mandatory"));
        page1Grid.add(txLastName, 1, row);

        WizardPane page1 = new WizardPane();
        page1.setHeaderText("Please Enter Your Details");
        page1.setContent(page1Grid);


        // --- page 2
        final WizardPane page2 = new WizardPane() {
            @Override public void onEnteringPage(Wizard wizard) {
                String firstName = (String) wizard.getSettings().get("firstName");
                String lastName = (String) wizard.getSettings().get("lastName");

                setContentText("Welcome, " + firstName + " " + lastName + "! Let's add some newlines!\n\n\n\n\n\n\nHello World!");
            }
        };
        page2.setHeaderText("Thanks For Your Details!");


        // --- page 3
        WizardPane page3 = new WizardPane();
        page3.setHeaderText("Goodbye!");
        page3.setContentText("Page 3, with extra 'help' button!");
        
        ButtonType helpDialogButton = new ButtonType("Help", ButtonData.HELP_2);
        page3.getButtonTypes().add(helpDialogButton);
        Button helpButton = (Button) page3.lookupButton(helpDialogButton);
        helpButton.addEventFilter(ActionEvent.ACTION, actionEvent -> {
            actionEvent.consume(); // stop hello.dialog from closing
            System.out.println("Help clicked!");
        });
                
                

        // create wizard
        wizard.setFlow(new LinearFlow(page1, page2, page3));
        
        System.out.println("page1: " + page1);
        System.out.println("page2: " + page2);
        System.out.println("page3: " + page3);

        // show wizard and wait for response
        wizard.showAndWait().ifPresent(result -> {
            if (result == ButtonType.FINISH) {
                System.out.println("Wizard finished, settings: " + wizard.getSettings());
            }
        });
    }
    
    private void showBranchingWizard() {
        Window owner = cbSetOwner.isSelected() ? stage : null;
        // define pages to show.
        // Because page1 references page2, we need to declare page2 first.
        final WizardPane page2 = new WizardPane();
        page2.setContentText("Page 2");

        final CheckBox checkBox = new CheckBox("Skip the second page");
        checkBox.setId("skip-page-2");
        VBox vbox = new VBox(10, new Label("Page 1"), checkBox);
        final WizardPane page1 = new WizardPane() {
            // when we exit page 1, we will check the state of the 'skip page 2'
            // checkbox, and if it is true, we will remove page 2 from the pages list
            @Override public void onExitingPage(Wizard wizard) {
//                List<WizardPage> pages = wizard.getPages();
//                if (checkBox.isSelected()) {
//                    pages.remove(page2);
//                } else {
//                    if (! pages.contains(page2)) {
//                        pages.add(1, page2);
//                    }
//                }
            }
        };
        page1.setContent(vbox);

        final WizardPane page3 = new WizardPane();
        page3.setContentText("Page 3");

        // create wizard
        Wizard wizard = new Wizard(owner);
        wizard.setTitle("Branching Wizard");
        Wizard.Flow branchingFlow = new Wizard.Flow() {

            @Override
            public Optional<WizardPane> advance(WizardPane currentPage) {
                return Optional.of(getNext(currentPage));
            }

            @Override
            public boolean canAdvance(WizardPane currentPage) {
                return currentPage != page3;
            }
            
            private WizardPane getNext(WizardPane currentPage) {
                if ( currentPage == null ) {
                    return page1;
                } else if ( currentPage == page1) {
                    return checkBox.isSelected()? page3: page2;
                } else {
                    return page3;
                }
            }
            
        };
        
        //wizard.setFlow( new LinearWizardFlow( page1, page2, page3));
        wizard.setFlow( branchingFlow);

        // show wizard
        wizard.showAndWait().ifPresent(result -> {
            if (result == ButtonType.FINISH) {
                System.out.println("Wizard finished, settings: " + wizard.getSettings());
            }
        });
    }
    
    private void showValidatedLinearWizard() {
        Window owner = cbSetOwner.isSelected() ? stage : null;
        Wizard wizard = new Wizard(owner);
        wizard.setTitle("Validated Linear Wizard");
        
        // Page 1
        WizardPane page1 = new WizardPane() {
            ValidationSupport vs = new ValidationSupport();
            {
                vs.initInitialDecoration();
                
                int row = 0;

                GridPane page1Grid = new GridPane();
                page1Grid.setVgap(10);
                page1Grid.setHgap(10);

                page1Grid.add(new Label("Username:"), 0, row);
                TextField txUsername = createTextField("username");
                vs.registerValidator(txUsername, Validator.createEmptyValidator("EMPTY!"));
                page1Grid.add(txUsername, 1, row++);

                page1Grid.add(new Label("Full Name:"), 0, row);
                TextField txFullName = createTextField("fullName");
                page1Grid.add(txFullName, 1, row);
                
                setContent(page1Grid);
            }
            
            @Override
            public void onEnteringPage(Wizard wizard) {
                wizard.invalidProperty().unbind();
                wizard.invalidProperty().bind(vs.invalidProperty());
            }
        };
        
        // Page 2

        WizardPane page2 = new WizardPane() {
            ValidationSupport vs = new ValidationSupport();
            {
                vs.initInitialDecoration();
                
                int row = 0;

                GridPane page2Grid = new GridPane();
                page2Grid.setVgap(10);
                page2Grid.setHgap(10);

                page2Grid.add(new Label("ControlsFX is:"), 0, row);
                ComboBox<String> cbControlsFX = createComboBox("controlsfx");
                cbControlsFX.setItems(FXCollections.observableArrayList("Cool", "Great"));
                vs.registerValidator(cbControlsFX, Validator.createEmptyValidator("EMPTY!"));
                page2Grid.add(cbControlsFX, 1, row++);

                page2Grid.add(new Label("Where have you heard of it?:"), 0, row);
                TextField txWhere = createTextField("where");
                vs.registerValidator(txWhere, Validator.createEmptyValidator("EMPTY!"));
                page2Grid.add(txWhere, 1, row++);

                page2Grid.add(new Label("Free text:"), 0, row);
                TextField txFreeText = createTextField("freetext");
                page2Grid.add(txFreeText, 1, row);
                
                setContent(page2Grid);
            }
            
            @Override
            public void onEnteringPage(Wizard wizard) {
                wizard.invalidProperty().unbind();
                wizard.invalidProperty().bind(vs.invalidProperty());
            }
        };

        // create wizard
        wizard.setFlow(new LinearFlow(page1, page2));

        // show wizard and wait for response
        wizard.showAndWait().ifPresent(result -> {
            if (result == ButtonType.FINISH) {
                System.out.println("Wizard finished, settings: " + wizard.getSettings());
            }
        });
    }
    
    private TextField createTextField(String id) {
        TextField textField = new TextField();
        textField.setId(id);
        GridPane.setHgrow(textField, Priority.ALWAYS);
        return textField;
    }
    
    private ComboBox<String> createComboBox(String id) {
        ComboBox<String> comboBox = new ComboBox<>();
        comboBox.setId(id);
        GridPane.setHgrow(comboBox, Priority.ALWAYS);
        return comboBox;
    }

}