/* This file is part of cardwords
   (c) 1999 Tobias Peters
   see file COPYING for the copyright terms.
   
   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
*/

// cardwords_game.cc

#include "cardwords_game.hh"
#include "cardwords_server.hh"
#include "cardwords_iostream.hh"
#include <string> // Get rid of "cardwords_machinestring.hh"
#include "cardwords_cardtable.hh"
#include "cardwords_pile.hh"
#include "cardwords_participant.hh"
#include <algo.h>
#include "cardwords_exceptions.hh"

// Get told about a new Connection. Will send info about registering
// possibilities and if there are none, will send "BYE".
// the latter case is indicated by return value -1
int CardWords_Game::new_connection (CardWords_Participant * applicant)
{
  // first assert that we do not know already about this participant:
  assert(new_participants.find(applicant) == new_participants.end());
  assert(players.find(applicant) == players.end());
  assert(watchers.find(applicant) == watchers.end());
  assert(applicant != owner);
  
  // If we reserve one player place for the owner we report one place less.
  // If there is no other chance to log in we will tell the remote participant
  // that he can log in as owner.
  bool player_possible = ( ( (max_players > players.size()
                              && reserve_player_place_for_owner == false)
                             ||
                             ((max_players - 1) > players.size()
                              && reserve_player_place_for_owner == true)
                             || (max_players > players.size()
                                 && reserve_player_place_for_owner == true
                                 && owner_is_present)
                             )
                           && game_running == false);

  bool owner_player_possible = ((! owner_is_present)
                                && max_players > players.size()
                                && game_running == false);

  bool owner_watcher_possible = ((!owner_is_present)
                                 && max_watchers > watchers.size());

  bool watcher_possible = (max_watchers > watchers.size());

  if (applicant->is_robot()) {
    assert (player_possible == true);
    assert (applicant->is_player());
    ostrstream name;
    name << "Robot" << players.size() + 1 << ends;
    name.freeze(true);
    applicant->set_name(name.str());
    name.freeze(false);
    players.insert(applicant);
#ifdef DEBUG
    set<CardWords_Participant *>::const_iterator ci =
      players.find (applicant);
    assert (*ci == applicant);
#endif
    // check_enough_players(); // FIXME: allow robots-only game?
#if defined AUTOSTART_WHEN_ENOUGH_LOGGED_ON && defined ALLOW_ROBOTS_ONLY_GAME
    if (players.size() == max_players) {
      start_game();
    }
#endif
    applicant->make_player();
    
    return 0;
  }

  if (player_possible == false
      && owner_watcher_possible == false
      && owner_player_possible == false
      && watcher_possible == false) {
    // No log on possible
    applicant->send_message(PERSONAL_MESSAGE, MESSAGE_BYE);
    return -1;
  }
  else {
    ostrstream log_info;
    log_info << MESSAGE_LOG_AS;
    if (player_possible) {
      log_info << ' ' << STRING_PLAYER;
    }
    if (watcher_possible) {
      log_info << ' ' << STRING_WATCHER;
    }
    if (owner_watcher_possible) {
      log_info << ' ' << STRING_OWNER_WATCHER;
    }
    if (owner_player_possible) {
      log_info << ' ' << STRING_OWNER_PLAYER;
    }
    log_info  << ' ' << MESSAGE_LOG_AS_END << endl;

    // Now write the Charset description, the card descriptions,
    // the bonus descriptions, the table description.
    CardWords_Char::write_definitions_to_stream(log_info);
    pile->print_cardSpecs (log_info);
    log_info << *bonus << *table << ends;
    
    log_info.freeze(true);
    applicant->send_message(PERSONAL_MESSAGE, log_info.str());
    log_info.freeze(false);
  }
  new_participants.insert(applicant);
  return 0;
}

// called by the destructor of CardWords_Participant:
void CardWords_Game::deregister (CardWords_Participant * participant)
{
  assert (participant != 0);
  if (participant == owner) {
    assert (owner_is_present);
    owner_is_present = false;
    owner = 0;
  }
  // find out in which set the participant is:
  E_In_Set in_set;
  if(new_participants.find(participant) != new_participants.end()) {
    in_set = new_set;
    assert(players.find(participant) == players.end());
    assert(watchers.find(participant) == watchers.end());
    new_participants.erase(participant);
  }
  else if(watchers.find(participant) != watchers.end()) {
    in_set = watch_set;
    assert(players.find(participant) == players.end());
    watchers.erase(participant);
  }
  else if(players.find(participant) != players.end()) {
    in_set = play_set;
    if (game_running) {
      // Why has the player not robotized ?
      // Print a warning.
      cerr << gettext("Warning: A player deregisters from the game while "\
                      "it is running.\nThis may be a bug in the server "\
                      "code.\n");
      if (participant->has_name()) {
        cerr << gettext("The leaving players name is:")
             << participant->get_name();
      }
      // try to eliminate these situations:
      assert (0);
      // FIXME: remove the warning when it is unnecessary
    }
    assert (game_running == false);
    players.erase(participant);
    // Tell the others:
    if (is_game_over() == false) {
      broadcast_logged_players();
      broadcast_cards_left();
    }
  }
  else {
    // Participant is in none of the sets:
    assert (0);
  }
}

// called when a request was received completely:
int CardWords_Game::request_from (const string & request,
                                  CardWords_Participant * participant)
{
  assert (participant != 0);
  assert (participant->is_robot() == false);
  
  // find out in which set the participant is:
  E_In_Set in_set;
  bool from_owner = (participant == owner);
#ifdef DEBUG
  if (from_owner) assert (participant->is_owner());
#endif
  if(new_participants.find(participant) != new_participants.end()) {
    in_set = new_set;
    assert(players.find(participant) == players.end());
    assert(watchers.find(participant) == watchers.end());
  }
  else if(players.find(participant) != players.end()) {
    in_set = play_set;
    assert(watchers.find(participant) == watchers.end());
  }
  else if(watchers.find(participant) != watchers.end()) {
    in_set = watch_set;
  }

  string command;
  istrstream istr(request.c_str());

  istr >> command;
  if (command.length() == 0) {
    // participant sent empty request. Ignore it ?
    return 0;
  }
  if (command == REQUEST_REGISTER) {
    if (in_set != new_set) {
      goto invalid_command; // only new participants can register
    }
    return registering (participant, istr);
  }
  if (command == REQUEST_START) {
    return start(participant);
  }
  if (command == REQUEST_MOVE) {
    return move(participant, istr);
  }
  if (command == REQUEST_TRADE) {
    return trade(participant, istr);
  }
  if (command == REQUEST_PASS) {
    return pass(participant);
  }

  if (command == REQUEST_ADD_WORD) {
    return add_word(participant, istr);
  }
 invalid_command:
  if (game_running && in_set == play_set) {
    participant->robotize();
    return -1;
  }
  else {
    delete participant;
    return -1;
  }
  return -1; // Quiet compiler.
}

CardWords_Game::
CardWords_Game (size_t set_max_remote_players,
                size_t set_initial_no_of_robots,
                size_t set_max_watchers,
                CardWords_CardTable * set_table,
                CardWords_Pile * set_pile,
                const CardWords_BonusTable * set_bonus,
                bool set_reserve_player_place_for_owner = true)
  : owner           (0),
    max_players     (set_max_remote_players + set_initial_no_of_robots),
    max_watchers    (set_max_watchers),
    owner_is_present(false),
    owner_is_player (false),

    reserve_player_place_for_owner (set_reserve_player_place_for_owner),
    game_running                   (false),
    game_over                      (false),
    number_of_consecuting_passes   (0),

    table       (set_table),
    pile        (set_pile),
    bonus       (set_bonus),
    asked_dicbot(false),

    expect_ok_from_dicbot (0U),
    asked_for_player      (static_cast<CardWords_Participant *>(0))
{
  assert (table != 0);
  assert (pile != 0);
  assert (max_players > 0);
  assert (bonus != 0);
  // make the robots:
  size_t robotindex;
  for (robotindex = 0; robotindex < set_initial_no_of_robots; ++robotindex) {
    (void) new CardWords_Participant (-1, &(server_global.selector), this);
  }
}

// Who's the lazy one:
CardWords_Participant * CardWords_Game::whose_turn (void)
{
  if (game_running == true) {
    return *active_player;
  }
  return 0;
}

// the requests from the players:
int CardWords_Game::pass (CardWords_Participant * participant)
  // will also be called from CardWords_Participant::robotize()
{
  assert (game_running);
  assert (participant->is_player());
  assert (whose_turn() == participant);

  ostrstream pass_message;
  pass_message << MESSAGE_PLAYER_PASSES << ' ' << participant->get_name()
               << ends;
  pass_message.freeze(true);
  broadcast_message(pass_message.str());
  pass_message.freeze(false);

  ostrstream points_message;
  points_message << MESSAGE_PLAYER_GETS_POINTS
                 << ' '
                 << (*active_player)->get_name()
                 << ' '
                 << (*get_bonus())[0]
                 << endl << ends;
  points_message.freeze(true);
  broadcast_message(points_message.str());
  points_message.freeze(false);

  // Adding the points:
  (*active_player)->add_points((*get_bonus())[0]);

  broadcast_logged_players();
  broadcast_cards_left();

  ++number_of_consecuting_passes;

  if (number_of_consecuting_passes >= (2 * players.size())) {
    assert (number_of_consecuting_passes == (2 * players.size()));
    end_game();
  }
  else {
    next_players_go();
  }
  return 0;
}

int CardWords_Game::registering (CardWords_Participant * participant,
                                 istream & istr)
{
  assert (participant->is_remote_participant());
  assert (new_participants.find(participant) != new_participants.end());
  // valid ways of registering are:
  // REGISTER PLAYER <name>
  // REGISTER WATCHER <name>
  // REGISTER OWNER_PLAYER <password> <name>
  // REGISTER OWNER_WATCHER <password> <name>
  string type, password, name;
  istr >> type;

  if (type != STRING_OWNER_PLAYER
      &&
      type != STRING_OWNER_WATCHER
      &&
      type != STRING_PLAYER
      &&
      type != STRING_WATCHER
      ) {
    // Invalid registering request.
    delete participant;
    return -1;
  }

  if (type == STRING_OWNER_PLAYER
      ||
      type == STRING_OWNER_WATCHER) {
    if (owner_is_present) {
      // second owner trying to log in
      assert (owner != 0);
      owner->send_message(PERSONAL_MESSAGE, MESSAGE_2ND_OWNER_TRIED);
      delete participant;
      return -1;
    }
    istr >> password;
    if (password != server_global.owner_password) {
      // wrong password
      delete participant;
      return -1;
    }
    // ok, he can log in as owner:
    assert (owner == 0);
    assert (participant->is_owner() == false);
    owner = participant;
    participant->make_owner();
    owner_is_present = true;
  }
  istr >> name;

  // Player or Watcher: first check the name:
  assert (participant->is_watcher()); // It is the default.
  if (name.length() > REQUEST_MAX_NAME_LEN + 1
      || name.length() == 0) {
    // Name too long or too short
    delete participant;
    return -1;
  }
  // the name must be unique:
  set<CardWords_Participant *>::const_iterator set_iter;
  set<CardWords_Participant *>::const_iterator set_end;
  for ((set_iter = players.begin()),(set_end = players.end());
       set_iter != set_end;
       ++set_iter ) {
    if (name == (*set_iter)->get_name()) {
      goto NON_UNIQUE_NAME;
    }
  }
  for ((set_iter = watchers.begin()),(set_end = watchers.end());
       set_iter != set_end;
       ++set_iter ) {
    if (name == (*set_iter)->get_name()) {
    NON_UNIQUE_NAME:
      participant->send_message(PERSONAL_MESSAGE,
                                MESSAGE_NON_UNIQUE_NAME);
      if (owner == participant) {
        assert(owner_is_present); assert(participant->is_owner());
        owner = 0;
        owner_is_present = false;
        participant->unmake_owner();
      }
      return 0;
    }
  }
  // The name is unique. Try to log him on:
  if (type == STRING_WATCHER
      ||
      type == STRING_OWNER_WATCHER) {
    if (watchers.size() < max_watchers) {
      participant->set_name(name.c_str());
      new_participants.erase(participant);
      watchers.insert(participant);
      send_after_log_info (participant);
    }
    else { // no more watchers allowed
      participant->send_message(PERSONAL_MESSAGE, MESSAGE_FULL);
      participant->send_message(PERSONAL_MESSAGE, MESSAGE_BYE);
      participant->block_reading();
      if (owner == participant) {
        assert(owner_is_present); assert(participant->is_owner());
        owner = 0;
        owner_is_present = false;
        participant->unmake_owner();
      }
    }
  }
  else { // PLAYER or OWNER_PLAYER:
    if (game_running) {
      // not possible to log on as player whe the game runs:
      participant->send_message(PERSONAL_MESSAGE,
                                MESSAGE_GAME_ALREADY_RUNNING);
      participant->send_message(PERSONAL_MESSAGE, MESSAGE_BYE);
      participant->block_reading();
      if (owner == participant) {
        assert(owner_is_present); assert(participant->is_owner());
        owner = 0;
        owner_is_present = false;
        participant->unmake_owner();
      }
    }
    else if ( ( ( players.size() < max_players
                  && reserve_player_place_for_owner == false
                  )
                || (players.size() < max_players
                    && reserve_player_place_for_owner == true
                    && owner_is_present)
                || players.size() < (max_players - 1)
                || (players.size() == (max_players - 1)
                    && reserve_player_place_for_owner == true
                    && participant->is_owner()
                    )
                ) // enough handes?
              
              && get_pile()->occupied_places() >= get_hand_capacity()
              // enough cards?
              
              ) {
        
      participant->set_name(name.c_str());
      new_participants.erase(participant);
      players.insert(participant);
      send_after_log_info (participant); // welcome message
      participant->make_player(); // give cards

      // Tell the others that this player logged in:
      // Tell everyone who is logged on now and how many cards are
      // left in the pile:
      broadcast_logged_players();
      broadcast_cards_left();

#ifdef AUTOSTART_WHEN_ENOUGH_LOGGED_ON
      if (players.size() == max_players) {
        start_game();
      }
#endif
    }
    else { // not possible to log on as player because the server is full:
      participant->send_message(PERSONAL_MESSAGE, MESSAGE_FULL);
      participant->send_message(PERSONAL_MESSAGE, MESSAGE_BYE);
      participant->block_reading();
      if (owner == participant) {
        assert(owner_is_present); assert(participant->is_owner());
        owner = 0;
        owner_is_present = false;
        participant->unmake_owner();
      }
    }
  }
  return 0;
}    


// After he logged successfully on, send the remaining info to
// this CardWords_Participant:
void CardWords_Game::send_after_log_info (CardWords_Participant * participant)
{
  ostrstream message;
  message <<  MESSAGE_WELCOME << ' '
          << participant->get_name() << endl << ends;
  message.freeze(true);
  participant->send_message(PERSONAL_MESSAGE, message.str());
  message.freeze(false);
}


int CardWords_Game::trade (CardWords_Participant *participant, istream & istr)
{
  CardWords_CardSelection trade(istr);
  CardWords_CardContainer1Dim temp_cardcontainer(trade.size());
  ostrstream accepting_message;
  ostrstream info_message;
  size_t index;
  CardWords_Card * card;

  // If a wildcard is to be traded, make sure it has no meaning set.
  // Also make sure all cards the player wants to trade are on his hand.
  // Move the cards to be traded to the temp container temp_cardcontainer:
  for (index = 0; index < trade.size(); ++index) {
    if (trade[index].is_wildcard()) {
      trade[index].set_meaning(CardWords_Char::blanko);
    }
    card = participant->get_hand()->matching_card(trade[index]);
    if (card == 0) {
      // No such card on the hand, it cannot be traded.
      // The trade fails. Send the cards from temp_cardcontainer back to the
      // hand:
      temp_cardcontainer.clear(participant->get_hand());
      // inform the user about the invalid request:
      ostrstream not_accepting_message;
      not_accepting_message << MESSAGE_NO_SUCH_CARD
                   << ' '
                   << trade[index]
                   << endl
                   << ends;
      not_accepting_message.freeze(true);
      participant->send_message(PERSONAL_MESSAGE,
                                not_accepting_message.str());
      not_accepting_message.freeze(false);
      return 0; // let him live.
    }
    temp_cardcontainer.add_card(card);
  }

  // Tell the active player that we received his trade request and are working
  // on it:
  accepting_message << MESSAGE_ACCEPTING_YOUR_TRADE
                    << ' '
                    << trade
                    << endl
                    << ends;
  accepting_message.freeze(true);
  participant->send_message(PERSONAL_MESSAGE, accepting_message.str());
  accepting_message.freeze(false);
  
  info_message << MESSAGE_PLAYER_TRADES_CARDS
               << ' '
               << participant->get_name()
               << ' '
               << trade.size()
               << ends
    ;
  info_message.freeze(true);
  broadcast_message(info_message.str());
  info_message.freeze(false);
  
  participant->refill_hand();
  temp_cardcontainer.clear(pile);
  participant->refill_hand(); // In case there were not enough cards
  //                              in the pile.

  ostrstream points_message;
  points_message << MESSAGE_PLAYER_GETS_POINTS
                 << ' '
                 << (*active_player)->get_name()
                 << ' '
                 << (*get_bonus())[-(int)trade.size()]
                 << endl << ends;
  points_message.freeze(true);
  broadcast_message(points_message.str());
  points_message.freeze(false);

  // Adding the points:
  participant->add_points((*get_bonus())[-(int)trade.size()]);

  broadcast_logged_players();
  broadcast_cards_left();

  number_of_consecuting_passes = 0; // This player did not pass.

  next_players_go();

  return 0;
}
int CardWords_Game::move (CardWords_Participant *ptpt, istream & istr)
{
  assert (ptpt == *active_player);

  if (asked_dicbot == true) {
    // The active player sent a second request but the first is not finished
    // yet. Ignore that request.
    cerr << gettext("WARNING: CardWords_Game::move(): received a "\
                    "second request from\n"\
                    "  the same player, while the first one "\
                    "has not been answered by\n"\
                    "  dicbot.\n"\
                    "IGNORING THIS REQUEST.")
         << endl;
    return 0;
  }

  istr >> current_move;
  CardWords_CardContainer1Dim temp_cardcontainer(current_move.size());
  CardWords_Card * card;
  
  // Make sure all cards the player wants to move are on his hand.
  for (size_t index = 0; index < current_move.size(); ++index) {
    CardWords_CardDescription card_desc(current_move[index]);
    
    if (card_desc.is_wildcard()) {
      card_desc.set_meaning(CardWords_Char::blanko);
    }

    card = ptpt->get_hand()->matching_card(card_desc);
    if (card == 0) {
      // No such card on the hand, it cannot be traded.
      
      temp_cardcontainer.
        clear(ptpt->get_hand()); // Undo the temporary changes.
      
      // The move fails. Inform the user about the invalid request:
      ostrstream not_accepting_message;
      not_accepting_message << MESSAGE_NO_SUCH_CARD
                   << ' '
                   << card_desc
                   << endl
                   << ends;
      not_accepting_message.freeze(true);
      ptpt->send_message(PERSONAL_MESSAGE, not_accepting_message.str());
      not_accepting_message.freeze(false);
      return 0; // let him live.
    }
    // Remove the card just found so it can not be counted a second time:
    temp_cardcontainer.add_card(card);
  }

  // All cards have been found.
  temp_cardcontainer.clear(ptpt->get_hand()); // Undo the temporary changes.
  
  ostrstream dicbot_command;

  dicbot_command << "ADD MOVE ";

  dicbot_command << current_move;
  
  dicbot_command << ends;
  dicbot_command.freeze(true);
  server_global.dicbot.lock_and_ask_child(this, dicbot_command.str());
  dicbot_command.freeze(false);
  asked_dicbot = true;
  asked_for_player = ptpt;
  
  ostrstream try_message;
  try_message << MESSAGE_PLAYER_TRIES_MOVE
              << ' '
              << ptpt->get_name()
              << ' '
              << current_move
              << endl << ends;

  try_message.freeze(true);
  broadcast_message(try_message.str());
  try_message.freeze(false);

  return 0;
}

void CardWords_Game::child_answers(const string & answer)
{
  assert (asked_dicbot == true || expect_ok_from_dicbot > 0U);
  assert (asked_for_player == *active_player
          || expect_ok_from_dicbot > 0U);

  // Possible DicBot Answers are:
  //   INVALID MOVE
  //   INVALID SIDEWORD <card_index> <false_word>
  //   INVALID WORD <false_word>
  //   MOVE COUNTS <score>
  //   ERROR: NO TABLE

  string ans[3];
  size_t         card_index = UINT_MAX;
  int            score = INT_MIN;
  CardWords_String false_word;

  istrstream ans_stream (answer.c_str());

  ans_stream >> ans[0];

  if (ans[0] == "OK") {
    assert (expect_ok_from_dicbot > 0U);
    --expect_ok_from_dicbot;
    return;
  }
    
  asked_dicbot = false;
  asked_for_player = 0;
  
  ans_stream >> ans[1];
  if (ans[0] == "ERROR:") {
    assert (ans[1] == "NO");

    ans_stream >> ans[2];
    assert (ans[2] == "TABLE");

    throw CardWords_XInternal_Error(gettext("Dicbot returns the \"ERROR: NO "\
                                            "TABLE\" error message in return "\
                                            "to a \"ADD MOVE\" question"));
  }
  if (ans[0] == "MOVE") {
    assert (ans[1] == "COUNTS");
    ans_stream >> score;

    {
      ostrstream broadcast;

      broadcast << MESSAGE_PLAYER_GETS_POINTS
                << ' '
                << (*active_player)->get_name()
                << ' '
                << score
                << endl << ends;
      broadcast.freeze(true);
      broadcast_message(broadcast.str());
      broadcast.freeze(false);
    }

    table->add_move(current_move, (*active_player)->get_hand());
    
    {
      ostrstream broadcast;

      broadcast << MESSAGE_MOVE_DONE
                << ' '
                << current_move
                << endl << ends;
      broadcast.freeze(true);
      broadcast_message(broadcast.str());
      broadcast.freeze(false);
    }
    (*active_player)->add_points(score);
    (*active_player)->refill_hand();
    broadcast_logged_players();
    broadcast_cards_left();

    number_of_consecuting_passes = 0; // This player did not pass.

    next_players_go();
  }
  else if (ans[0] == "INVALID") {
    if (ans[1] == "MOVE") {
      // The move does not fit on the table
      ostrstream not_fitting_message;
      not_fitting_message << MESSAGE_MOVE_NOT_ALLOWED
                          << ' '
                          << current_move
                          << endl << ends;
      not_fitting_message.freeze(true);
      broadcast_message(not_fitting_message.str());
      not_fitting_message.freeze(false);

      ostrstream must_pass_message;
      must_pass_message << MESSAGE_PLAYER_MUST_PASS
                        << ' '
                        << (*active_player)->get_name()
                        << endl << ends;

      must_pass_message.freeze(true);
      broadcast_message(must_pass_message.str());
      must_pass_message.freeze(false);
      
      pass(*active_player);
    }
    else {
      // The move would create an unknown word:
      ostrstream false_word_message;
      if (ans[1] == "SIDEWORD") {
        
        ans_stream >> card_index >> false_word;

        false_word_message << MESSAGE_MOVE_WOULD_CREATE_UNKNOWN_WORD_AT
                           << ' '
                           << current_move
                           << ' '
                           << false_word
                           << ' '
                           << card_index;
      }
      else {
        assert (ans[1] == "WORD");
        
        ans_stream >> false_word;

        false_word_message << MESSAGE_MOVE_WOULD_CREATE_UNKNOWN_WORD
                           << ' '
                           << current_move
                           << ' '
                           << false_word;
      }
      false_word_message << endl << ends;
      false_word_message.freeze(true);
      broadcast_message(false_word_message.str());
      false_word_message.freeze(false);
    }
  }
}
int CardWords_Game::quit (CardWords_Participant *)
{
  return 0;
}
int CardWords_Game::kick (CardWords_Participant *, istream & istr)
{
  return 0;
}
int CardWords_Game::add_word (CardWords_Participant *ptpt, istream & istr)
{
  if (owner == ptpt) {
    CardWords_String word;
    string dicbot_command;
    ostrstream word_string;

    istr >> word;

    dicbot_command = "ADD WORD ";

    word_string << word << ends;
    word_string.freeze(true);
    
    dicbot_command += word_string.str();
    dicbot_command += '\n';
    server_global.dicbot.lock_and_ask_child(this, dicbot_command);

    ++expect_ok_from_dicbot;

    string notify_players = MESSAGE_NEW_WORD;
    notify_players += ' ';
    notify_players += word_string.str();
    notify_players += '\n';
    
    word_string.freeze(false);
    broadcast_message(notify_players);
  }
  return 0;
}
int CardWords_Game::remove_word (CardWords_Participant *, istream & istr)
{
  return 0;
}
int CardWords_Game::start (CardWords_Participant * participant)
{
  assert (participant->is_remote_participant());
  if (game_running) {
    return 0; // Don't kill him, it may be an error.
  }
  if (participant == owner
      || players.size() == max_players) {
    assert(participant->is_owner());
    start_game();
  }
  else {
    if (participant->is_watcher()) {
      // Watcher is not allowed to request the game start
      delete participant;
      return -1;
    }
    participant->send_message(ANSWER_MESSAGE, MESSAGE_NOT_OWNER);
  }  
  return 0;
}

static int
random_number_generator(int max)
{
  return rand() % max;
}

void CardWords_Game::start_game (void)
{
  assert(game_running == false);
  ordered_players.erase(ordered_players.begin(), ordered_players.end());
  
  back_insert_iterator<
    vector<CardWords_Participant *>
    >
    iter (ordered_players);
  
  copy (players.begin(), players.end(), iter);

  //random_shuffle(ordered_players.begin(), ordered_players.end(),
  //               random_number_generator);
  // does not compile
  
  {
    int (*rng)(int) = &random_number_generator;
    random_shuffle(ordered_players.begin(), ordered_players.end(),
                   rng);
  }


  game_running = true;

  ostrstream message;
  size_t index;
  
  message << MESSAGE_GAME_STARTS;
  for (index = 0; index < ordered_players.size(); ++index) {
    message << ' ' << ordered_players[index]->get_name();
  }
  message << ends;
  message.freeze(true);
  broadcast_message(message.str());
  message.freeze(false);
  
  active_player = ordered_players.end();
  next_players_go();
}

void CardWords_Game::broadcast_message(const string & message)
{
  set<CardWords_Participant *>::iterator iter;
  for (iter = players.begin();
       iter != players.end();
       ++iter) {
    if ((*iter)->is_remote_participant()) {
      (*iter)->send_message(INFO_MESSAGE, message.c_str());
    }
  }
  for (iter = watchers.begin();
       iter != watchers.end();
       ++iter) {
    assert ((*iter)->is_remote_participant());
    (*iter)->send_message(INFO_MESSAGE, message.c_str());
  }
}

void CardWords_Game::next_players_go(void)
{
  if (active_player != ordered_players.end()) {
    // tell the previous player that it's not his go any more
    assert ((*active_player)->is_player());
    assert (players.find(*active_player) != players.end());
    (*active_player)->go(false);
    
    // switch to the next player:
    ++active_player;
  }
  if (active_player == ordered_players.end()) {
    active_player = ordered_players.begin();
  }

  ostrstream active_player_message;
  active_player_message << MESSAGE_GO_OF << ' '
                        << (*active_player)->get_name() << ends;
  active_player_message.freeze(true);
  broadcast_message(active_player_message.str());
  active_player_message.freeze(false);

  // send a personal message to the new active player, or, if he is a robot,
  // push the button:
  (*active_player)->go();
}

// This player mutated into a robot:
int CardWords_Game::robotized (CardWords_Participant * new_robot)
  // Result is an info to all remaining
  // remote_patricipant-s.
{
  if (new_robot == owner) {
    owner = 0;
    owner_is_present = false;
  }
  broadcast_logged_players();
  return 0;
}

// info about the game:
size_t CardWords_Game::get_hand_capacity(void) const
{
  return bonus->get_hand_capacity();
}

CardWords_Pile * CardWords_Game::get_pile(void)
{
  return pile;
}
const
CardWords_BonusTable *
CardWords_Game::get_bonus(void) const
{
  return bonus;
}

bool
CardWords_Game::is_game_running(void) const
{
  return game_running;
}

bool
CardWords_Game::is_game_over(void) const
{
  return game_over;
}

size_t
CardWords_Game::get_no_of_robots(void) const
{
  size_t count = 0;
  set<CardWords_Participant *>::const_iterator iter = players.begin();
  for(;iter != players.end(); ++iter) {
    if ((*iter)->is_robot() == true) {
      ++count;
    }
  }
  return count;
}

size_t
CardWords_Game::get_no_of_remote_players(void) const
{
 size_t count = 0;
  set<CardWords_Participant *>::const_iterator iter = players.begin();
  for(;iter != players.end(); ++iter) {
    if ((*iter)->is_remote_participant() == true) {
      ++count;
    }
  }
  return count; 
}
size_t
CardWords_Game::get_no_of_players(void) const
{
  return players.size();
}

void
CardWords_Game::broadcast_logged_players(void)
{
  // This broadcasts the message MESSAGE_LOGGED_PLAYERS which
  // is followed by:
  // - the number of robot players and STRING_ROBOTS
  // - the names and numbers of cards and the points of each
  //   robot
  // - the number of remote players and STRING_REMOTE_PLAYERS
  // - the names and numbers of cards and the points of each
  //   remote player

  size_t no_of_robots = get_no_of_robots();
  size_t no_of_remote_players = get_no_of_remote_players();
  
  ostrstream players_message;

  players_message << MESSAGE_LOGGED_PLAYERS
                  << endl
                  << no_of_robots
                  << ' '
                  << STRING_ROBOTS
                  << ' ';

  set<CardWords_Participant *>::const_iterator iter = players.begin();
  for(;iter != players.end(); ++iter) {
    if ((*iter)->is_robot() == true) {
      players_message << (*iter)->get_name()
                      << ' '
                      << (*iter)->get_hand()->occupied_places()
                      << ' '
                      << STRING_CARDS
                      << ' '
                      << (*iter)->get_points()
                      << ' '
                      << STRING_POINTS
                      << ' ';
    }
  }

  players_message << endl
                  << no_of_remote_players
                  << ' '
                  << STRING_REMOTE_PLAYERS
                  << ' ';
  
  iter = players.begin();
  for(;iter != players.end(); ++iter) {
    if ((*iter)->is_remote_participant() == true) {
      players_message << (*iter)->get_name()
                      << ' '
                      << (*iter)->get_hand()->occupied_places()
                      << ' '
                      << STRING_CARDS
                      << ' '
                      << (*iter)->get_points()
                      << ' '
                      << STRING_POINTS
                      << ' ';
    }
  }
  players_message << endl << ends;
  
  players_message.freeze(true);
  broadcast_message(players_message.str());
  players_message.freeze(false);
}

void
CardWords_Game::broadcast_cards_left(void)
{
  ostrstream message;
  message << MESSAGE_REMAINING_CARDS
          << ' '
          << get_pile()->occupied_places()
          << endl << ends;
  message.freeze(true);
  broadcast_message(message.str());
  message.freeze(false);
}

void
CardWords_Game::end_game(void)
{
  assert (number_of_consecuting_passes == (2 * players.size()));
  broadcast_message(MESSAGE_GAME_ENDS);

  // Calculate the end results:
  vector<CardWords_Participant *>::iterator player_iterator;
  for (player_iterator = ordered_players.begin();
       player_iterator != ordered_players.end();
       ++player_iterator) {

    int points = 0;
    CardWords_Hand::const_iterator player_card_iterator;

    for(player_card_iterator = (*player_iterator)->get_hand()->begin();
        player_card_iterator != (*player_iterator)->get_hand()->end();
        ++player_card_iterator) {

      points -= (*player_card_iterator)->get_points();
    }
    (*player_iterator)->add_points(points);
  }

  broadcast_logged_players();

  // send everyone the by_message
  set<CardWords_Participant *>::iterator ptpt_iterator;
  for (ptpt_iterator = new_participants.begin();
       ptpt_iterator != new_participants.end();
       ++ptpt_iterator) {
    (*ptpt_iterator)->send_message(PERSONAL_MESSAGE, MESSAGE_BYE);
  }
  for (ptpt_iterator = watchers.begin();
       ptpt_iterator != watchers.end();
       ++ptpt_iterator) {
    (*ptpt_iterator)->send_message(PERSONAL_MESSAGE, MESSAGE_BYE);
  }
  for (ptpt_iterator = players.begin();
       ptpt_iterator != players.end();
       ++ptpt_iterator) {
    if ((*ptpt_iterator)->is_remote_participant()) {
      (*ptpt_iterator)->send_message(PERSONAL_MESSAGE, MESSAGE_BYE);
    }
  }

  // kill Dicbot
  server_global.dicbot.stop_communication();
  
  game_running = false;
  game_over = true;
}
