package org.peerbox.share;
import java.net.URL;
import java.nio.file.Path;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
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.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import org.controlsfx.control.StatusBar;
import org.hive2hive.core.exceptions.NoPeerConnectionException;
import org.hive2hive.core.exceptions.NoSessionException;
import org.hive2hive.core.model.PermissionType;
import org.hive2hive.core.model.UserPermission;
import org.hive2hive.processframework.exceptions.InvalidProcessStateException;
import org.hive2hive.processframework.exceptions.ProcessExecutionException;
import org.peerbox.ResultStatus;
import org.peerbox.app.manager.ProcessHandle;
import org.peerbox.app.manager.file.FileInfo;
import org.peerbox.app.manager.file.IFileManager;
import org.peerbox.app.manager.file.messages.LocalShareFolderMessage;
import org.peerbox.app.manager.user.IUserManager;
import org.peerbox.events.MessageBus;
import org.peerbox.presenter.validation.UsernameRegisteredValidator;
import org.peerbox.presenter.validation.ValidationUtils.ValidationResult;
import org.peerbox.utils.DialogUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
/**
* Controller for the Share Folder View.
* Responsible for configuring sharing and starting process.
*
* @author albrecht
*
*/
public final class ShareFolderController implements Initializable {
private final static Logger logger = LoggerFactory.getLogger(ShareFolderController.class);
@FXML
private AnchorPane pane;
@FXML
private GridPane grdForm;
@FXML
private TextField txtFolderPath;
@FXML
private TextField txtUsername;
@FXML
private Label lblUsernameError;
@FXML
private StatusBar statusBar;
/* checks whether user exists in the network */
private UsernameRegisteredValidator usernameValidator;
private Path folderToShare;
private final StringProperty folderToShareProperty;
private final BooleanProperty busyProperty;
private final StringProperty statusProperty;
private final IFileManager fileManager;
private final IUserManager userManager;
private final MessageBus messageBus;
@Inject
public ShareFolderController(IFileManager fileManager, IUserManager userManager, MessageBus messageBus) {
this.statusProperty = new SimpleStringProperty();
this.busyProperty = new SimpleBooleanProperty(false);
this.folderToShareProperty = new SimpleStringProperty();
this.fileManager = fileManager;
this.userManager = userManager;
this.messageBus = messageBus;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
initializeValidations();
initializeStatusBar();
grdForm.disableProperty().bind(busyProperty);
txtFolderPath.textProperty().bind(folderToShareProperty);
Platform.runLater(() -> {
txtUsername.requestFocus();
});
}
private void initializeValidations() {
usernameValidator = new UsernameRegisteredValidator(
txtUsername, lblUsernameError.textProperty(), userManager);
}
private void uninstallValidationDecorations() {
usernameValidator.reset();
}
private void initializeStatusBar() {
statusBar = new StatusBar();
pane.getChildren().add(statusBar);
AnchorPane.setBottomAnchor(statusBar, 0.0);
AnchorPane.setLeftAnchor(statusBar, 0.0);
AnchorPane.setRightAnchor(statusBar, 0.0);
busyProperty.addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
Boolean newValue) {
if(newValue != null && newValue.booleanValue()) {
statusBar.setProgress(-1);
} else {
statusBar.setProgress(0);
}
}
});
// text in status bar
statusBar.textProperty().bind(statusProperty);
}
private void resetForm() {
uninstallValidationDecorations();
txtUsername.clear();
setBusy(false);
setStatus("");
}
@FXML
public void shareAction(ActionEvent event) {
boolean inputOk = validateAll();
if (inputOk) {
Task<ResultStatus> task = createShareTask();
new Thread(task).start();
}
}
private Task<ResultStatus> createShareTask() {
Task<ResultStatus> task = new Task<ResultStatus>() {
final String username = getUsername();
final Path toShare = getFolderToShare();
@Override
public ResultStatus call() {
return shareFolder(toShare, username);
}
};
task.setOnScheduled(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
setStatus("Sharing folder... Please wait.");
setBusy(true);
}
});
task.setOnFailed(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
onShareFailed(ResultStatus.error("Could not share folder."));
}
});
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
ResultStatus result = task.getValue();
if (result.isOk()) {
onShareSucceeded();
} else {
onShareFailed(result);
}
}
});
return task;
}
private ResultStatus shareFolder(Path toShare, String username) {
ProcessHandle<Void> handle = null;
try {
handle = fileManager.share(toShare, username, PermissionType.WRITE);
handle.execute();
return ResultStatus.ok();
} catch (NoSessionException e) {
logger.warn("Cannot share folder - no session.", e);
return ResultStatus.error("The user is not logged in (no session).");
} catch (NoPeerConnectionException e) {
logger.warn("Cannot share folder - no connection to the network.", e);
return ResultStatus.error("There is no connection to the network.");
} catch (IllegalArgumentException e) {
logger.warn("Cannot share folder - invalid parameters.", e);
return ResultStatus.error(String.format(
"Invalid parameters provided (%s).", e.getMessage()));
} catch (InvalidProcessStateException | ProcessExecutionException e) {
logger.warn("Cannot share folder.", e);
return ResultStatus.error(String.format(
"Sharing folder failed (%s)", e.getMessage()));
}
}
private boolean validateAll() {
return usernameValidator.validate(true) == ValidationResult.OK;
}
private void onShareSucceeded() {
Runnable succeeded = new Runnable() {
@Override
public void run() {
setStatus("Sharing succeeded.");
setBusy(false);
Alert dlg = DialogUtils.createAlert(AlertType.INFORMATION);
dlg.setTitle("Folder Sharing");
dlg.setHeaderText("Folder sharing finished");
dlg.setContentText("The user is granted access to the folder.");
dlg.showAndWait();
getStage().close();
}
};
if (Platform.isFxApplicationThread()) {
succeeded.run();
} else {
Platform.runLater(succeeded);
}
FileInfo file = new FileInfo(folderToShare, true);
UserPermission permission = new UserPermission(getUsername(), PermissionType.WRITE);
messageBus.publish(new LocalShareFolderMessage(file, permission));
}
private void onShareFailed(ResultStatus status) {
Runnable failed = new Runnable() {
@Override
public void run() {
setStatus("Sharing failed.");
setBusy(false);
Alert dlg = DialogUtils.createAlert(AlertType.ERROR);
dlg.setTitle("Folder Sharing");
dlg.setHeaderText("Folder sharing failed.");
dlg.setContentText(status.getErrorMessage());
dlg.showAndWait();
}
};
if (Platform.isFxApplicationThread()) {
failed.run();
} else {
Platform.runLater(failed);
}
}
@FXML
public void cancelAction(ActionEvent event) {
resetForm();
getStage().close();
}
private Stage getStage() {
return (Stage)pane.getScene().getWindow();
}
private String getUsername() {
return txtUsername.getText().trim();
}
private Path getFolderToShare() {
return folderToShare;
}
public void setFolderToShare(final Path path) {
folderToShare = path;
folderToShareProperty.set(path.toString());
}
public String getStatus() {
return statusProperty.get();
}
public void setStatus(String status) {
this.statusProperty.set(status);
}
public StringProperty statusProperty() {
return statusProperty;
}
public Boolean isBusy() {
return busyProperty.get();
}
public void setBusy(Boolean isBusy) {
this.busyProperty.set(isBusy);
}
public BooleanProperty busyProperty() {
return busyProperty;
}
}