package ru.testing.client.controllers;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.FlowPane;
import javafx.scene.shape.Circle;
import org.controlsfx.control.StatusBar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.testing.client.MainApp;
import ru.testing.client.common.Utils;
import ru.testing.client.common.DataBase;
import ru.testing.client.common.objects.ReceivedMessage;
import ru.testing.client.common.objects.Settings;
import ru.testing.client.elements.Dialogs;
import ru.testing.client.elements.filter.FilterListPopOver;
import ru.testing.client.elements.tabs.WsMessagesTab;
import ru.testing.client.websocket.*;
import java.net.URI;
/**
* Controller for detail message tab form
*/
public class TabWsMessagesController {
private static final Logger LOGGER = LoggerFactory.getLogger(TabWsMessagesController.class);
private static final int CHECK_CONNECTION_STATUS_TIMEOUT = 1000;
private DataBase dataBase = DataBase.getInstance();
private final ObservableList<ReceivedMessage> receivedMessageList = FXCollections.observableArrayList();
private final ObservableList<ReceivedMessage> receivedFilteredMessageList = FXCollections.observableArrayList();
private final ObservableList<String> filterList = FXCollections.observableArrayList();
private MainController mainController = MainApp.getMainController();
private Settings settings;
private FilterListPopOver filterPopOver;
private SendMessagesPopOver sendMessagesPopOver;
private Tooltip statusTooltip;
private WsClient wsClient;
private boolean filtered, autoScroll;
@FXML
private FlowPane sendMessagePane;
@FXML
private ListView<ReceivedMessage> outputTextView;
@FXML
private Label filterCount;
@FXML
private FlowPane filterBar;
@FXML
private Button messageSendBtn;
@FXML
private ToggleButton sendMsgHistoryBtn;
@FXML
private Button filterAddBtn;
@FXML
private ToggleButton filterListBtn;
@FXML
private TextField sendMsgTextField;
@FXML
private TextField filterTextField;
@FXML
private Label filterStatusLabel;
@FXML
private Label timeDiffLabel;
@FXML
private Label autoScrollLabel;
@FXML
private Label outputMsgCount;
@FXML
private Label lbHeadersCounter;
@FXML
private StatusBar statusBar;
@FXML
private Circle connectStatus;
@FXML
protected void initialize() {
// Get settings
settings = dataBase.getSettings();
autoScroll = settings.isAutoScroll();
// Set message font size
outputTextView.setStyle(String.format("-fx-font-size: %spx;", settings.getFontSize()));
// Default focus request
Platform.runLater(() -> outputTextView.requestFocus());
// Update output message list view
outputTextView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
outputSetList(false);
outputTextView.getItems().addListener(this::receivedMessageListener);
outputTextView.getSelectionModel().getSelectedItems().addListener(this::selectedMessages);
outputTextView.setStyle(String.format("-fx-font-size: %spx;", settings.getFontSize()));
// Send message
sendMsgTextField.setOnKeyPressed(keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
sendWebsocketMessage();
}
});
// Filters
filterList.addListener((ListChangeListener<String>) c -> {
if (c.next()) {
int size = filterList.size();
if (size > 0) {
filterListBtn.setDisable(false);
filterCount.setText(String.valueOf(size));
receivedFilteredMessageList.clear();
receivedMessageList.forEach(message ->
filterList.forEach(filter -> {
if (message.getMessage().contains(filter)) {
receivedFilteredMessageList.add(message);
}
}));
outputSetList(filtered);
} else {
filterListBtn.setDisable(true);
filterListBtn.setSelected(false);
getFilterPopOver().hide();
filterCount.setText("");
receivedFilteredMessageList.clear();
outputSetList(false);
}
}
});
filterTextField.setOnKeyPressed(keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
addToFilterList();
}
});
// Set circle tooltip status
setCircleTooltip("Disconnected");
// Start ws client
startWsClient();
}
/**
* Send websocket message
*/
@FXML
private void sendWebsocketMessage() {
String text = sendMsgTextField.getText().trim();
if (!text.isEmpty()) {
ObservableList<String> sendList = getSendMessagesPopOver().getController().getList();
if (!sendList.contains(text)) {
sendList.add(text);
}
addMessageToOutput(ReceivedMessageType.SEND, text);
if (wsClient != null) {
wsClient.sendMessage(text);
}
sendMsgTextField.clear();
}
sendMsgTextField.requestFocus();
}
@FXML
private void changeFilterStatus() {
Platform.runLater(() -> {
if (filtered) {
filterBar.setVisible(false);
filterBar.setManaged(false);
filterStatusLabel.setGraphic(new ImageView("/images/turn-off.png"));
filterAddBtn.setDisable(true);
filterTextField.setDisable(true);
filterListBtn.setDisable(true);
filtered = false;
outputSetList(false);
lbHeadersCounter.requestFocus();
} else {
filterBar.setVisible(true);
filterBar.setManaged(true);
filterStatusLabel.setGraphic(new ImageView("/images/turn-on.png"));
filterAddBtn.setDisable(false);
filterTextField.setDisable(false);
filterTextField.requestFocus();
if (filterList.size() > 0) {
filterListBtn.setDisable(false);
outputSetList(true);
}
filtered = true;
}
});
}
/**
* Set on/off auto scroll output list status from menu bar
*/
@FXML
private void changeAutoScrollStatus() {
Platform.runLater(() -> {
if (autoScroll) {
autoScrollLabel.setGraphic(new ImageView("/images/turn-off.png"));
autoScroll = false;
} else {
autoScrollLabel.setGraphic(new ImageView("/images/turn-on.png"));
autoScroll = true;
}
});
}
/**
* Apply text filter for new response
*/
@FXML
private void addToFilterList() {
String text = filterTextField.getText().trim();
if (!text.isEmpty()) {
filterList.add(text);
filterTextField.clear();
}
filterTextField.requestFocus();
}
/**
* Show message history pop over
*/
@FXML
private void showSendHistoryPopOver() {
if (sendMsgHistoryBtn.isSelected()) {
getSendMessagesPopOver().show(sendMsgHistoryBtn, -7);
} else {
getSendMessagesPopOver().hide();
}
}
/**
* Method create and show message history window
*/
@FXML
private void showFilterListPopOver() {
if (filterListBtn.isSelected()) {
getFilterPopOver().show(filterListBtn, -10);
} else {
getFilterPopOver().hide();
}
}
/**
* Get current websocket client
*
* @return WsClient
*/
public WsClient getWsClient() {
return wsClient;
}
/**
* Get received messages
*
* @return ObservableList<ReceivedMessage>
*/
public ObservableList<ReceivedMessage> getReceivedMessageList() {
return receivedMessageList;
}
/**
* Start websocket wsClient
*/
void startWsClient() {
try {
if (wsClient == null) {
wsClient = new WsClient();
wsClient.setEndpointURI(new URI(mainController.getServerUrl().getText()));
wsClient.setHeaders(mainController.getHeadersList());
wsClient.setSslValidate(settings.isWsSslValidate());
}
wsClient.openConnection();
wsClient.setMessageHandler(new MessageHandler(this));
getSendMessagesPopOver().getController().getSentMessages().forEach(sentMessage -> {
if (wsClient != null && sentMessage.isAutoSend()) {
String msgValue = sentMessage.getValue();
wsClient.sendMessage(msgValue);
addMessageToOutput(ReceivedMessageType.SEND, msgValue);
}
});
} catch (Exception e) {
LOGGER.error("Error open connection: {}", e.getLocalizedMessage());
Platform.runLater(() -> new Dialogs().getExceptionDialog(e));
} finally {
checkConnectionStatus();
}
}
/**
* Add websocket response message to output text area
*
* @param type Message type
* @param message String message
*/
public void addMessageToOutput(ReceivedMessageType type, String message) {
if (filtered && filterList.size() > 0) {
for (String filterItem : filterList) {
if (message.contains(filterItem)) {
Platform.runLater(() -> {
receivedMessageList.add(new ReceivedMessage(type, message));
receivedFilteredMessageList.add(new ReceivedMessage(type, message));
});
return;
}
}
Platform.runLater(() -> receivedMessageList.add(new ReceivedMessage(type, message)));
} else {
Platform.runLater(() -> receivedMessageList.add(new ReceivedMessage(type, message)));
}
}
/**
* Scroll to last list message after new message added
*
* @param change ListChangeListener.Change<? extends ReceivedMessage>
*/
private void receivedMessageListener(ListChangeListener.Change<? extends ReceivedMessage> change) {
if (change.next()) {
Platform.runLater(() -> {
showAllMsgAndSelectedMsgCount();
final int size = receivedMessageList.size();
if (size > 0 && autoScroll) {
outputTextView.scrollTo(size - 1);
}
});
}
}
/**
* Show all output message and selected message count
*/
private void showAllMsgAndSelectedMsgCount() {
outputMsgCount.setText(String.format("%s/%s",
receivedMessageList.size(),
outputTextView.getSelectionModel().getSelectedItems().size()));
}
/**
* Show time diff between first and last selected message
*
* @param change ListChangeListener.Change<? extends ReceivedMessage>
*/
private void selectedMessages(ListChangeListener.Change<? extends ReceivedMessage> change) {
if (change.next()) {
showAllMsgAndSelectedMsgCount();
int selectedSize = change.getList().size();
if (selectedSize > 1 && change.wasAdded()) {
long timeFirst = change.getList().get(0).getMilliseconds();
long timeLast = change.getList().get(selectedSize - 1).getMilliseconds();
timeDiffLabel.setText(Utils.getFormattedDiffTime(timeFirst, timeLast));
} else {
timeDiffLabel.setText("");
}
}
}
/**
* Set output text list
*
* @param isFiltered boolean
*/
private void outputSetList(boolean isFiltered) {
if (isFiltered) {
outputTextView.setItems(receivedFilteredMessageList);
} else {
outputTextView.setItems(receivedMessageList);
}
outputTextView.setCellFactory(listView -> new ReceivedMessageCellFactory(this, isFiltered));
}
/**
* Get filter list pop over
*
* @return FilterListPopOver
*/
private FilterListPopOver getFilterPopOver() {
if (filterPopOver == null) {
filterPopOver = new FilterListPopOver(this);
}
return filterPopOver;
}
/**
* Get send message history pop over
*
* @return SendMessagesPopOver
*/
public SendMessagesPopOver getSendMessagesPopOver() {
if (sendMessagesPopOver == null) {
sendMessagesPopOver = new SendMessagesPopOver(this);
}
return sendMessagesPopOver;
}
/**
* Check connection status
*/
void checkConnectionStatus() {
Task task = new Task() {
@Override
protected Object call() throws Exception {
try {
do {
if (wsClient != null && wsClient.isOpenConnection()) {
setConnectStat(true);
} else {
setConnectStat(false);
break;
}
Thread.sleep(CHECK_CONNECTION_STATUS_TIMEOUT);
} while (wsClient != null);
} catch (InterruptedException e) {
LOGGER.error("Thread interrupted exception{}", e.getMessage());
}
return null;
}
};
new Thread(task).start();
}
public ListView<ReceivedMessage> getOutputTextView() {
return outputTextView;
}
public ObservableList<ReceivedMessage> getReceivedFilteredMessageList() {
return receivedFilteredMessageList;
}
public ObservableList<String> getFilterList() {
return filterList;
}
public ToggleButton getFilterListBtn() {
return filterListBtn;
}
public TextField getSendMsgTextField() {
return sendMsgTextField;
}
public ToggleButton getSendMsgHistoryBtn() {
return sendMsgHistoryBtn;
}
/**
* Set status disable or enable send message text field and button
*
* @param isConnected boolean
*/
private void setConnectStat(boolean isConnected) {
Platform.runLater(() -> {
Tab currentTab = mainController.getTabPane().getSelectionModel().getSelectedItem();
Button connectionButton = null;
if (currentTab instanceof WsMessagesTab) {
if (((WsMessagesTab) currentTab).getController() == this) {
connectionButton = mainController.getConnectionButton();
}
}
if (isConnected) {
connectStatus.getStyleClass().clear();
connectStatus.getStyleClass().add("connected");
setCircleTooltip("Connected");
sendMessagePane.setVisible(true);
sendMessagePane.setManaged(true);
if (connectionButton != null) {
connectionButton.setText("Disconnect");
}
} else {
connectStatus.getStyleClass().clear();
connectStatus.getStyleClass().add("disconnected");
setCircleTooltip("Disconnected");
sendMessagePane.setVisible(false);
sendMessagePane.setManaged(false);
if (connectionButton != null) {
connectionButton.setText("Connect");
}
}
});
}
/**
* Set circle status tooltip message
*
* @param message String
*/
private void setCircleTooltip(String message) {
if (statusTooltip == null) {
statusTooltip = new Tooltip(message);
Tooltip.install(connectStatus, statusTooltip);
} else {
statusTooltip.setText(message);
}
}
}