package eric.Roullette.controller; // import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import io.javalin.Javalin; import io.javalin.http.Context; import eric.Roullette.service.GameService; import eric.Roullette.service.SpotifyAuthService; import eric.Roullette.websocket.GameWebSocketHandler; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.MediaType; // import com.fasterxml.jackson.databind.node.JsonNodeFactory; // import com.fasterxml.jackson.databind.node.ObjectNode; // import org.slf4j.Logger; // import org.slf4j.LoggerFactory; import java.awt.*; // import java.io.IOException; // import java.util.List; import java.util.Map; // import java.util.Objects; // import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; // import java.util.concurrent.TimeUnit; public class GameController { private final GameService gameService; private final SpotifyAuthService authService; private final GameWebSocketHandler webSocketHandler; private final OkHttpClient httpClient = new OkHttpClient(); private String accessToken = ""; private Map userAccessTokens = new ConcurrentHashMap<>(); // Map private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GameController.class); public GameController(Javalin app, GameService gs, SpotifyAuthService sas, GameWebSocketHandler wsHandler) { this.gameService = gs; this.authService = sas; this.webSocketHandler = wsHandler; app.post("/api/create-game", this::createGame); app.post("/api/join-game", this::joinGame); app.get("/api/game/{gameId}/players", this::getPlayers); app.post("/api/game/{gameId}/start-round", this::startRound); app.post("/api/game/{gameId}/guess", this::guess); app.post("/api/spotify/play", this::playTrack); app.get("/api/spotify/devices", ctx -> { String username = ctx.queryParam("username"); if (username == null) { ctx.status(400).result("username fehlt"); return; } // spieler in map hinzufügen String playerAccessToken = null; if(!userAccessTokens.containsKey(username)) { playerAccessToken = authService.getAccessTokenForUser(username); if (playerAccessToken != null) { userAccessTokens.put(username, playerAccessToken); } } if (accessToken == null) { ctx.status(401).result("Zugriffstoken fehlt oder ist ungültig"); return; } var devices = authService.getDevices(accessToken); ctx.json(devices); }); } @SuppressWarnings("unchecked") private void createGame(Context ctx) throws InterruptedException { Map body = (Map) ctx.bodyAsClass(Map.class); String user = (String) body.get("username"); if (user == null || user.isBlank()) { ctx.status(400).result("username fehlt"); return; } String gameId; do { gameId = String.format("%04d", ThreadLocalRandom.current().nextInt(0, 10000)); } while (gameService.gameExists(gameId)); gameService.createGame(gameId); gameService.addPlayer(gameId, user); gameService.broadcastPlayers(gameId); ctx.json(Map.of("status", "ok", "gameId", gameId)); } private void joinGame(Context ctx) throws InterruptedException { Map body = ctx.bodyAsClass(Map.class); String user = body.get("username"); String gameId = body.get("gameId"); if (user == null || gameId == null) { ctx.status(400).result("username oder gameId fehlt"); return; } gameService.addPlayer(gameId, user); gameService.broadcastPlayers(gameId); ctx.json(Map.of("status", "ok")); } private void getPlayers(Context ctx) { String gameId = ctx.pathParam("gameId"); var game = gameService.getOrCreateGame(gameId); ctx.json(Map.of( "players", game.players() )); } private void startRound(Context ctx) { String gameId = ctx.pathParam("gameId"); ctx.json(Map.of("status", "ok")); webSocketHandler.broadcastRoundStart(gameId); } private void guess(Context ctx) { String gameId = ctx.pathParam("gameId"); Map body = ctx.bodyAsClass(Map.class); String guess = body.get("guess"); String user = body.get("username"); GameService.Game game = gameService.getOrCreateGame(gameId); String owner = game.currentOwner(); if (owner == null || guess == null) { ctx.status(400).result("ungültig"); return; } boolean correct = guess.equals(owner); if (correct) game.scores().merge(user, 1, Integer::sum); ctx.json(Map.of( "correct", correct, "owner", owner, "scores", game.scores() )); } // Java private void playTrack(Context ctx) { Map body = ctx.bodyAsClass(Map.class); String username = body.get("username"); String deviceId = body.get("device_id"); String trackUri = body.get("track_uri"); if (username == null || deviceId == null || trackUri == null) { ctx.status(400).result("Fehlende Parameter: username, device_id oder track_uri"); return; } try { //String accessToken = authService.getAccessTokenForUser(username); String accessToken = userAccessTokens.get(username); OkHttpClient client = httpClient; String trackId = trackUri.split(":")[2]; Request getTrack = new Request.Builder() .url("https://api.spotify.com/v1/tracks/" + trackId) .addHeader("Authorization", "Bearer " + accessToken) .build(); try (Response trackResp = client.newCall(getTrack).execute()) { if (!trackResp.isSuccessful()) { //ctx.status(trackResp.code()).result("Fehler beim Laden der Track-Details: " + trackResp.body().string()); return; } assert trackResp.body() != null; var node = new com.fasterxml.jackson.databind.ObjectMapper().readTree(trackResp.body().string()); long durationMs = node.get("duration_ms").asLong(); long startOffset = durationMs / 2; ObjectNode jsonNode = JsonNodeFactory.instance.objectNode(); jsonNode.putArray("uris").add(trackUri); jsonNode.put("position_ms", startOffset); String jsonBody = jsonNode.toString(); Request playReq = new Request.Builder() .url("https://api.spotify.com/v1/me/player/play?device_id=" + deviceId) .addHeader("Authorization", "Bearer " + accessToken) .put(RequestBody.create(jsonBody, MediaType.parse("application/json"))) .build(); try (Response playResp = client.newCall(playReq).execute()) { // ctx.status(playResp.code()); // ctx.header("Retry-After", playResp.header("Retry-After") != null ? playResp.header("Retry-After") : ""); // ctx.header("x-rate-limit-limit", playResp.header("x-rate-limit-limit")); // ctx.header("x-rate-limit-remaining", playResp.header("x-rate-limit-remaining")); // ctx.header("x-rate-limit-reset", playResp.header("x-rate-limit-reset")); // ctx.result(playResp.body().string()); if (playResp.isSuccessful()) { ctx.status(204).result("Track erfolgreich abgespielt"); } else { //ctx.status(playResp.code()).result("Fehler beim Abspielen: " + playResp.body().string()); System.out.println("Fehler beim Abspielen des Tracks"); } } } } catch (Exception e) { log.error("Fehler beim Abspielen des Tracks", e); ctx.status(500).result("Interner Fehler: " + e.getMessage()); } } }