package com.faforever.client.chat;
import com.faforever.client.player.PlayerService;
import com.faforever.client.util.Assert;
import javafx.event.EventHandler;
import javafx.scene.control.TextInputControl;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Locale.US;
public class AutoCompletionHelper {
@Resource
PlayerService playerService;
private List<String> possibleAutoCompletions;
private int nextAutoCompleteIndex;
private String autoCompletePartialName;
private TextInputControl boundTextField;
private EventHandler<KeyEvent> keyEventHandler;
public AutoCompletionHelper() {
keyEventHandler = keyEvent -> {
if (!keyEvent.isControlDown() && keyEvent.getCode() == KeyCode.TAB) {
keyEvent.consume();
autoComplete();
}
};
}
private void autoComplete() {
if (boundTextField.getText().isEmpty()) {
return;
}
if (possibleAutoCompletions == null) {
initializeAutoCompletion();
if (possibleAutoCompletions.isEmpty()) {
// There are no auto completion matches
resetAutoCompletion();
return;
}
// It's the first auto complete event at this location, just replace the text with the first user name
boundTextField.selectPreviousWord();
boundTextField.replaceSelection(possibleAutoCompletions.get(nextAutoCompleteIndex++));
return;
}
// At this point, it's a subsequent auto complete event
String wordBeforeCaret = getWordBeforeCaret(boundTextField);
/*
* We have to check if the previous word is the one we auto completed. If not we reset and start all over again
* as the user started auto completion on another word.
*/
if (!wordBeforeCaret.equals(possibleAutoCompletions.get(nextAutoCompleteIndex - 1))) {
resetAutoCompletion();
autoComplete();
return;
}
if (possibleAutoCompletions.size() == 1) {
// No need to cycle since there was only one match
return;
}
if (possibleAutoCompletions.size() <= nextAutoCompleteIndex) {
// Start over again in order to cycle
nextAutoCompleteIndex = 0;
}
boundTextField.selectPreviousWord();
boundTextField.replaceSelection(possibleAutoCompletions.get(nextAutoCompleteIndex++));
}
private void initializeAutoCompletion() {
possibleAutoCompletions = new ArrayList<>();
autoCompletePartialName = getWordBeforeCaret(boundTextField);
if (autoCompletePartialName.isEmpty()) {
return;
}
nextAutoCompleteIndex = 0;
possibleAutoCompletions.addAll(
playerService.getPlayerNames().stream()
.filter(playerName -> playerName.toLowerCase(US).startsWith(autoCompletePartialName.toLowerCase()))
.sorted()
.collect(Collectors.toList())
);
}
public void bindTo(TextInputControl messageTextField) {
Assert.checkNotNullIllegalState(boundTextField, "AutoCompletionHelper is already bound to a TextInputControl");
boundTextField = messageTextField;
boundTextField.addEventFilter(KeyEvent.KEY_PRESSED, keyEventHandler);
}
public void unbind() {
if (boundTextField != null) {
boundTextField.removeEventFilter(KeyEvent.KEY_PRESSED, keyEventHandler);
boundTextField = null;
}
}
public boolean isBound() {
return boundTextField != null;
}
private String getWordBeforeCaret(TextInputControl messageTextField) {
messageTextField.selectPreviousWord();
String selectedText = messageTextField.getSelectedText();
messageTextField.positionCaret(messageTextField.getAnchor());
return selectedText;
}
private void resetAutoCompletion() {
possibleAutoCompletions = null;
nextAutoCompleteIndex = -1;
autoCompletePartialName = null;
}
}