package com.faforever.client.chat;
import com.faforever.client.chat.avatar.AvatarService;
import com.faforever.client.fx.JavaFxUtil;
import com.faforever.client.game.GameInfoBean;
import com.faforever.client.game.GameService;
import com.faforever.client.game.GameStatus;
import com.faforever.client.game.GamesController;
import com.faforever.client.game.JoinGameHelper;
import com.faforever.client.game.PlayerCardTooltipController;
import com.faforever.client.i18n.I18n;
import com.faforever.client.notification.ImmediateNotification;
import com.faforever.client.notification.NotificationService;
import com.faforever.client.notification.ReportAction;
import com.faforever.client.notification.Severity;
import com.faforever.client.preferences.ChatPrefs;
import com.faforever.client.preferences.PreferencesService;
import com.faforever.client.replay.ReplayService;
import com.faforever.client.reporting.ReportingService;
import com.faforever.client.theme.ThemeService;
import com.google.common.eventbus.EventBus;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.WeakMapChangeListener;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.PopupWindow;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import static com.faforever.client.chat.ChatColorMode.CUSTOM;
import static com.faforever.client.chat.SocialStatus.SELF;
import static com.faforever.client.util.RatingUtil.getGlobalRating;
import static com.faforever.client.util.RatingUtil.getLeaderboardRating;
import static java.util.Collections.singletonList;
import static java.util.Locale.US;
public class ChatUserItemController {
private static final String CLAN_TAG_FORMAT = "[%s]";
@FXML
Pane chatUserItemRoot;
@FXML
ImageView countryImageView;
@FXML
ImageView avatarImageView;
@FXML
Label usernameLabel;
@FXML
Label clanLabel;
@FXML
ImageView statusImageView;
@Resource
ApplicationContext applicationContext;
@Resource
AvatarService avatarService;
@Resource
CountryFlagService countryFlagService;
@Resource
GameService gameService;
@Resource
PreferencesService preferencesService;
@Resource
ChatService chatService;
@Resource
GamesController gamesController;
@Resource
ReplayService replayService;
@Resource
I18n i18n;
@Resource
ThemeService themeService;
@Resource
NotificationService notificationService;
@Resource
ReportingService reportingService;
@Resource
JoinGameHelper joinGameHelper;
@Resource
EventBus eventBus;
private PlayerInfoBean playerInfoBean;
private boolean colorsAllowedInPane;
private ChangeListener<ChatColorMode> colorModeChangeListener;
private MapChangeListener<? super String, ? super Color> colorPerUserMapChangeListener;
private ChangeListener<String> avatarChangeListener;
private ChangeListener<String> clanChangeListener;
private ChangeListener<GameStatus> gameStatusChangeListener;
private PlayerCardTooltipController playerCardTooltipController;
@FXML
void initialize() {
chatUserItemRoot.setUserData(this);
}
@FXML
void onContextMenuRequested(ContextMenuEvent event) {
ChatUserContextMenuController contextMenuController = applicationContext.getBean(ChatUserContextMenuController.class);
contextMenuController.setPlayerInfoBean(playerInfoBean);
contextMenuController.getContextMenu().show(chatUserItemRoot.getScene().getWindow(), event.getScreenX(), event.getScreenY());
}
@FXML
void onUsernameClicked(MouseEvent mouseEvent) {
if (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.getClickCount() == 2) {
eventBus.post(new InitiatePrivateChatEvent(playerInfoBean.getUsername()));
}
}
@PostConstruct
void postConstruct() {
ChatPrefs chatPrefs = preferencesService.getPreferences().getChat();
colorModeChangeListener = (observable, oldValue, newValue) -> configureColor();
colorPerUserMapChangeListener = change -> {
String lowerUsername = playerInfoBean.getUsername().toLowerCase(US);
if (lowerUsername.equalsIgnoreCase(change.getKey())) {
Color newColor = chatPrefs.getUserToColor().get(lowerUsername);
assignColor(newColor);
}
};
avatarChangeListener = (observable, oldValue, newValue) -> Platform.runLater(() -> setAvatarUrl(newValue));
clanChangeListener = (observable, oldValue, newValue) -> Platform.runLater(() -> setClanTag(newValue));
gameStatusChangeListener = (observable, oldValue, newValue) -> Platform.runLater(() -> setGameStatus(newValue));
joinGameHelper.setParentNode(getRoot());
}
private void configureColor() {
ChatPrefs chatPrefs = preferencesService.getPreferences().getChat();
if (playerInfoBean.getSocialStatus() == SELF) {
usernameLabel.getStyleClass().add(SELF.getCssClass());
clanLabel.getStyleClass().add(SELF.getCssClass());
return;
}
Color color = null;
String lowerUsername = playerInfoBean.getUsername().toLowerCase(US);
ChatUser chatUser = chatService.getOrCreateChatUser(lowerUsername);
if (chatPrefs.getChatColorMode() == CUSTOM) {
synchronized (chatPrefs.getUserToColor()) {
if (chatPrefs.getUserToColor().containsKey(lowerUsername)) {
color = chatPrefs.getUserToColor().get(lowerUsername);
}
chatPrefs.getUserToColor().addListener(new WeakMapChangeListener<>(colorPerUserMapChangeListener));
}
} else if (chatPrefs.getChatColorMode() == ChatColorMode.RANDOM && colorsAllowedInPane) {
color = ColorGeneratorUtil.generateRandomColor(chatUser.getUsername().hashCode());
}
chatUser.setColor(color);
assignColor(color);
}
private void assignColor(Color color) {
if (color != null) {
usernameLabel.setStyle(String.format("-fx-text-fill: %s", JavaFxUtil.toRgbCode(color)));
clanLabel.setStyle(String.format("-fx-text-fill: %s", JavaFxUtil.toRgbCode(color)));
} else {
usernameLabel.setStyle("");
clanLabel.setStyle("");
}
}
private void setAvatarUrl(String avatarUrl) {
if (StringUtils.isEmpty(avatarUrl)) {
avatarImageView.setVisible(false);
} else {
avatarImageView.setImage(avatarService.loadAvatar(avatarUrl));
avatarImageView.setVisible(true);
}
}
private void setClanTag(String newValue) {
if (StringUtils.isEmpty(newValue)) {
clanLabel.setVisible(false);
} else {
clanLabel.setText(String.format(CLAN_TAG_FORMAT, newValue));
clanLabel.setVisible(true);
}
}
public void setGameStatus(GameStatus gameStatus) {
Image statusImage;
switch (gameStatus) {
case PLAYING:
statusImage = themeService.getThemeImage(ThemeService.PLAYING_STATUS_IMAGE);
break;
case HOST:
statusImage = themeService.getThemeImage(ThemeService.HOSTING_STATUS_IMAGE);
break;
case LOBBY:
statusImage = themeService.getThemeImage(ThemeService.LOBBY_STATUS_IMAGE);
break;
default:
statusImage = null;
}
statusImageView.setImage(statusImage);
statusImageView.setVisible(statusImageView.getImage() != null);
}
public Pane getRoot() {
return chatUserItemRoot;
}
public PlayerInfoBean getPlayerInfoBean() {
return playerInfoBean;
}
public void setPlayerInfoBean(PlayerInfoBean playerInfoBean) {
this.playerInfoBean = playerInfoBean;
configureColor();
addChatColorModeListener();
configureCountryImageView();
configureAvatarImageView();
configureClanLabel();
configureGameStatusView();
configureTooltip();
usernameLabel.setText(playerInfoBean.getUsername());
}
private void addChatColorModeListener() {
ChatPrefs chatPrefs = preferencesService.getPreferences().getChat();
chatPrefs.chatColorModeProperty().addListener(new WeakChangeListener<>(colorModeChangeListener));
}
private void configureCountryImageView() {
setCountry(playerInfoBean.getCountry());
Tooltip countryTooltip = new Tooltip(playerInfoBean.getCountry());
countryTooltip.textProperty().bind(playerInfoBean.countryProperty());
Tooltip.install(countryImageView, countryTooltip);
}
private void configureAvatarImageView() {
playerInfoBean.avatarUrlProperty().addListener(new WeakChangeListener<>(avatarChangeListener));
setAvatarUrl(playerInfoBean.getAvatarUrl());
Tooltip avatarTooltip = new Tooltip(playerInfoBean.getAvatarTooltip());
avatarTooltip.textProperty().bind(playerInfoBean.avatarTooltipProperty());
avatarTooltip.setAnchorLocation(PopupWindow.AnchorLocation.CONTENT_TOP_LEFT);
Tooltip.install(avatarImageView, avatarTooltip);
}
private void configureClanLabel() {
setClanTag(playerInfoBean.getClan());
playerInfoBean.clanProperty().addListener(new WeakChangeListener<>(clanChangeListener));
}
private void configureGameStatusView() {
setGameStatus(playerInfoBean.getGameStatus());
playerInfoBean.gameStatusProperty().addListener(new WeakChangeListener<>(gameStatusChangeListener));
}
private void configureTooltip() {
if (playerInfoBean.getChatOnly()) {
return;
}
// FIXME #294
Tooltip tooltip = new Tooltip();
Tooltip.install(clanLabel, tooltip);
Tooltip.install(usernameLabel, tooltip);
Bindings.createStringBinding(
() -> i18n.get("userInfo.ratingFormat", getGlobalRating(playerInfoBean), getLeaderboardRating(playerInfoBean)),
playerInfoBean.leaderboardRatingMeanProperty(), playerInfoBean.leaderboardRatingDeviationProperty(),
playerInfoBean.globalRatingMeanProperty(), playerInfoBean.globalRatingDeviationProperty()
);
}
private void setCountry(String country) {
if (StringUtils.isEmpty(country)) {
countryImageView.setVisible(false);
} else {
countryImageView.setImage(countryFlagService.loadCountryFlag(country));
countryImageView.setVisible(true);
}
}
@FXML
void onMouseEnterGameStatus() {
if (playerInfoBean.getGameStatus() == GameStatus.NONE) {
return;
}
GameStatusTooltipController gameStatusTooltipController = applicationContext.getBean(GameStatusTooltipController.class);
gameStatusTooltipController.setGameInfoBean(gameService.getByUid(playerInfoBean.getGameUid()));
Tooltip statusTooltip = new Tooltip();
statusTooltip.setGraphic(gameStatusTooltipController.getRoot());
Tooltip.install(statusImageView, statusTooltip);
}
@FXML
void onMouseEnterUsername() {
if (playerInfoBean.getChatOnly()) {
return;
}
if (playerCardTooltipController != null) {
return;
}
playerCardTooltipController = applicationContext.getBean(PlayerCardTooltipController.class);
playerCardTooltipController.setPlayer(playerInfoBean);
Tooltip statusTooltip = new Tooltip();
statusTooltip.setGraphic(playerCardTooltipController.getRoot());
Tooltip.install(usernameLabel, statusTooltip);
}
@FXML
void onMouseClickGameStatus(MouseEvent mouseEvent) {
GameStatus gameStatus = playerInfoBean.getGameStatus();
if (gameStatus == GameStatus.NONE) {
return;
}
if (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.getClickCount() == 2) {
int uid = playerInfoBean.getGameUid();
if (gameStatus == GameStatus.LOBBY || gameStatus == GameStatus.HOST) {
GameInfoBean gameInfoBean = gameService.getByUid(uid);
joinGameHelper.join(gameInfoBean);
} else if (gameStatus == GameStatus.PLAYING) {
try {
replayService.runLiveReplay(uid, playerInfoBean.getId());
} catch (IOException e) {
notificationService.addNotification(new ImmediateNotification(
i18n.get("errorTitle"), i18n.get("replayCouldNotBeStarted"),
Severity.ERROR, e, singletonList(new ReportAction(i18n, reportingService, e))
));
}
}
}
}
public void setColorsAllowedInPane(boolean colorsAllowedInPane) {
this.colorsAllowedInPane = colorsAllowedInPane;
configureColor();
}
public void setVisible(boolean visible) {
chatUserItemRoot.setVisible(visible);
chatUserItemRoot.setManaged(visible);
}
}