package com.faforever.client.vault.replay;

import com.faforever.client.fx.AbstractViewController;
import com.faforever.client.fx.NodeTableCell;
import com.faforever.client.fx.StringCell;
import com.faforever.client.game.Game;
import com.faforever.client.game.GameService;
import com.faforever.client.game.MapPreviewTableCell;
import com.faforever.client.i18n.I18n;
import com.faforever.client.map.MapService;
import com.faforever.client.map.MapService.PreviewSize;
import com.faforever.client.remote.domain.GameStatus;
import com.faforever.client.theme.UiService;
import com.faforever.client.util.TimeService;
import com.google.common.base.Joiner;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.collections.transformation.SortedList;
import javafx.scene.Node;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.image.Image;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LiveReplayController extends AbstractViewController<Node> {
  private final ObjectProperty<Game> selectedGame;
  private final GameService gameService;
  private final UiService uiService;
  private final I18n i18n;
  private final MapService mapService;
  private final TimeService timeService;
  public TableView<Game> liveReplayControllerRoot;
  public TableColumn<Game, Image> mapPreviewColumn;
  public TableColumn<Game, Instant> startTimeColumn;
  public TableColumn<Game, String> gameTitleColumn;
  public TableColumn<Game, Number> playersColumn;
  public TableColumn<Game, String> modsColumn;
  public TableColumn<Game, String> hostColumn;
  public TableColumn<Game, Game> watchColumn;

  public LiveReplayController(GameService gameService, UiService uiService, I18n i18n,
                              MapService mapService, TimeService timeService) {
    this.gameService = gameService;
    this.uiService = uiService;
    this.i18n = i18n;
    this.mapService = mapService;
    this.timeService = timeService;

    selectedGame = new SimpleObjectProperty<>();
  }

  public void initialize() {
    Comparator gameSorter = Comparator.comparing(
        (Game game) -> game.getStartTime(),
        Comparator.nullsFirst(Comparator.naturalOrder()))
        .reversed();
    initializeGameTable(gameService.getGames()
                    .filtered(game -> game.getStatus() == GameStatus.PLAYING)
                    .sorted(gameSorter));
  }

  private void initializeGameTable(ObservableList<Game> games) {
    SortedList<Game> sortedList = new SortedList<>(games);
    sortedList.comparatorProperty().bind(liveReplayControllerRoot.comparatorProperty());

    mapPreviewColumn.setCellFactory(param -> new MapPreviewTableCell(uiService));
    mapPreviewColumn.setCellValueFactory(param -> Bindings.createObjectBinding(
        () -> mapService.loadPreview(param.getValue().getMapFolderName(), PreviewSize.SMALL),
        param.getValue().mapFolderNameProperty()
    ));

    startTimeColumn.setCellValueFactory(param -> param.getValue().startTimeProperty());
    startTimeColumn.setCellFactory(param -> new StringCell<>(this.timeService::asShortTime));
    gameTitleColumn.setCellValueFactory(param -> param.getValue().titleProperty());
    gameTitleColumn.setCellFactory(param -> new StringCell<>(title -> title));
    playersColumn.setCellValueFactory(param -> param.getValue().numPlayersProperty());
    playersColumn.setCellFactory(param -> new StringCell<>(number -> i18n.number(number.intValue())));
    hostColumn.setCellValueFactory(param -> param.getValue().hostProperty());
    hostColumn.setCellFactory(param -> new StringCell<>(String::toString));
    modsColumn.setCellValueFactory(this::modCell);
    modsColumn.setCellFactory(param -> new StringCell<>(String::toString));
    watchColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue()));
    watchColumn.setCellFactory(param -> new NodeTableCell<>(this::watchReplayButton));

    liveReplayControllerRoot.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue)
        -> Platform.runLater(() -> selectedGame.set(newValue)));

    liveReplayControllerRoot.setItems(games);
  }

  private Node watchReplayButton(Game game) {
    WatchButtonController controller = uiService.loadFxml("theme/vault/replay/watch_button.fxml");
    controller.setGame(game);
    return controller.getRoot();
  }

  @Override
  public Node getRoot() {
    return liveReplayControllerRoot;
  }

  @NotNull
  private ObservableValue<String> modCell(CellDataFeatures<Game, String> param) {
    ObservableMap<String, String> simMods = param.getValue().getSimMods();
    int simModCount = simMods.size();
    List<String> modNames = simMods.entrySet().stream()
        .limit(2)
        .map(Entry::getValue)
        .collect(Collectors.toList());
    if (simModCount > 2) {
      return new SimpleStringProperty(i18n.get("game.mods.twoAndMore", modNames.get(0), modNames.size()));
    }
    return new SimpleStringProperty(Joiner.on(i18n.get("textSeparator")).join(modNames));
  }
}

@@ -989,16 +989,71 @@
 bleMap;%0A
+import javafx.collections.transformation.FilteredList;%0A
 import j
@@ -1222,16 +1222,66 @@
 atures;%0A
+import javafx.scene.control.TableColumn.SortType;%0A
 import j
@@ -1592,37 +1592,8 @@
 nt;%0A
-import java.util.Comparator;%0A
 impo
@@ -2862,253 +2862,204 @@
     
-Comparator gameSorter = Comparator.comparing(%0A        (Game game) -%3E game.getStartTi
+initializeGameTable(gameService.getGa
 me
+s
 ()
-,%0A        Comparator.nullsFirst(Comparator.naturalOrder()))%0A        .reversed();%0A    initializeGameTable(gameService.getGames()%0A                
+);%0A  %7D%0A%0A  private void initializeGameTable(ObservableList%3CGame%3E games) %7B%0A    FilteredList%3CGame%3E filteredGameList = new FilteredList%3C%3E(games);%0A
     
-.
 filtered
 (gam
@@ -3054,16 +3054,37 @@
 filtered
+GameList.setPredicate
 (game -%3E
@@ -3127,120 +3127,9 @@
 ING)
-%0A                    .sorted(gameSorter));%0A  %7D%0A%0A  private void initializeGameTable(ObservableList%3CGame%3E games) %7B
+;
 %0A   
@@ -3172,29 +3172,40 @@
 ortedList%3C%3E(
-games
+filteredGameList
 );%0A    sorte
@@ -3281,24 +3281,182 @@
 operty());%0A%0A
+    startTimeColumn.setSortType(SortType.DESCENDING);%0A    liveReplayControllerRoot.getSortOrder().add(startTimeColumn);%0A    liveReplayControllerRoot.sort();%0A%0A
     mapPrevi
@@ -4974,21 +4974,26 @@
 etItems(
-games
+sortedList
 );%0A  %7D%0A%0A

Clean up replay sorting