package net.olemartin.domain;
import com.google.common.collect.Lists;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import org.neo4j.ogm.annotation.Transient;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static net.olemartin.domain.Color.BLACK;
import static net.olemartin.domain.Color.WHITE;
@NodeEntity
public class Player implements Comparable<Player> {
@GraphId
private Long id;
private double score;
private String colors = "";
@Relationship(type = "MET", direction = Relationship.UNDIRECTED)
private Set<Player> playersMet = new HashSet<>();
@Transient
private Player newOpponent;
@Transient
private String roundResults;
private double monrad2;
private double monrad1;
private double monrad;
private double berger;
private int tournamentRank;
@Relationship(type = "IS_PLAYER", direction = Relationship.INCOMING)
private Person person;
@Relationship(type = "PLAYS_IN", direction = Relationship.OUTGOING)
private Tournament tournament;
private boolean walkover;
private Player() {
}
public Player(String name) {
this(new Person(name));
}
public Player(long id, String name) {
this(new Person(name));
this.id = id;
}
public Player(Person person) {
this.person = person;
person.addPlayer(this);
}
public void setMonradAndBerger(Iterable<Player> wonIterable, Iterable<Player> remisIterable) {
setBerger(wonIterable, remisIterable);
LinkedList<Player> opponents = getOpponents();
if (opponents.size() >= 2) {
monrad = sumOpponentPoints(opponents);
opponents.removeLast();
monrad1 = sumOpponentPoints(opponents);
opponents.removeLast();
monrad2 = sumOpponentPoints(opponents);
}
}
public boolean hasPlayedMatches() {
return !playersMet.isEmpty();
}
private LinkedList<Player> getOpponents() {
LinkedList<Player> opponents = new LinkedList<>();
opponents.addAll(playersMet.stream().sorted().collect(toList()));
return opponents;
}
private void setBerger(Iterable<Player> wonIterable, Iterable<Player> remisIterable) {
final Set<Player> remis = new HashSet<>();
final Set<Player> won = new HashSet<>();
remisIterable.forEach(remis::add);
wonIterable.forEach(won::add);
berger = sumOpponentPoints(won) + (sumOpponentPoints(remis) / 2.0);
}
public void setRoundScore(Iterable<Match> matches, Iterable<Player> players) {
List<Player> allPlayers = new ArrayList<>();
allPlayers.addAll(Lists.newArrayList(players).stream().collect(toList()));
Collections.sort(allPlayers);
List<String> result = new ArrayList<>();
matches.forEach(match -> {
if (match.hasResult()) {
if (match.getResult() == Result.WALKOVER) {
result.add("WO");
} else {
Player opponent = getOpponent(match);
int position = allPlayers.indexOf(opponent) + 1;
if (match.getResult() == Result.REMIS) {
result.add("=" + position);
} else if (match.getWinner().id.equals(id)) {
result.add("+" + position);
} else {
result.add("-" + position);
}
}
} else {
result.add("?");
}
});
roundResults = result.stream().collect(joining(", "));
}
private Player getOpponent(Match match) {
return match.getPlayers()
.stream()
.filter(player -> !player.id.equals(id)).findFirst().get();
}
private Double sumOpponentPoints(Collection<Player> players) {
return players
.stream()
.map(player -> player.score).reduce((d, d2) -> d + d2).orElse(0.0);
}
public Player increaseScore(double score) {
this.score += score;
return this;
}
public String getName() {
return person != null ? person.getName() : "no-name";
}
public void countRound(Color color, Player otherPlayer) {
this.newOpponent = otherPlayer;
playersMet.add(otherPlayer);
addColor(color);
}
public void countWalkover() {
this.walkover = true;
addColor(Color.WHITE);
}
private void addColor(Color color) {
if (colors == null) {
colors = "";
}
colors += color.name() + ":";
}
private long numberOfRounds(Color color) {
return asLinkedList().stream().filter(c -> c == color).count();
}
public Color nextOptimalColor() {
long blacks = numberOfRounds(BLACK);
long white = numberOfRounds(WHITE);
if (blacks == 0 && white == 0) {
return WHITE;
} else if (blacks > white) {
return WHITE;
} else if (white > blacks) {
return BLACK;
} else {
return asLinkedList().getLast().getOther();
}
}
private LinkedList<Color> asLinkedList() {
if (StringUtils.isNotEmpty(colors)) {
return new LinkedList<>(Arrays.stream(
colors.split(":"))
.map(Color::valueOf)
.collect(toList()));
} else {
return new LinkedList<>();
}
}
public boolean hasMet(Player otherPlayer) {
return playersMet.contains(otherPlayer);
}
public Color mustHaveColor() {
LinkedList<Color> matches = asLinkedList();
if (matches.size() >= 2) {
if (matches.get(matches.size() - 2) == matches.getLast()) {
return matches.getLast().getOther();
} else {
long blacks = numberOfRounds(BLACK);
long white = numberOfRounds(WHITE);
if (blacks - white >= 2) {
return WHITE;
} else if (white - blacks >= 2) {
return BLACK;
}
}
}
return null;
}
@Override
public String toString() {
return "Player{" +
" id=" + id +
", score=" + score +
", name='" + getName() + '\'' +
", met='" + playersMet.stream().map(p -> String.valueOf(p.getName())).collect(Collectors.joining(",")) + '\'' +
", colors='" + asLinkedList() + '\'' +
", must='" + mustHaveColor() + '\'' +
'}';
}
public double getScore() {
return score;
}
public void removeLastOpponent() {
playersMet.remove(newOpponent);
LinkedList<Color> matches = asLinkedList();
matches.removeLast();
colors = matches.stream().map(Enum::name).collect(joining(":", "", ":"));
}
@Override
public int compareTo(Player p2) {
double score1 = getScore();
double score2 = p2.getScore();
if (score1 == score2) {
int monrad2 = (int) (1000.0 * (p2.monrad2 - this.monrad2));
int monrad1 = (int) (1000.0 * (p2.monrad1 - this.monrad1));
int monrad = (int) (1000.0 * (p2.monrad - this.monrad));
int berger = (int) (1000.0 * (p2.berger - this.berger));
return
monrad2 == 0 ?
monrad1 == 0 ?
monrad == 0 ?
berger == 0 ?
getName().compareTo(p2.getName())
: berger
: monrad
: monrad1
: monrad2;
} else {
return (int) (1000.0 * (score2 - score1));
}
}
public Long getId() {
return id;
}
public Person getPerson() {
return person;
}
public void setTournamentRank(int tournamentRank) {
this.tournamentRank = tournamentRank;
}
public int getTournamentRank() {
return tournamentRank;
}
public Tournament getTournament() {
return tournament;
}
public boolean hasWalkover() {
return walkover;
}
public LinkedList<Color> getColorsAsList() {
return asLinkedList();
}
public double getMonrad() {
return monrad;
}
public double getMonrad1() {
return monrad1;
}
public double getMonrad2() {
return monrad2;
}
public static class PlayerSerializer implements JsonSerializer<Player> {
@Override
public JsonElement serialize(Player player, Type typeOfSrc, JsonSerializationContext context) {
JsonObject root = new JsonObject();
root.addProperty("id", player.id);
root.addProperty("name", player.getName());
root.addProperty("score", player.score);
root.addProperty("roundResults", player.roundResults);
root.addProperty("monrad", player.monrad);
root.addProperty("monrad1", player.monrad1);
root.addProperty("monrad2", player.monrad2);
root.addProperty("berger", player.berger);
return root;
}
}
}