* @return Null.
     */
    public Element endTurn(ServerPlayer serverPlayer) {
        FreeColServer freeColServer = getFreeColServer();
        ServerGame game = getGame();
        ServerPlayer player = (ServerPlayer) game.getCurrentPlayer();

        if (serverPlayer != player) {
            throw new IllegalArgumentException("It is not "
                + serverPlayer.getName() + "'s turn, it is "
                + ((player == null) ? "noone" : player.getName()) + "'s!");
        }

        for (;;) {
            logger.finest("Ending turn for " + player.getName());
            player.clearModelMessages();

            // Has anyone won?
            // Do not end single player games where an AI has won,
            // that would stop revenge mode.
            Player winner = game.checkForWinner();
            if (winner != null
                && !(freeColServer.isSinglePlayer() && winner.isAI())) {
                ChangeSet cs = new ChangeSet();
                cs.addTrivial(See.all(), "gameEnded",
                              ChangePriority.CHANGE_NORMAL,
                              "winner", winner.getId());
                sendToOthers(serverPlayer, cs);
                return cs.build(serverPlayer);
            }

            // Are there humans left?
            // TODO: see if this can be relaxed so we can run large
            // AI-only simulations.
            boolean human = false;
            for (Player p : game.getPlayers()) {
                if (!p.isDead() && !p.isAI()
                    && ((ServerPlayer) p).isConnected()) {
                    human = true;
                    break;
                }
            }
            if (!human) {
                game.setCurrentPlayer(null);
                return null;
            }

            // ATM AI player colony-rearrangements do not use the standard
            // c-s model but exploit the fact that the AIs and the server
            // are still in the same memory space.  This means that
            // the colony sizes will be wrongly reported unless we take
            // the following explicit action.
            ChangeSet cs = new ChangeSet();
            if (player.isAI() && player.isEuropean()) {
                for (Colony c : player.getColonies()) {
                    cs.add(See.perhaps().except(player), c);
                }
            }

            // Clean up futures from the current player.
            resolveOutstandingQueries();

            // Check for new turn
            if (game.isNextPlayerInNewTurn()) {
                game.csNewTurn(random, cs);
                if (debugOnlyAITurns > 0) {
                    if (--debugOnlyAITurns <= 0) {
                        // If this was a debug run, complete it.  This will
                        // signal the client to save and quit at the next
                        // suitable opportunity.
                        FreeColDebugger.signalEndDebugRun();
                    }
                }
            }

            if ((player = (ServerPlayer) game.getNextPlayer()) == null) {
                // "can not happen"
                return DOMMessage.clientError("Can not get next player");
            }
            // Remove dead players and retry
            switch (player.checkForDeath()) {
            case ServerPlayer.IS_DEAD:
                player.csWithdraw(cs);
                sendToAll(cs);
                logger.info(player.getNation() + " is dead.");
                continue;
            case ServerPlayer.IS_ALIVE:
                if (player.isREF() && player.checkForREFDefeat()) {
                    for (Player p : player.getRebels()) {
                        csGiveIndependence(player, (ServerPlayer) p, cs);
                    }
                    player.csWithdraw(cs);
                    sendToAll(cs);
                    logger.info(player.getNation() + " is defeated.");
                    continue;
                }
                break;
            default: // Need to autorecruit a unit to keep alive.
                ((ServerPlayer)player).csEmigrate(0, MigrationType.SURVIVAL,
                                                  random, cs);
                break;
            }

            // Do "new turn"-like actions that need to wait until right
            // before the player is about to move.
            game.setCurrentPlayer(player);
            if (player.isREF() && player.getEntryLocation() == null) {
                // Initialize this newly created REF, determining its
                // entry location.
                // If the teleportREF option is enabled, teleport it in.
                REFAIPlayer refAIPlayer = (REFAIPlayer) freeColServer
                    .getAIPlayer(player);
                boolean teleport = getGame().getSpecification()
                    .getBoolean(GameOptions.TELEPORT_REF);
                Tile entry = refAIPlayer.initialize(teleport);
                if (entry == null) {
                    for (Player p : player.getRebels()) {
                        entry = p.getEntryLocation().getTile();
                        break;
                    }
                }
                player.setEntryLocation(entry);
                logger.info(player.getName() + " will appear at " + entry);
                if (teleport) {
                    for (Unit u : player.getUnits()) {
                        if (u.isNaval()) {
                            u.setLocation(entry);
                            u.setWorkLeft(-1);
                            u.setState(Unit.UnitState.ACTIVE);
                        }
                    }
                    cs.add(See.perhaps(), entry);
                }
            }
            player.csStartTurn(random, cs);
            nextFoundingFather(player);

            cs.addTrivial(See.all(), "setCurrentPlayer",
                          ChangePriority.CHANGE_LATE,
                          "player", player.getId());
            if (player.getPlayerType() == PlayerType.COLONIAL) {
                Monarch monarch = player.getMonarch();
                MonarchAction action = null;
                if (debugMonarchAction != null
                    && player == debugMonarchPlayer) {
                    action = debugMonarchAction;
                    debugMonarchAction = null;
                    debugMonarchPlayer = null;
                    logger.finest("Debug monarch action: " + action);
                } else {
                    action = RandomChoice.getWeightedRandom(logger,
                            "Choose monarch action", random,
                            monarch.getActionChoices());
                }
                if (action != null) {
                    if (monarch.actionIsValid(action)) {
                        logger.finest("Monarch action: " + action);
                        csMonarchAction(player, action, cs);
                    } else {
                        logger.finest("Skipping invalid monarch action: "
                            + action);
                    }
                }
            }

            // Flush accumulated changes.  Send to all players, but
            // take care that the new player is last so that it does
            // not immediately start moving and cause further changes
            // which conflict with these updates.  Finally return to the
            // current player which requested the end-of-turn, unless
            // it is doing a debug run.
            sendToList(getOtherPlayers(serverPlayer, (ServerPlayer)player), cs);
            sendElement((ServerPlayer)player, cs);
            if (!player.isAI()
                && freeColServer.isSinglePlayer()
                && debugOnlyAITurns > 0) {
                continue;
            }
            return cs.build(serverPlayer);
        }
    }