package rmblworx.tools.timey.gui;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import rmblworx.tools.timey.ITimey;
import rmblworx.tools.timey.event.CountdownExpiredEvent;
import rmblworx.tools.timey.event.TimeyEvent;
import rmblworx.tools.timey.event.TimeyEventListener;
import rmblworx.tools.timey.gui.component.TimePicker;
import rmblworx.tools.timey.vo.TimeDescriptor;
/*
* Copyright 2014-2015 Christian Raue
* MIT License http://opensource.org/licenses/mit-license.php
*/
/**
* Controller für die Countdown-GUI.
* @author Christian Raue {@literal <christian.raue@gmail.com>}
*/
public class CountdownController extends Controller implements TimeyEventListener {
/**
* Formatiert Zeitstempel als Zeit-Werte.
*/
private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
@FXML
private ResourceBundle resources;
@FXML
private Button countdownStartButton;
@FXML
private Button countdownStopButton;
@FXML
private Label countdownTimeLabel;
@FXML
private TimePicker countdownTimePicker;
/**
* Ob der Countdown läuft.
*/
private boolean countdownRunning = false;
/**
* Countdown-Zeit in ms.
*/
private long countdownValue;
@FXML
private void initialize() {
// Start-Schaltfläche nur aktivieren, wenn Zeit > 0
countdownStartButton.setDisable(true);
countdownTimePicker.getTimeProperty().addListener(new ChangeListener<LocalTime>() {
public void changed(final ObservableValue<? extends LocalTime> property, final LocalTime oldValue, final LocalTime newValue) {
countdownStartButton.setDisable(newValue.toNanoOfDay() == 0L);
}
});
final TimeyEventListener eventListener = this;
Platform.runLater(new Runnable() {
public void run() {
getGuiHelper().getFacade().addEventListener(eventListener);
}
});
// Betätigen einer fokussierten Schaltfläche per Enter-Taste mit JavaFX 8 nicht mehr möglich
// countdownTimePicker.setOnKeyPressed(new EventHandler<KeyEvent>() {
// public void handle(final KeyEvent keyEvent) {
// if (keyEvent.getEventType() == KeyEvent.KEY_PRESSED && keyEvent.getCode() == KeyCode.ENTER) {
// keyEvent.consume();
// startCountdown();
// }
// }
// });
}
/**
* Aktion bei Klick auf Start-Schaltfläche.
*/
@FXML
private void handleStartButtonClick() {
startCountdown();
}
/**
* Aktion bei Klick auf Stop-Schaltfläche.
*/
@FXML
private void handleStopButtonClick() {
stopCountdown(false);
}
/**
* Startet den Countdown.
*/
private void startCountdown() {
if (countdownRunning) {
return;
}
final long nanos = countdownTimePicker.getValue().toNanoOfDay();
if (nanos == 0L) {
return;
}
countdownRunning = true;
final Task<Void> task = new Task<Void>() {
private static final long SLEEP_TIME = 100L;
public Void call() throws InterruptedException {
final TimeDescriptor timeDescriptor = new TimeDescriptor(nanos / DateTimeUtil.MILLI_TO_NANO);
final ITimey facade = getGuiHelper().getFacade();
facade.setCountdownTime(timeDescriptor);
facade.startCountdown();
Platform.runLater(new Runnable() {
public void run() {
countdownStartButton.setVisible(false);
countdownStopButton.setVisible(true);
countdownStopButton.requestFocus();
transferTimeFromInputToLabel();
enableTimeInput(false);
}
});
while (countdownRunning) {
countdownValue = timeDescriptor.getMilliSeconds();
updateMessage(timeFormatter.format(getCountdownTime()));
if (countdownValue == 0) {
break;
}
Thread.sleep(SLEEP_TIME);
}
return null;
}
};
task.messageProperty().addListener(new ChangeListener<String>() {
public void changed(final ObservableValue<? extends String> property, final String oldValue, final String newValue) {
countdownTimeLabel.setText(newValue);
}
});
getGuiHelper().runInThread(task, resources);
}
/**
* Stoppt den Countdown.
* @param countdownExpired Ob der Countdown abgelaufen ist.
*/
private void stopCountdown(final boolean countdownExpired) {
if (!countdownRunning) {
return;
}
countdownRunning = false;
getGuiHelper().runInThread(new Task<Void>() {
public Void call() throws InterruptedException {
if (countdownExpired) {
countdownValue = 0;
} else {
getGuiHelper().getFacade().stopCountdown();
}
Platform.runLater(new Runnable() {
public void run() {
countdownTimePicker.setValue(getCountdownTime());
countdownStartButton.setVisible(true);
countdownStartButton.requestFocus();
countdownStopButton.setVisible(false);
enableTimeInput(true);
}
});
return null;
}
}, resources);
}
/**
* Aktiviert bzw. deaktiviert alle nötigen Bedienelemente zur Eingabe einer Zeit.
* @param enabled ob Felder aktiv sein sollen
*/
private void enableTimeInput(final boolean enabled) {
countdownTimeLabel.setVisible(!enabled);
countdownTimePicker.setVisible(enabled);
countdownTimePicker.setDisable(!enabled);
}
/**
* @param time Zeit in ms
* @return Zeit in ms (aufgerundet auf ganze Sekunden)
*/
private long getMillisRoundedToWholeSeconds(final long time) {
final double milli = 1000D;
return new Double(Math.ceil(time / milli) * milli).longValue();
}
/**
* @return Countdown-Zeit (aufgerundet auf ganze Sekunden)
*/
private LocalTime getCountdownTime() {
return DateTimeUtil.getTimePart(DateTimeUtil.getLocalDateTimeFromMillis(getMillisRoundedToWholeSeconds(countdownValue)));
}
/**
* Überträgt die Zeit von den Textfeldern auf das Label.
*/
private void transferTimeFromInputToLabel() {
countdownTimeLabel.setText(timeFormatter.format(countdownTimePicker.getValue()));
}
/**
* {@inheritDoc}
*/
@Override
public final void handleEvent(final TimeyEvent event) {
if (event instanceof CountdownExpiredEvent) {
stopCountdown(true);
getGuiHelper().showTrayMessageWithFallbackToDialog(resources.getString("countdown.event.expired.title"),
resources.getString("countdown.event.expired.text"), resources);
}
}
}