<commit_before>package rmblworx.tools.timey.gui;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.ResourceBundle;
import java.util.TimeZone;

import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import rmblworx.tools.timey.SimpleCountdown;
import rmblworx.tools.timey.vo.TimeDescriptor;

public class CountdownController {

	// private TimeyFacade facade = new TimeyFacade();
	private SimpleCountdown countdown = new SimpleCountdown();

	private SimpleDateFormat dateFormatter;
	private SimpleDateFormat hoursFormatter;
	private SimpleDateFormat minutesFormatter;
	private SimpleDateFormat secondsFormatter;

	private static final long MAX_HOURS = 23L;
	private static final long MAX_MINUTES = 59L;
	private static final long MAX_SECONDS = 59L;

	@FXML
	private ResourceBundle resources;

	@FXML
	private Button countdownStartButton;

	@FXML
	private Button countdownStopButton;

	@FXML
	private TextField countdownHoursField;

	@FXML
	private TextField countdownMinutesField;

	@FXML
	private TextField countdownSecondsField;

	@FXML
	private Slider countdownHoursSlider;

	@FXML
	private Slider countdownMinutesSlider;

	@FXML
	private Slider countdownSecondsSlider;

	@FXML
	private Label countdownTimeLabel;

	@FXML
	private AnchorPane countdownTimeInputPane;

	private boolean countdownRunning = false;
	private long countdownValue;

	@FXML
	void initialize() {
		assert countdownHoursField != null : "fx:id='countdownHoursField' was not injected";
		assert countdownMinutesField != null : "fx:id='countdownMinuteField' was not injected";
		assert countdownSecondsField != null : "fx:id='countdownSecondsField' was not injected";
		assert countdownHoursSlider != null : "fx:id='countdownHoursSlider' was not injected";
		assert countdownMinutesSlider != null : "fx:id='countdownMinutesSlider' was not injected";
		assert countdownSecondsSlider != null : "fx:id='countdownSecondsSlider' was not injected";
		assert countdownStartButton != null : "fx:id='countdownStartButton' was not injected";
		assert countdownStopButton != null : "fx:id='countdownStopButton' was not injected";
		assert countdownTimeLabel != null : "fx:id='countdownTimeLabel' was not injected";
		assert countdownTimeInputPane != null : "fx:id='countdownTimeInputPane' was not injected";

		if (countdownStartButton != null) {
			countdownStartButton.setOnAction(new EventHandler<ActionEvent>() {
				public void handle(final ActionEvent event) {
					if (countdownRunning) {
						return;
					}

					final TimeDescriptor td = new TimeDescriptor(getTimeFromInputFields());
					startCountdown(td);

					final Task<Void> task = new Task<Void>() {
						private static final long SLEEP_TIME_COARSE_GRAINED = 1000L;

						public Void call() throws InterruptedException {
							while (countdownRunning) {
								countdownValue = td.getMilliSeconds();
								updateMessage(dateFormatter.format(countdownValue));
								Thread.sleep(SLEEP_TIME_COARSE_GRAINED);
							}

							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);
						}
					});

					final Thread thread = new Thread(task);
					thread.setDaemon(true);
					thread.setPriority(Thread.MIN_PRIORITY);
					thread.start();
				}
			});
		}

		if (countdownStopButton != null) {
			countdownStopButton.setOnAction(new EventHandler<ActionEvent>() {
				public void handle(final ActionEvent event) {
					if (!countdownRunning) {
						return;
					}

					stopCountdown();
				}
			});
		}

		if (countdownHoursField != null) {
			bindTextInputListenersAndSlider(countdownHoursField, countdownHoursSlider, MAX_HOURS);
		}

		if (countdownMinutesField != null) {
			bindTextInputListenersAndSlider(countdownMinutesField, countdownMinutesSlider, MAX_MINUTES);
		}

		if (countdownSecondsField != null) {
			bindTextInputListenersAndSlider(countdownSecondsField, countdownSecondsSlider, MAX_SECONDS);
		}

		setupDateFormatters();
	}

	protected void bindTextInputListenersAndSlider(final TextField textField, final Slider slider, final long maxValue) {
		final StringProperty textProperty = textField.textProperty();

		// Eingaben auf Zahlen beschränken
		textField.addEventFilter(KeyEvent.KEY_TYPED, new AllowOnlyNumericKeysKeyEventHandler());

		// Inhalt auf gültigen Wertebereich beschränken
		textProperty.addListener(new CountdownTimePartChangeListener(textProperty, maxValue));

		// bei Verlassen des Feldes sicherstellen, dass Wert zweistellig
		textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
			public void changed(final ObservableValue<? extends Boolean> property, final Boolean oldValue, final Boolean newValue) {
				if (Boolean.FALSE.equals(newValue)) {
					textProperty.setValue(String.format("%02d", Long.parseLong(textProperty.get())));
				}
			}
		});

		// Textfeld mit Slider koppeln
		if (slider != null) {
			slider.setMax(maxValue);
			textProperty.bindBidirectional(slider.valueProperty(), new CountdownTimePartConverter());
		}
	}

	protected void startCountdown(final TimeDescriptor td) {
		countdownRunning = true;
		countdownStartButton.setVisible(false);
		countdownStopButton.setVisible(true);
		countdownStopButton.requestFocus();
		enableTimeInput(false);

		transferTimeFromInputToLabel();

		countdown.setCountdownTime(td);<commit_msg>[GUI] Start-Schaltfläche für Countdown nur aktivieren, wenn Zeit > 0<commit_after>package rmblworx.tools.timey.gui;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.ResourceBundle;
import java.util.TimeZone;

import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import rmblworx.tools.timey.SimpleCountdown;
import rmblworx.tools.timey.vo.TimeDescriptor;

public class CountdownController {

	// private TimeyFacade facade = new TimeyFacade();
	private SimpleCountdown countdown = new SimpleCountdown();

	private SimpleDateFormat dateFormatter;
	private SimpleDateFormat hoursFormatter;
	private SimpleDateFormat minutesFormatter;
	private SimpleDateFormat secondsFormatter;

	private static final long MAX_HOURS = 23L;
	private static final long MAX_MINUTES = 59L;
	private static final long MAX_SECONDS = 59L;

	@FXML
	private ResourceBundle resources;

	@FXML
	private Button countdownStartButton;

	@FXML
	private Button countdownStopButton;

	@FXML
	private TextField countdownHoursField;

	@FXML
	private TextField countdownMinutesField;

	@FXML
	private TextField countdownSecondsField;

	@FXML
	private Slider countdownHoursSlider;

	@FXML
	private Slider countdownMinutesSlider;

	@FXML
	private Slider countdownSecondsSlider;

	@FXML
	private Label countdownTimeLabel;

	@FXML
	private AnchorPane countdownTimeInputPane;

	private boolean countdownRunning = false;
	private long countdownValue;

	@FXML
	void initialize() {
		assert countdownHoursField != null : "fx:id='countdownHoursField' was not injected";
		assert countdownMinutesField != null : "fx:id='countdownMinuteField' was not injected";
		assert countdownSecondsField != null : "fx:id='countdownSecondsField' was not injected";
		assert countdownHoursSlider != null : "fx:id='countdownHoursSlider' was not injected";
		assert countdownMinutesSlider != null : "fx:id='countdownMinutesSlider' was not injected";
		assert countdownSecondsSlider != null : "fx:id='countdownSecondsSlider' was not injected";
		assert countdownStartButton != null : "fx:id='countdownStartButton' was not injected";
		assert countdownStopButton != null : "fx:id='countdownStopButton' was not injected";
		assert countdownTimeLabel != null : "fx:id='countdownTimeLabel' was not injected";
		assert countdownTimeInputPane != null : "fx:id='countdownTimeInputPane' was not injected";

		if (countdownStartButton != null) {
			countdownStartButton.setOnAction(new EventHandler<ActionEvent>() {
				public void handle(final ActionEvent event) {
					if (countdownRunning) {
						return;
					}

					final TimeDescriptor td = new TimeDescriptor(getTimeFromInputFields());
					startCountdown(td);

					final Task<Void> task = new Task<Void>() {
						private static final long SLEEP_TIME_COARSE_GRAINED = 1000L;

						public Void call() throws InterruptedException {
							while (countdownRunning) {
								countdownValue = td.getMilliSeconds();
								updateMessage(dateFormatter.format(countdownValue));
								Thread.sleep(SLEEP_TIME_COARSE_GRAINED);
							}

							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);
						}
					});

					final Thread thread = new Thread(task);
					thread.setDaemon(true);
					thread.setPriority(Thread.MIN_PRIORITY);
					thread.start();
				}
			});
		}

		if (countdownStopButton != null) {
			countdownStopButton.setOnAction(new EventHandler<ActionEvent>() {
				public void handle(final ActionEvent event) {
					if (!countdownRunning) {
						return;
					}

					stopCountdown();
				}
			});
		}

		if (countdownHoursField != null) {
			bindTextInputListenersAndSlider(countdownHoursField, countdownHoursSlider, MAX_HOURS);
		}

		if (countdownMinutesField != null) {
			bindTextInputListenersAndSlider(countdownMinutesField, countdownMinutesSlider, MAX_MINUTES);
		}

		if (countdownSecondsField != null) {
			bindTextInputListenersAndSlider(countdownSecondsField, countdownSecondsSlider, MAX_SECONDS);
		}

		setupDateFormatters();
	}

	protected void bindTextInputListenersAndSlider(final TextField textField, final Slider slider, final long maxValue) {
		final StringProperty textProperty = textField.textProperty();

		// Eingaben auf Zahlen beschränken
		textField.addEventFilter(KeyEvent.KEY_TYPED, new AllowOnlyNumericKeysKeyEventHandler());

		// Inhalt auf gültigen Wertebereich beschränken
		textProperty.addListener(new CountdownTimePartChangeListener(textProperty, maxValue));

		// Start-Schaltfläche nur aktivieren, wenn Zeit > 0
		countdownStartButton.setDisable(true);
		textProperty.addListener(new ChangeListener<String>() {
			public void changed(final ObservableValue<? extends String> property, final String oldValue, final String newValue) {
				countdownStartButton.setDisable(getTimeFromInputFields() == 0);
			}
		});

		// bei Verlassen des Feldes sicherstellen, dass Wert zweistellig
		textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
			public void changed(final ObservableValue<? extends Boolean> property, final Boolean oldValue, final Boolean newValue) {
				if (Boolean.FALSE.equals(newValue)) {
					textProperty.setValue(String.format("%02d", Long.parseLong(textProperty.get())));
				}
			}
		});

		// Textfeld mit Slider koppeln
		if (slider != null) {
			slider.setMax(maxValue);
			textProperty.bindBidirectional(slider.valueProperty(), new CountdownTimePartConverter());
		}
	}

	protected void startCountdown(final TimeDescriptor td) {
		countdownRunning = true;
		countdownStartButton.setVisible(false);
		countdownStopButton.setVisible(true);
		countdownStopButton.requestFocus();
		enableTimeInput(false);

		transferTimeFromInputToLabel();

		countdown.setCountdownTime(td);
{'fraction_non_alphanumeric': 0.09061197810278618, 'fraction_numerical': 0.002206062586812648, 'mean_word_length': 5.116941529235382, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2652383', 'n_tokens_mistral': 4128, 'n_tokens_neox': 3587, 'n_words': 807}