gt:                             observers.forEach(o -> o.jokerLetterChosen(letter, player));
context: /*
   * Copyright (C) 2016 Gergely Kadar
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   */
  package hu.unideb.kg.socotra.model;
  
  import java.io.FileNotFoundException;
  import java.io.InputStreamReader;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.StringJoiner;
  import java.util.Timer;
  import java.util.TimerTask;
  import java.util.concurrent.atomic.AtomicInteger;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   *
   * @author Gergely Kadar
   */
  public class GameManager {
  
      private static final Logger LOGGER = LoggerFactory.getLogger(GameManager.class);
  
      /**
       * The possible actions a player can perform during his/her turn.
       */
      public enum TurnAction {
          /**
           * The player played one or more words.
           */
          PLAY,
          /**
           * The player swapped letters.
           */
          SWAP,
          /**
           * The player passed his/her turn.
           */
          PASS
      }
  
      public class PlayedWord {
  
          private int turn;
          private String playerName;
          private String word;
          private int score;
  
          public PlayedWord(int turn, String playerName, String word, int score) {
              this.turn = turn;
              this.playerName = playerName;
              this.word = word;
              this.score = score;
          }
  
          public int getTurn() {
              return turn;
          }
  
          public void setTurn(int turn) {
              this.turn = turn;
          }
  
          public String getPlayerName() {
              return playerName;
          }
  
          public void setPlayerName(String playerName) {
              this.playerName = playerName;
          }
  
          public String getWord() {
              return word;
          }
  
          public void setWord(String word) {
              this.word = word;
          }
  
          public int getScore() {
              return score;
          }
  
          public void setScore(int score) {
              this.score = score;
          }
  
      }
  
      public class ThinkingTimeCountdownTimer extends TimerTask {
  
          AtomicInteger remainingSeconds = new AtomicInteger(maxThinkingTime);
          boolean timeExtended = false;
  
          @Override
          public void run() {
              int currentValue = remainingSeconds.decrementAndGet();
              observers.stream().forEach(o -> o.updateTimer(currentValue));
              if (currentValue == 0) {
                  thinkingTimeOver();
              }
          }
  
          public void extendTime() {
              if (!timeExtended) {
                  timeExtended = true;
                  remainingSeconds.addAndGet(extensionsLength);
              }
          }
  
          public void reset() {
              remainingSeconds.set(maxThinkingTime);
          }
      }
  
      private Players players;
      private boolean boardLegal;
      private boolean movableTiles;
      private boolean wordsCorrect;
      private int turn;
      private int numOfPasses;
      private List<PlayedWord> playedWords;
  
      private int maxThinkingTime;
      private int maxExtensions;
      private int extensionsLength;
      private ThinkingTimeCountdownTimer thinkingTimeCountdownTimer;
  
      private static final String REDRAW = "<csere>";
      private static final String PASS = "<passz>";
  
      private GameBoard gameBoard;
      private Bag bag;
      private long bagSeed;
      private Dictionary dictionary;
  
      private List<GameObserver> observers;
  
      public GameManager(InputStreamReader inputStream, Players players)
              throws FileNotFoundException {
          this.gameBoard = new GameBoard();
          this.observers = new ArrayList<>();
          this.players = players;
          this.bag = new Bag();
          this.dictionary = new Dictionary(inputStream);
          this.playedWords = new ArrayList<>();
      }
  
      public void addObserver(GameObserver observer) {
          observers.add(observer);
      }
  
      public void addObservers(List<GameObserver> observers) {
          this.observers = observers;
      }
  
      public void gameWindowReady() {
          startGame(bagSeed);
      }
  
      private void startGame(long bagSeed) {
          boardLegal = false;
          movableTiles = false;
          wordsCorrect = false;
          turn = 1;
          numOfPasses = 0;
  
          this.bagSeed = bagSeed;
          bag.shuffle(bagSeed);
  
          for (int i = 0; i < Tray.TRAY_SIZE; i++) {
              for (Player player : players.getPlayersList()) {
                  player.drawTile(bag);
              }
          }
  
          observers.forEach(o -> o.gameStarted(players.getCurrentPlayer()));
  
          if (thinkingTimeLimitExists()) {
              new Timer().scheduleAtFixedRate(thinkingTimeCountdownTimer, 0, 1000);
          }
      }
  
      public boolean alterBoard(int row, int col, Player player) {
          boolean boardAltered = false;
          if (players.getCurrentPlayer() == player) {
              if (player.getTileInHand() == null) {
                  Tile pickedUpTile = gameBoard.pickUpTile(row, col);
                  if (pickedUpTile != null) {
                      if (pickedUpTile.isJoker()) {
                          pickedUpTile.resetJokerLetter();
                      }
                      player.setTileInHand(pickedUpTile);
                      boardAltered = true;
                  }
              } else {
                  if (player.getTileInHand().isJoker() && !player.getTileInHand().jokerLetterSpecified()) {
                      for (GameObserver obs : observers) {
                          String letter = obs.jokerLetterRequested(player);
                          if (letter != null) {
                              player.getTileInHand().setJokerLetter(letter);
  