package app.data.service; import app.data.bean.CalculatedMatch; import app.data.bean.PlayerForTable; import app.data.entity.Matchday; import app.data.entity.Player; import app.data.repository.PlayerRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.vaadin.artur.helpers.CrudService; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @Service public class PlayerService extends CrudService { private final PlayerRepository repository; private final MatchdayService matchdayService; private final MatchService matchService; public PlayerService(@Autowired PlayerRepository repository, @Autowired MatchdayService matchdayService, @Autowired MatchService matchService) { this.repository = repository; this.matchdayService = matchdayService; this.matchService = matchService; } @Override protected PlayerRepository getRepository() { return repository; } public List getPlayersForTable(Matchday matchday) { return new PlayerForTableProvider(matchday).getPlayersForTableSorted(true); } private class PlayerForTableProvider { List matchdays = new ArrayList<>(); List calculatedMatches = new ArrayList<>(); List players = repository.findAll(); public PlayerForTableProvider(Matchday matchday) { matchdays.addAll(matchdayService.getMatchdaysSorted(matchday.getSeason()).stream() .filter(matchdayToFilter -> matchdayToFilter.getNumber() <= matchday.getNumber()) .collect(Collectors.toList())); calculatedMatches.addAll(matchdays.stream() .flatMap((Function>) matchdayToMap -> matchService.getCalculatedMatches(matchdayToMap).stream()) .collect(Collectors.toList())); } private List getPlayersForTableSorted(boolean calcDiffToLastMatchday) { List playerForTableList = calcSortedPlayerForTableList(); int offset = 0; PlayerForTable lastPlayer; PlayerForTable currentPlayer; for (int i = 0; i < playerForTableList.size(); i++) { currentPlayer = playerForTableList.get(i); offset = getOffset(playerForTableList, offset, currentPlayer, i); currentPlayer.setPlace(i + 1 - offset); currentPlayer.setPlaceString(offset == 0 ? String.valueOf(i + 1) : ""); } if (calcDiffToLastMatchday) calcAndSetPlaceDiffToLastMatchday(playerForTableList); return playerForTableList; } private int getOffset(List playerForTableList, int offset, PlayerForTable currentPlayer, int i) { PlayerForTable lastPlayer; if (i > 0) { lastPlayer = playerForTableList.get(i - 1); // TODO: add direct comparison below if (playersHaveCompletelyEqualScores(lastPlayer, currentPlayer)) return offset + 1; return 0; } return offset; } private boolean playersHaveCompletelyEqualScores(PlayerForTable lastPlayer, PlayerForTable currentPlayer) { return Objects.equals(currentPlayer.getMatchPoints(), lastPlayer.getMatchPoints()) && Objects.equals(currentPlayer.getGamePointsForSelf(), lastPlayer.getGamePointsForSelf()) && Objects.equals(currentPlayer.getGamePointsForOpponents(), lastPlayer.getGamePointsForOpponents()); } private List calcSortedPlayerForTableList() { return players.stream() .map(this::getPlayerForTable) .sorted(Comparator.comparingDouble(PlayerForTable::getGamePointsForSelf).reversed()) .sorted(Comparator.comparingDouble(PlayerForTable::getGamePointDiff).reversed()) .sorted(Comparator.comparingInt(PlayerForTable::getMatchPoints).reversed()) .collect(Collectors.toList()); } private void calcAndSetPlaceDiffToLastMatchday(List playerForTableList) { if (matchdays.size() < 2) { playerForTableList.forEach(playerForTable -> playerForTable.setPlaceDiffToLastMatchday(Integer.MAX_VALUE)); return; } List listForLastMatchday = getListForLastMatchday(); playerForTableList.forEach(playerForTable -> calcAndSetPlaceDiffToLastMatchday(playerForTable, listForLastMatchday)); } private void calcAndSetPlaceDiffToLastMatchday(PlayerForTable playerForTable, List listForLastMatchday) { int placeLastMatchday = listForLastMatchday.stream() .filter(playerForTableLastMatchday -> playerForTable.getPlayer().equals(playerForTableLastMatchday.getPlayer())) .findFirst() .map(PlayerForTable::getPlace) .orElseThrow(); playerForTable.setPlaceDiffToLastMatchday(placeLastMatchday - playerForTable.getPlace()); } private List getListForLastMatchday() { return new PlayerForTableProvider(matchdays.get(matchdays.size() - 2)).getPlayersForTableSorted(false); } private PlayerForTable getPlayerForTable(Player player) { List matchesAsPlayer1 = getMatchesAsPlayer1(player); List matchesAsPlayer2 = getMatchesAsPlayer2(player); Map> map = getWinStateLists(matchesAsPlayer1, matchesAsPlayer2); int amountOfMatchesWon = map.get(WinState.WON).size(); int amountOfMatchesDrawn = map.get(WinState.DRAWN).size(); int amountOfMatchesLost = map.get(WinState.LOST).size(); int amountOfMatches = amountOfMatchesWon + amountOfMatchesDrawn + amountOfMatchesLost; int matchPoints = amountOfMatchesWon * 2 + amountOfMatchesDrawn; double gamePointsForSelf = getGamePointsForSelf(matchesAsPlayer1, matchesAsPlayer2); double gamePointsForOpponents = getGamePointsForOpponents(matchesAsPlayer1, matchesAsPlayer2); double gamePointDiff = gamePointsForSelf - gamePointsForOpponents; return new PlayerForTable(player, matchPoints, amountOfMatches, amountOfMatchesWon, amountOfMatchesDrawn, amountOfMatchesLost, gamePointsForSelf, gamePointsForOpponents, gamePointDiff); } private List getMatchesAsPlayer1(Player player) { return calculatedMatches.stream() .filter(match -> match.getPlayer1().equals(player)) .collect(Collectors.toList()); } private List getMatchesAsPlayer2(Player player) { return calculatedMatches.stream() .filter(match -> match.getPlayer2().equals(player)) .collect(Collectors.toList()); } private double getGamePointsForSelf(List matchesAsPlayer1, List matchesAsPlayer2) { double gamePointsForSelf = 0; gamePointsForSelf += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore1).sum(); gamePointsForSelf += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore2).sum(); return gamePointsForSelf; } private double getGamePointsForOpponents(List matchesAsPlayer1, List matchesAsPlayer2) { double gamePointsForOpponents = 0; gamePointsForOpponents += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore2).sum(); gamePointsForOpponents += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore1).sum(); return gamePointsForOpponents; } private Map> getWinStateLists(List matchesAsPlayer1, List matchesAsPlayer2) { Map> map = new HashMap<>(); map.put(WinState.WON, new ArrayList<>()); map.put(WinState.DRAWN, new ArrayList<>()); map.put(WinState.LOST, new ArrayList<>()); for (CalculatedMatch match : matchesAsPlayer1) { if (match.getScore1() > match.getScore2()) { map.get(WinState.WON).add(match); continue; } if (match.getScore1() < match.getScore2()) { map.get(WinState.LOST).add(match); continue; } if (match.getScore1() > 0) { map.get(WinState.DRAWN).add(match); } } for (CalculatedMatch match : matchesAsPlayer2) { if (match.getScore2() > match.getScore1()) { map.get(WinState.WON).add(match); continue; } if (match.getScore2() < match.getScore1()) { map.get(WinState.LOST).add(match); continue; } if (match.getScore2() > 0) { map.get(WinState.DRAWN).add(match); } } return map; } } private enum WinState { WON, DRAWN, LOST } }