package tc.oc.commons.bukkit.ticket;
import java.time.Duration;
import java.util.Collections;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.google.common.cache.LoadingCache;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.bukkit.users.OnlinePlayers;
import tc.oc.api.docs.Arena;
import tc.oc.api.docs.Game;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.Ticket;
import tc.oc.api.docs.virtual.MatchDoc;
import tc.oc.api.games.ArenaStore;
import tc.oc.api.games.GameStore;
import tc.oc.api.games.TicketStore;
import tc.oc.api.model.ModelDispatcher;
import tc.oc.api.model.ModelListener;
import tc.oc.api.servers.ServerStore;
import tc.oc.commons.bukkit.bossbar.BossBarFactory;
import tc.oc.commons.bukkit.chat.Audiences;
import tc.oc.commons.bukkit.format.GameFormatter;
import tc.oc.commons.bukkit.util.PlayerStates;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.commons.core.util.CacheUtils;
import tc.oc.minecraft.api.scheduler.Tickable;
/**
* Displays the current state of {@link Game} queues to the {@link Player}s in each queue.
*/
@Singleton
public class TicketDisplay implements ModelListener, Listener, PluginFacet, Tickable {
private final BukkitUserStore userStore;
private final OnlinePlayers players;
private final PlayerStates playerStates;
private final Audiences audiences;
private final GameFormatter gameFormatter;
private final Server localServer;
private final ServerStore servers;
private final TicketStore tickets;
private final ArenaStore arenas;
private final GameStore games;
private final LoadingCache<Arena, BossBar> bars;
@Inject TicketDisplay(BukkitUserStore userStore, OnlinePlayers players, PlayerStates playerStates, Audiences audiences, GameFormatter gameFormatter, Server localServer, BossBarFactory bossBarFactory, ServerStore servers, TicketStore tickets, ArenaStore arenas, GameStore games, ModelDispatcher modelDispatcher) {
this.userStore = userStore;
this.players = players;
this.playerStates = playerStates;
this.audiences = audiences;
this.gameFormatter = gameFormatter;
this.localServer = localServer;
this.servers = servers;
this.tickets = tickets;
this.arenas = arenas;
this.games = games;
this.bars = CacheUtils.newCache(game -> bossBarFactory.createRenderedBossBar());
modelDispatcher.subscribe(this);
}
private void updateArena(Arena arena) {
final Game game = games.byId(arena.game_id());
int minPlayers = 0;
if(arena.next_server_id() != null) {
minPlayers = servers.byId(arena.next_server_id()).min_players();
}
final BaseComponent text;
final double progress;
if(minPlayers > 0 && arena.num_queued() < minPlayers) {
text = gameFormatter.queued(game, minPlayers - arena.num_queued());
progress = (double) arena.num_queued() / (double) minPlayers;
} else {
text = gameFormatter.joining(game);
progress = 1;
}
bars.getUnchecked(arena).update(text, progress, BarColor.YELLOW, BarStyle.SOLID, Collections.emptySet());
}
@HandleModel
public void arenaUpdated(@Nullable Arena before, @Nullable Arena after, Arena latest) {
updateArena(latest);
}
@HandleModel
public void ticketUpdated(@Nullable Ticket before, @Nullable Ticket after, Ticket latest) {
final Arena arena = arenas.byId(latest.arena_id());
updateArena(arena);
final Player player = userStore.find(latest.user());
if(player != null) {
final BossBar bar = bars.getUnchecked(arena);
if(after != null && after.server_id() == null) {
bar.addPlayer(player);
} else {
bar.removePlayer(player);
}
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onJoin(PlayerJoinEvent event) {
final Ticket ticket = tickets.tryUser(userStore.getUser(event.getPlayer()));
if(ticket != null && ticket.server_id() == null) {
bars.getUnchecked(arenas.byId(ticket.arena_id())).addPlayer(event.getPlayer());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent event) {
final Ticket ticket = tickets.tryUser(userStore.getUser(event.getPlayer()));
if(ticket != null && ticket.server_id() == null) {
bars.getUnchecked(arenas.byId(ticket.arena_id())).removePlayer(event.getPlayer());
}
}
@Override
public Duration tickPeriod() {
return Duration.ofSeconds(1);
}
@Override
public void tick() {
final MatchDoc match = localServer.current_match();
if(match != null && match.start() != null && !match.join_mid_match() && match.end() == null) {
players.stream()
.filter(playerStates::isObserving)
.forEach(player -> {
final Ticket ticket = tickets.tryUser(userStore.tryUser(player));
if(ticket != null) {
final Game game = games.byId(arenas.byId(ticket.arena_id()).game_id());
if(game != null) {
audiences.get(player).sendHotbarMessage(gameFormatter.replayMaybe(game));
}
}
});
}
}
}