@Override
  public ICard getCard(String gameId, String playerId) {

    IGame game = getGame(gameId);
    int cardId = game.getCardId(playerId);

    return cards[cardId];
  }
  @Override
  public String createNewGame(String player1Id, String player2Id) {

    IGame game = createGame(player1Id, player2Id);
    Logger.info(
        String.format(
            "Player %s and %s has started a new multi player game.", player1Id, player2Id));

    return game.getGameID();
  }
  /** Creates new game for a given player and returns the gameId as String */
  @Override
  public String createNewGame(String playerId) {

    PlayerAI cp = new SimplePlayerAI(UUID.randomUUID().toString());
    Logger.info(String.format("Player %s has started a new single player game.", playerId));
    IGame game = createGame(playerId, cp.getId());
    game.addObserver(cp);
    game.notifyObservers();

    return game.getGameID();
  }
  @Override
  public void commitRound(String gameId, String playerId) {
    try {
      IGame game = getGame(gameId);
      Logger.info(String.format("Player %s has committed the round.", playerId));
      game.commitRound(playerId);

    } catch (IllegalStateException ex) {
      Logger.error(ex.toString());
    }
  }
  @Override
  public void makeMove(String gameId, String playerId, int categoryID) {

    try {
      IGame game = getGame(gameId);
      Logger.info(String.format("Player %s has choosen category %d.", playerId, categoryID));
      game.chooseCategory(playerId, categoryID);
      tryEvaluateRound(game);

    } catch (IllegalStateException ex) {
      Logger.error(ex.getMessage());
    }
  }
  @Override
  public void commitCard(String gameId, String playerId) {

    try {
      IGame game = getGame(gameId);
      Logger.info(String.format("Player %s has committed his card.", playerId));
      game.commitCard(playerId);
      tryEvaluateRound(game);

    } catch (IllegalStateException ex) {
      Logger.error(ex.getMessage());
    }
  }
  @Override
  public ICard getCardFromCompetitor(String gameId, String playerId) throws IllegalStateException {

    IGame game = getGame(gameId);

    String currentState = game.getGameStatus(playerId).getGameState();
    List<String> expectedStates = new LinkedList<>();
    expectedStates.add(GameState.WaitForCommitRound.toString());
    expectedStates.add(GameState.Aborted.toString());

    if (!expectedStates.contains(currentState))
      throw new IllegalStateException(
          "The competitors card is only accessible in state WaitForCommit.");

    int cardId = game.getCompetitorCardId(playerId);
    return cards[cardId];
  }
  /** Tries to create a multi player game if at least two players are waiting */
  private void tryCreateMultiPlayerGame() {

    Logger.info(
        String.format(
            "Try to create a new multi player game. Queue length: %d", waitingPlayers.size()));
    if (waitingPlayers.size() >= 2) {
      String p1 = waitingPlayers.poll();
      String p2 = waitingPlayers.poll();

      IGame game = createGame(p1, p2);
      String gid = game.getGameID();

      games.put(gid, game);
      mpGames.put(p1, gid);
      mpGames.put(p2, gid);

      Logger.info(String.format("Multi player game %s created.", gid));
    }
  }
  @Override
  public void abortGame(String gameId, String playerId) {

    try {
      IGame game = getGame(gameId);

      Logger.info(String.format("Player %s has aborted game %s.", playerId, gameId));
      game.setAborted(playerId);

      // Delete the game if both players have aborted.
      if (game.isFinished()) {
        games.remove(game.getGameID());
        Logger.info(String.format("Game %s has been removed.", gameId));
      } else {
        game.notifyObservers();
      }

    } catch (UnknownPlayerException ex) {
      Logger.error(ex.toString());
    }
  }
  /**
   * Tries to evaluate the current round in the game
   *
   * @param game the game which should be evaluated
   */
  private void tryEvaluateRound(IGame game) {
    if (game.canEvaluateRound()) {

      String p1 = game.getActivePlayer();
      String p2 = game.getPassivePlayer();
      int category = game.getGameStatus(p1).getChoosenCategoryId();
      ICard c1 = cards[game.getCardId(p1)];
      ICard c2 = cards[game.getCardId(p2)];
      Integer rank1 = c1.getRankingArray()[category];
      Integer rank2 = c2.getRankingArray()[category];

      if (rank1 < rank2) {
        game.setWinner(p1);
      } else {
        game.setWinner(p2);
      }
    }
  }
  @Override
  public GameStatus getGameStatus(String gameId, String playerId) {

    IGame game = getGame(gameId);
    return game.getGameStatus(playerId);
  }