package models;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.annotation.Where;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.base.Predicate;
import org.apache.commons.collections.map.MultiKeyMap;
import play.db.ebean.Model;
import javax.annotation.Nullable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.avaje.ebean.Expr.eq;
import static com.fasterxml.jackson.databind.node.JsonNodeFactory.instance;
import static com.google.common.collect.Collections2.filter;
import static javax.persistence.CascadeType.ALL;
import static javax.persistence.CascadeType.PERSIST;
@Entity
public class Game extends BaseModel<Game> implements Comparable<Game> {
public static Finder<Long, Game> finder = new Model.Finder(Long.class, Game.class);
public String thread;
public String cover;
public String title;
@OneToMany(mappedBy = "game")
@Where(clause = "rank > 0")
public List<Score> scores;
@OneToMany(mappedBy = "game")
public List<Score> allScores;
@OneToMany(mappedBy = "game")
@Where(clause = "onecc = true")
public List<Score> oneccs;
@OrderBy("name")
@OneToMany(mappedBy = "game", cascade = PERSIST)
public List<Platform> platforms;
@OrderBy("sortOrder")
@OneToMany(mappedBy = "game", cascade = PERSIST)
public List<Difficulty> difficulties;
@OrderBy("sortOrder")
@OneToMany(mappedBy = "game", cascade = PERSIST)
public List<Mode> modes;
@OrderBy("sortOrder")
@OneToMany(mappedBy = "game", cascade = PERSIST)
public List<Ship> ships;
@OrderBy("sortOrder")
@OneToMany(mappedBy = "game", cascade = PERSIST)
public List<Stage> stages;
@OneToOne(mappedBy = "game", cascade = ALL)
public Event event;
private boolean generalRanking;
public Game(String title, String cover, String thread) {
this.title = title;
this.cover = cover;
this.thread = thread;
}
public static List<Game> findAll() {
return Ebean.find(Game.class).order("title").findList();
}
public List<Ranking> rankings() {
List<Ranking> rankings = new ArrayList<Ranking>();
if (generalRanking) {
rankings.add(createGeneralRanking());
}
if (modes.isEmpty()) {
if (difficulties.isEmpty()) {
rankings.add(createGeneralRanking());
} else {
for (Difficulty difficulty : difficulties) {
rankings.add(new Ranking(findBestScoresByVIPPlayers(difficulty, null), difficulty));
}
}
} else {
for (Mode mode : modes) {
if (difficulties.isEmpty()) {
rankings.add(new Ranking(findBestScoresByVIPPlayers(null, mode), mode));
} else {
for (Difficulty difficulty : difficulties) {
rankings.add(new Ranking(findBestScoresByVIPPlayers(difficulty, mode), difficulty, mode));
}
}
}
}
return rankings;
}
private Ranking createGeneralRanking() {
Ranking ranking = new Ranking(findBestScoresByVIPPlayers());
List<Score> scores = new ArrayList<Score>();
for (int rank = 0; rank < ranking.scores.size(); rank++) {
Score score = ranking.scores.get(rank);
scores.add(new Score(score.id, score.game, score.player, score.stage, score.ship, score.mode, score.difficulty, score.comment, score.platform, score.value, score.photo, score.replay, rank + 1));
}
Ranking ranking1 = new Ranking(scores);
ranking1.general = true;
return ranking1;
}
public Collection<Score> findBestScoresByVIPPlayers(final Difficulty difficulty, final Mode mode) {
List<Score> scores = Score.finder.
fetch("mode").
fetch("difficulty").
fetch("player").
where().conjunction().
add(eq("game", this)).
add(eq("difficulty", difficulty)).
add(eq("mode", mode)).
orderBy((mode == null || !mode.isTimerScore()) ? "value desc" : "value").findList();
return keepBestScoreByVIPPlayer(scores);
}
private Collection<Score> findBestScoresByVIPPlayers() {
if (scores == null) {
return new ArrayList<Score>();
}
List<Score> scores = Score.finder.
where().conjunction().
add(eq("game", this)).
orderBy("value desc").findList();
return keepBestScoreByVIPPlayer(scores);
}
private Collection<Score> keepBestScoreByVIPPlayer(List<Score> scores) {
final Set<Player> players = new HashSet<Player>();
return filter(scores, new Predicate<Score>() {
@Override
public boolean apply(@Nullable Score score) {
if (players.contains(score.player)) {
return false;
}
if (!score.player.isVip()) {
return false;
}
players.add(score.player);
return true;
}
});
}
public String post() {
return thread.replace("viewtopic.php?", "posting.php?mode=reply&f=20&");
}
@Override
public String toString() {
return title;
}
public String getEscapedTitle() {
String s = title.replaceAll("[^a-zA-Z0-9]", "_");
s = s.replaceAll("_(_)*", "_");
if (s.endsWith("_")) {
s = s.substring(0, s.length() - 1);
}
return s;
}
public void recomputeRankings() {
for (Score score : allScores) {
score.updateRank(null);
score.update();
}
rankings();
}
public String getCoverType() {
if (cover.endsWith("jpg") || cover.endsWith("jpeg")) {
return "image/jpeg";
}
if (cover.endsWith("png")) {
return "image/png";
}
return "image/gif";
}
public Collection<Player> getPlayers() {
Set<Player> players = new HashSet<Player>();
for (Score score : scores) {
players.add(score.player);
}
return players;
}
@Override
public int compareTo(Game game) {
return this.title.compareTo(game.title);
}
public boolean hasShip() {
return ships != null && !ships.isEmpty();
}
public boolean hasDifficulties() {
return difficulties != null && !difficulties.isEmpty();
}
public boolean hasModes() {
return modes != null && !modes.isEmpty();
}
public int getOneCreditCount() {
Collection<Score> oneCreditScores = filter(allScores, new Predicate<Score>() {
@Override
public boolean apply(@Nullable Score score) {
return score.onecc;
}
});
Set<String> uniqueOneCreditScores = new HashSet<String>();
for (Score oneCreditScore : oneCreditScores) {
String player = oneCreditScore.player.name;
String mode = oneCreditScore.modeName();
String difficulty = oneCreditScore.difficultyName();
uniqueOneCreditScores.add(player + mode + difficulty);
}
return uniqueOneCreditScores.size();
}
public boolean hasTimerScores() {
if (this.modes == null || this.modes.isEmpty()) {
return false;
}
for (Mode mode : modes) {
if (mode.isTimerScore()) {
return true;
}
}
return false;
}
public Collection<Score> getAllOneCCS() {
MultiKeyMap map = new MultiKeyMap();
for (Score onecc : this.oneccs) {
Score score = (Score) map.get(onecc.player, onecc.mode, onecc.difficulty);
if (score == null) {
map.put(onecc.player, onecc.mode, onecc.difficulty, onecc);
} else if (score.isWorstThan(onecc)) {
map.put(onecc.player, onecc.mode, onecc.difficulty, onecc);
}
}
List<Score> scores = new ArrayList<Score>(map.values());
Collections.sort(scores, new Comparator<Score>() {
@Override
public int compare(Score o1, Score o2) {
return o1.player.name.compareToIgnoreCase(o2.player.name);
}
});
return scores;
}
public ObjectNode json() {
ObjectNode node = new ObjectNode(instance);
node.set("id", new LongNode(id));
node.set("title", new TextNode(title));
node.set("cover", new TextNode(cover));
return node;
}
public JsonNode jsonDetail() {
ObjectNode json = json();
json.set("platforms", jsonPlatforms());
json.set("stages", jsonStages());
json.set("modes", jsonModes());
json.set("difficulties", jsonDifficulties());
json.set("ships", jsonShips());
json.set("rankings", jsonRankings());
return json;
}
private ArrayNode jsonRankings() {
ArrayNode nodes = new ArrayNode(instance);
for (Ranking item : rankings()) {
if (item.isNotEmpty()) {
nodes.add(item.json());
}
}
return nodes;
}
private ArrayNode jsonStages() {
ArrayNode nodes = new ArrayNode(instance);
for (Stage item : stages) {
nodes.add(item.json());
}
return nodes;
}
private ArrayNode jsonPlatforms() {
ArrayNode nodes = new ArrayNode(instance);
for (Platform item : platforms) {
nodes.add(item.json());
}
return nodes;
}
private ArrayNode jsonModes() {
ArrayNode nodes = new ArrayNode(instance);
for (Mode item : modes) {
nodes.add(item.json());
}
return nodes;
}
private ArrayNode jsonDifficulties() {
ArrayNode nodes = new ArrayNode(instance);
for (Difficulty item : difficulties) {
nodes.add(item.json());
}
return nodes;
}
private ArrayNode jsonShips() {
ArrayNode nodes = new ArrayNode(instance);
for (Ship item : ships) {
nodes.add(item.json());
}
return nodes;
}
public boolean hasStages() {
return stages != null && !stages.isEmpty();
}
public boolean hasPlatforms() {
return platforms != null && !platforms.isEmpty();
}
}