package org.peerbox.presenter;
import java.io.IOException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import org.peerbox.ResultStatus;
import org.peerbox.app.config.BootstrappingNodes;
import org.peerbox.app.config.BootstrappingNodesFactory;
import org.peerbox.app.manager.node.INodeManager;
import org.peerbox.presenter.validation.EmptyTextFieldValidator;
import org.peerbox.presenter.validation.ValidationUtils.ValidationResult;
import org.peerbox.view.ViewNames;
import org.peerbox.view.controls.ErrorLabel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
public class JoinNetworkController implements Initializable {
private static final Logger logger = LoggerFactory.getLogger(JoinNetworkController.class);
private INodeManager nodeManager;
private NavigationService fNavigationService;
private BootstrappingNodes bootstrappingNodes;
private BootstrappingNodesFactory bootstrappingNodesFactory;
@FXML
private VBox vboxForm;
@FXML
private TextField txtBootstrapAddress;
@FXML
private ComboBox<String> cbBootstrapNodes;
@FXML
private ErrorLabel lblError;
@FXML
private Label lblBootstrapAddressError;
@FXML
private ProgressIndicator piProgress;
private EmptyTextFieldValidator bootstrapValidator;
@Inject
public JoinNetworkController(NavigationService navigationService, INodeManager nodeManager) {
this.fNavigationService = navigationService;
this.nodeManager = nodeManager;
}
public void initialize(URL arg0, ResourceBundle arg1) {
Platform.runLater(() -> {
txtBootstrapAddress.requestFocus();
});
initializeValidations();
loadBootstrapNodes();
}
private void loadBootstrapNodes() {
cbBootstrapNodes.getItems().clear();
try {
bootstrappingNodes = bootstrappingNodesFactory.create();
bootstrappingNodesFactory.load();
cbBootstrapNodes.getItems().addAll(bootstrappingNodes.getBootstrappingNodes());
txtBootstrapAddress.setText(bootstrappingNodes.getLastNode());
} catch (IOException ioex) {
logger.warn("Could not load bootstrapping nodes from configuration.", ioex);
setError("Could not load settings.");
}
}
private void initializeValidations() {
bootstrapValidator = new EmptyTextFieldValidator(txtBootstrapAddress, true, ValidationResult.BOOTSTRAPHOST_EMPTY);
bootstrapValidator.setErrorProperty(lblBootstrapAddressError.textProperty());
}
private void saveJoinConfig() {
try {
bootstrappingNodes.getBootstrappingNodes().add(getBootstrapNode());
bootstrappingNodes.setLastNode(getBootstrapNode());
bootstrappingNodesFactory.save();
} catch (IOException ioex) {
logger.warn("Could not save settings: {}", ioex.getMessage());
setError("Could not save settings.");
}
}
private void resetForm() {
loadBootstrapNodes();
txtBootstrapAddress.clear();
uninstallProgressIndicator();
uninstallValidationDecorations();
vboxForm.disableProperty().unbind();
vboxForm.setDisable(false);
}
private void uninstallValidationDecorations() {
bootstrapValidator.reset();
}
private void installProgressIndicator() {
Platform.runLater(() -> {
// center indicator with respect to the grid
double xOffset = piProgress.getWidth() / 2.0;
double yOffset = piProgress.getHeight() / 2.0;
double x = vboxForm.getWidth() / 2.0 - xOffset;
double y = vboxForm.getHeight() / 2.0 - yOffset;
piProgress.relocate(x, y);
// show
piProgress.setVisible(true);
});
}
private void uninstallProgressIndicator() {
Platform.runLater(() -> {
piProgress.setVisible(false);
});
}
@FXML
public void navigateBackAction(ActionEvent event) {
if (nodeManager.isConnected()) {
nodeManager.leaveNetwork();
logger.info("Disconnect from network.");
}
logger.debug("Navigate back.");
fNavigationService.navigateBack();
}
@FXML
public void onBootstrapNodeSelected(ActionEvent event) {
String selectedNode = cbBootstrapNodes.getSelectionModel().getSelectedItem();
txtBootstrapAddress.setText(selectedNode);
}
@FXML
public void joinNetworkAction(ActionEvent event) {
clearError();
boolean inputValid = !validateAll().isError();
if(inputValid) {
Task<ResultStatus> task = createJoinTask();
new Thread(task).start();
}
}
protected ResultStatus joinNetwork(final String address) {
logger.info("Join network '{}'", address);
try {
boolean res = nodeManager.joinNetwork(address);
if (res) {
return ResultStatus.ok();
} else {
return ResultStatus.error("Could not connect to the network.");
}
} catch (UnknownHostException ex) {
return ResultStatus.error("Could not determine address of host.");
}
}
protected void onJoinSucceeded() {
logger.info("Join task succeeded: network {} joined.", getBootstrapNode());
saveJoinConfig();
resetForm();
// if (!userConfig.hasRootPath()) {
// fNavigationService.navigate(ViewNames.SELECT_ROOT_PATH_VIEW);
// } else {
fNavigationService.navigate(ViewNames.LOGIN_VIEW);
// }
}
protected void onJoinFailed(ResultStatus result) {
logger.error("Join task failed: {}", result.getErrorMessage());
Platform.runLater(() -> {
uninstallProgressIndicator();
vboxForm.disableProperty().unbind();
vboxForm.requestLayout();
setError(result.getErrorMessage());
});
}
private Task<ResultStatus> createJoinTask() {
Task<ResultStatus> task = new Task<ResultStatus>() {
// bootstrap node
final String address = getBootstrapNode();
@Override
public ResultStatus call() {
return joinNetwork(address);
}
};
task.setOnScheduled(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
vboxForm.disableProperty().bind(task.runningProperty());
installProgressIndicator();
}
});
task.setOnFailed(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
onJoinFailed(ResultStatus.error("Could not join network."));
}
});
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
ResultStatus result = task.getValue();
if (result.isOk()) {
onJoinSucceeded();
} else {
onJoinFailed(result);
}
}
});
return task;
}
private ValidationResult validateAll() {
return (bootstrapValidator.validate() == ValidationResult.OK)
? ValidationResult.OK : ValidationResult.ERROR;
}
private void setError(final String error) {
if(Platform.isFxApplicationThread()) {
lblError.setText(error);
} else {
Platform.runLater(() -> {
setError(error); // run again on application thread
});
}
}
private void clearError() {
lblError.setText("");
}
private String getBootstrapNode() {
return txtBootstrapAddress.getText().trim();
}
@Inject
public void setBootstrappingNodesFactory(BootstrappingNodesFactory bootstrappingNodesFactory) {
this.bootstrappingNodesFactory = bootstrappingNodesFactory;
}
}