package com.faforever.client.game;
import com.faforever.client.remote.domain.GameInfoMessage;
import com.faforever.client.remote.domain.GameState;
import com.faforever.client.remote.domain.VictoryCondition;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.MapProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleMapProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import org.apache.commons.lang3.StringEscapeUtils;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GameInfoBean {
private static final String RATING_NUMBER = "\\d+(?:\\.\\d+)?k?";
private static final Pattern MIN_RATING_PATTERN = Pattern.compile(">\\s*(" + RATING_NUMBER + ")|(" + RATING_NUMBER + ")\\s*\\+");
private static final Pattern MAX_RATING_PATTERN = Pattern.compile("<\\s*(" + RATING_NUMBER + ")");
private static final Pattern ABOUT_RATING_PATTERN = Pattern.compile("~\\s*(" + RATING_NUMBER + ")");
private static final Pattern BETWEEN_RATING_PATTERN = Pattern.compile("(" + RATING_NUMBER + ")\\s*-\\s*(" + RATING_NUMBER + ")");
private final StringProperty host;
private final StringProperty title;
private final StringProperty mapFolderName;
private final StringProperty featuredMod;
private final IntegerProperty uid;
private final IntegerProperty numPlayers;
private final IntegerProperty maxPlayers;
private final IntegerProperty minRating;
private final IntegerProperty maxRating;
private final BooleanProperty passwordProtected;
private final StringProperty password;
private final ObjectProperty<GameVisibility> visibility;
private final ObjectProperty<GameState> status;
private final ObjectProperty<VictoryCondition> victoryCondition;
/**
* Maps a sim mod's UID to its name.
*/
private final MapProperty<String, String> simMods;
private final MapProperty<String, List<String>> teams;
/**
* Maps an index (1,2,3,4...) to a version number. Don't ask me what this index maps to.
*/
private final MapProperty<String, Integer> featuredModVersions;
public GameInfoBean(GameInfoMessage gameInfoMessage) {
this();
updateFromGameInfo(gameInfoMessage);
}
public GameInfoBean() {
uid = new SimpleIntegerProperty();
host = new SimpleStringProperty();
title = new SimpleStringProperty();
mapFolderName = new SimpleStringProperty();
featuredMod = new SimpleStringProperty();
numPlayers = new SimpleIntegerProperty();
maxPlayers = new SimpleIntegerProperty();
minRating = new SimpleIntegerProperty(0);
maxRating = new SimpleIntegerProperty(3000);
passwordProtected = new SimpleBooleanProperty();
password = new SimpleStringProperty();
victoryCondition = new SimpleObjectProperty<>();
visibility = new SimpleObjectProperty<>();
simMods = new SimpleMapProperty<>(FXCollections.observableHashMap());
teams = new SimpleMapProperty<>(FXCollections.observableHashMap());
featuredModVersions = new SimpleMapProperty<>(FXCollections.observableHashMap());
status = new SimpleObjectProperty<>();
}
public void updateFromGameInfo(GameInfoMessage gameInfoMessage) {
uid.set(gameInfoMessage.getUid());
host.set(gameInfoMessage.getHost());
title.set(StringEscapeUtils.unescapeHtml4(gameInfoMessage.getTitle()));
mapFolderName.set(gameInfoMessage.getMapname());
featuredMod.set(gameInfoMessage.getFeaturedMod());
numPlayers.setValue(gameInfoMessage.getNumPlayers());
maxPlayers.setValue(gameInfoMessage.getMaxPlayers());
victoryCondition.set(gameInfoMessage.getGameType());
status.set(gameInfoMessage.getState());
passwordProtected.set(gameInfoMessage.getPasswordProtected());
synchronized (simMods.get()) {
simMods.clear();
if (gameInfoMessage.getSimMods() != null) {
simMods.putAll(gameInfoMessage.getSimMods());
}
}
synchronized (teams.get()) {
teams.clear();
if (gameInfoMessage.getTeams() != null) {
teams.putAll(gameInfoMessage.getTeams());
}
}
synchronized (featuredModVersions.get()) {
featuredModVersions.clear();
if (gameInfoMessage.getFeaturedModVersions() != null) {
featuredModVersions.putAll(gameInfoMessage.getFeaturedModVersions());
}
}
// TODO this can be removed as soon as we get server side support. Until then, let's be hacky
String titleString = title.get();
Matcher matcher = BETWEEN_RATING_PATTERN.matcher(titleString);
if (matcher.find()) {
minRating.set(parseRating(matcher.group(1)));
maxRating.set(parseRating(matcher.group(2)));
} else {
matcher = MIN_RATING_PATTERN.matcher(titleString);
if (matcher.find()) {
if (matcher.group(1) != null) {
minRating.set(parseRating(matcher.group(1)));
}
if (matcher.group(2) != null) {
minRating.set(parseRating(matcher.group(2)));
}
maxRating.set(3000);
} else {
matcher = MAX_RATING_PATTERN.matcher(titleString);
if (matcher.find()) {
minRating.set(0);
maxRating.setValue(parseRating(matcher.group(1)));
} else {
matcher = ABOUT_RATING_PATTERN.matcher(titleString);
if (matcher.find()) {
int rating = parseRating(matcher.group(1));
minRating.set(rating - 300);
maxRating.set(rating + 300);
}
}
}
}
}
private int parseRating(String string) {
try {
return Integer.parseInt(string);
} catch (NumberFormatException e) {
int rating;
String[] split = string.replace("k", "").split("\\.");
try {
rating = Integer.parseInt(split[0]) * 1000;
if (split.length == 2) {
rating += Integer.parseInt(split[1]) * 100;
}
return rating;
} catch (NumberFormatException e1) {
return Integer.MAX_VALUE;
}
}
}
public String getHost() {
return host.get();
}
public void setHost(String host) {
this.host.set(host);
}
public StringProperty hostProperty() {
return host;
}
public String getTitle() {
return title.get();
}
public void setTitle(String title) {
this.title.set(title);
}
public StringProperty titleProperty() {
return title;
}
public String getMapFolderName() {
return mapFolderName.get();
}
public void setMapFolderName(String mapFolderName) {
this.mapFolderName.set(mapFolderName);
}
public StringProperty mapFolderNameProperty() {
return mapFolderName;
}
public String getFeaturedMod() {
return featuredMod.get();
}
public void setFeaturedMod(String featuredMod) {
this.featuredMod.set(featuredMod);
}
public StringProperty featuredModProperty() {
return featuredMod;
}
public int getUid() {
return uid.get();
}
public void setUid(int uid) {
this.uid.set(uid);
}
public IntegerProperty uidProperty() {
return uid;
}
public int getNumPlayers() {
return numPlayers.get();
}
public void setNumPlayers(int numPlayers) {
this.numPlayers.set(numPlayers);
}
public IntegerProperty numPlayersProperty() {
return numPlayers;
}
public int getMaxPlayers() {
return maxPlayers.get();
}
public void setMaxPlayers(int maxPlayers) {
this.maxPlayers.set(maxPlayers);
}
public IntegerProperty maxPlayersProperty() {
return maxPlayers;
}
public int getMinRating() {
return minRating.get();
}
public void setMinRating(int minRating) {
this.minRating.set(minRating);
}
public IntegerProperty minRatingProperty() {
return minRating;
}
public int getMaxRating() {
return maxRating.get();
}
public void setMaxRating(int maxRating) {
this.maxRating.set(maxRating);
}
public IntegerProperty maxRatingProperty() {
return maxRating;
}
public GameState getStatus() {
return status.get();
}
public void setStatus(GameState status) {
this.status.set(status);
}
public ObjectProperty<GameState> statusProperty() {
return status;
}
public VictoryCondition getVictoryCondition() {
return victoryCondition.get();
}
public void setVictoryCondition(VictoryCondition victoryCondition) {
this.victoryCondition.set(victoryCondition);
}
public ObjectProperty<VictoryCondition> victoryConditionProperty() {
return victoryCondition;
}
/**
* Returns a map of simulation mod UIDs to the mod's name.
*/
public ObservableMap<String, String> getSimMods() {
return simMods.get();
}
public void setSimMods(ObservableMap<String, String> simMods) {
this.simMods.set(simMods);
}
public MapProperty<String, String> simModsProperty() {
return simMods;
}
/**
* Maps team names ("1", "2", ...) to a list of player names.
* <strong>Make sure to synchronize on the return value.</strong>
*/
public ObservableMap<String, List<String>> getTeams() {
return teams.get();
}
public void setTeams(ObservableMap<String, List<String>> teams) {
this.teams.set(teams);
}
public MapProperty<String, List<String>> teamsProperty() {
return teams;
}
public ObservableMap<String, Integer> getFeaturedModVersions() {
return featuredModVersions.get();
}
public void setFeaturedModVersions(ObservableMap<String, Integer> featuredModVersions) {
this.featuredModVersions.set(featuredModVersions);
}
public MapProperty<String, Integer> featuredModVersionsProperty() {
return featuredModVersions;
}
@Override
public int hashCode() {
return uid.getValue().hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof GameInfoBean
&& uid.getValue().equals(((GameInfoBean) obj).uid.getValue());
}
public GameVisibility getVisibility() {
return visibility.get();
}
public ObjectProperty<GameVisibility> visibilityProperty() {
return visibility;
}
public boolean getPasswordProtected() {
return passwordProtected.get();
}
public BooleanProperty passwordProtectedProperty() {
return passwordProtected;
}
public String getPassword() {
return password.get();
}
public void setPassword(String password) {
this.password.set(password);
}
public StringProperty passwordProperty() {
return password;
}
}