package org.royaldev.thehumanity.server.controllers; import org.kitteh.irc.client.library.Client; import org.kitteh.irc.client.library.element.Channel; import org.royaldev.thehumanity.cards.packs.CAHCardPack; import org.royaldev.thehumanity.cards.play.Play; import org.royaldev.thehumanity.cards.types.WhiteCard; import org.royaldev.thehumanity.game.HouseRule; import org.royaldev.thehumanity.game.TheHumanityGame; import org.royaldev.thehumanity.game.round.CAHRound.RoundState; import org.royaldev.thehumanity.game.round.CurrentRound; import org.royaldev.thehumanity.player.TheHumanityPlayer; import org.royaldev.thehumanity.server.services.client.ClientService; import org.royaldev.thehumanity.server.services.game.GameService; import org.royaldev.thehumanity.server.services.server.ServerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import xyz.cardstock.cardstock.configuration.Server; import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @Controller public class GameController { @Autowired private ServerService serverService; @Autowired private ClientService clientService; @Autowired private GameService gameService; @ResponseBody @RequestMapping(value = "/api/game/{server}/{channel}", method = RequestMethod.GET, produces = APIHelper.PRODUCES) public String apiViewGame(@PathVariable String server, @PathVariable String channel, final HttpServletResponse response) { final Client client = this.clientService.getFromHostname(server); final TheHumanityGame g = this.gameService.getFromChannelName(client, "#" + channel); if (g == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return APIHelper.makeError("No such game."); } // TODO: Use toJSON()? return APIHelper.makeJSON(jw -> { jw .object() .key("channel") .value(g.getChannel().getName()) .key("players") .value(g.getPlayers().stream().map(p -> p.getUser().getNick()).collect(Collectors.toList())) .key("historicPlayers") .value(g.getHistoricPlayers().stream().map(p -> p.getUser().getNick()).collect(Collectors.toList())) .key("host") .value(g.getHost().getUser().getNick()) .key("scores") .value(g.getHistoricPlayers().stream().collect(Collectors.toMap(p -> p.getUser().getNick(), TheHumanityPlayer::getScore))) .key("houseRules") .value(g.getHouseRules().stream().map(HouseRule::getFriendlyName).collect(Collectors.toList())) .key("cardPacks") .value(g.getDeck().getCardPacks().stream().map(CAHCardPack::getName).collect(Collectors.toList())) .key("state") .value(g.getState().getUniqueName()) .key("timestamps") .object() .key("started") .value(g.getStartTime()) .endObject() .key("currentRound"); final CurrentRound round = g.getCurrentRound(); if (round == null) { jw.value(null); } else { final List<List<String>> plays = !round.getState().getUniqueName().equals(RoundState.WAITING_FOR_CZAR.name()) ? null : round.getPlays().stream() .map(Play::getWhiteCards) // Convert each Play to a List<WhiteCard> .map( list -> list.stream() // Stream each WhiteCard .map(WhiteCard::getText) // Convert each WhiteCard to its text form .collect(Collectors.toList()) // Collect the Strings into a List<String> ) // Map the List<WhiteCard> to List<String> .collect(Collectors.toList()); // Collect the lists into a List<List<String>> // There is no need to shuffle, as these are only shown during the WAITING_FOR_CZAR stage, in which the // plays are shuffled automatically. Keeping them unshuffled allows for index detection. jw .object() .key("number") .value(round.getNumber()) .key("czar") .value(round.getCzar() == null ? null : round.getCzar().getUser().getNick()) .key("blackCard") .value(round.getBlackCard()) .key("stage") .value(round.getState()) .key("plays") .value(round.getState().getUniqueName().equals(RoundState.WAITING_FOR_CZAR.name()) ? plays : new String[0]) .key("skippedPlayers") .value(round.getSkippedPlayers().stream().map(p -> p.getUser().getNick()).collect(Collectors.toList())) .endObject(); } jw.endObject(); }); } @ResponseBody @RequestMapping(value = "/api/game/{server}/{channel}/{round}", method = RequestMethod.GET, produces = APIHelper.PRODUCES) public String apiViewGameRound(@PathVariable String server, @PathVariable String channel, @PathVariable int round, final HttpServletResponse response) { final Client client = this.clientService.getFromHostname(server); final TheHumanityGame g = this.gameService.getFromChannelName(client, "#" + channel); if (g == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return APIHelper.makeError("No such game"); } try { return g.getPreviousRounds().get(round - 1).toJSON(); } catch (final IndexOutOfBoundsException ex) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return APIHelper.makeError("No such round."); } } @ResponseBody @RequestMapping(value = "/api/games/{server}", method = RequestMethod.GET, produces = APIHelper.PRODUCES) public String apiViewGames(@PathVariable String server, final HttpServletResponse response) { final Client client = this.clientService.getFromHostname(server); return APIHelper.makeObjectMapperJSON(response, om -> om.writeValueAsString(this.gameService.getAllForClient(client).stream().map(TheHumanityGame::getChannel).map(Channel::getName).collect(Collectors.toList()))); } @RequestMapping(value = "/game/{server}/{channel}", method = RequestMethod.GET) public String gameInChannel(@PathVariable String server, @PathVariable String channel, final Model model) { final Client client = this.clientService.getFromHostname(server); final TheHumanityGame g = this.gameService.getFromChannelName(client, "#" + channel); if (g == null) { return "redirect:/"; } model.addAttribute("game", g); return "games/game"; } @RequestMapping(value = "/games", method = RequestMethod.GET) public String viewGames(final Model model) { final Map<Server, Set<TheHumanityGame>> games = this.clientService.getClients().stream() .collect(Collectors.toMap( client -> this.serverService.getServer(client), client -> this.gameService.getAllForClient(client) )); model.addAttribute("games", games); return "games/index"; } }