/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2006  by Diether Knof and Borg Enders
 *
 *   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 can find this license in the file 'gpl.txt'.
 *
 *   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
 *
 *  Contact:
 *    Diether Knof dknof@gmx.de
 *    Borg Enders  borg@borgsoft.de
 *
 **********************************************************************/

#include <iterator>

#include "constants.h"
#include "party.h"

#include "../utils/string.h"

#include "rule.h"
#include "../game/game_summary.h"
#include "../player/player.h"
#include "../player/ai/ai.h"
#include "../card/trick.h"

#include "../misc/setting.h"
#include "../misc/translations.h"

#include "../ui/ui.h"
#include "../ui/ui.wrap.h"

#ifdef USE_NETWORK
#include "../network/server.h"
#endif


/**
 **
 ** constructor:
 ** load the players from the private data directory
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Party::Party() :
  rule_(new Rule),
  players_(0),
  seed_first_(SEED_START),
  startplayer_first_(STARTPLAYER),
  real_seed_first_(SEED_START),
  real_startplayer_first_(STARTPLAYER),
  round_startgame_(),
  game_(),
  seed_(this->seed_first_),
  startplayer_(this->startplayer_first_),
  game_summaries_()
{
  vector<string> default_names;
  default_names.push_back("");
  default_names.push_back("Gerd");
  default_names.push_back("Erika");
  default_names.push_back("Sven");
  // load the players from the private data directory
  // or take default ones
  for (unsigned n = 0;
       (this->playerno() < this->rule()(Rule::NUMBER_OF_PLAYERS));
       n++) {
    Player* player = Player::new_(::setting(Setting::PRIVATE_DATA_DIRECTORY)
				  + "/player."
				  + DK::Utils::String::to_string(n + 1));
    if (player)
      this->players_.push_back(player);
    else
      this->players_.push_back(Player::new_(( (n == 0)
					     ? Player::HUMAN
					     : Player::AI),
					    ( (n < default_names.size())
					     ? default_names[n]
					     : "")
					   ));

    this->players_.back()->set_no(n);
  } // for (n; this->playerno() < this->rule()(Rule::NUMBER_OF_PLAYERS))

  return ;
} // Party::Party()

/**
 **
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Party::~Party()
{
  delete this->game_;
  delete this->rule_;

  return ;
} // Party::~Party()

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of players
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::playerno() const
{
  return this->players().size();
} // unsigned Party::playerno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	number of humans in the party
 **
 ** @version	0.6.7
 **
 ** @author	Diether Knof
 **
 **/
unsigned
Party::humanno() const
{
  // count the number of human players
  unsigned human_no = 0;
  for (vector<Player*>::const_iterator player = this->players().begin();
       player != this->players().end();
       player++)
    if ((*player)->type() == Player::HUMAN)
      human_no += 1;

  return human_no;
} // unsigned Party::humanno() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the players
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.0
 **/
vector<Player*>&
Party::players()
{
  return this->players_;
} // vector<Player*>& Party::players()

/**
 **
 ** -> result
 **
 ** @param	p	number of the player
 **
 ** @return	the player with the number 'p'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Player&
Party::player(unsigned const p)
{
  DEBUG_ASSERTION((p < this->playerno()),
		  "Party::player(p):\n"
		  "'p' is not valid (");

  return *(this->players()[p]);
} // Player& Party::player(unsigned const p)

/**
 **
 ** -> result
 **
 ** @param	p	number of the player
 **
 ** @return	the player with the number 'p'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Player const&
Party::player(unsigned const p) const
{
  DEBUG_ASSERTION((p < this->playerno()),
		  "Party::player(p):\n"
		  "'p' is not valid (" << p << ")");

  return *(this->players()[p]);
} // Player const& Party::player(unsigned const p) const

/**
 **
 ** Sets the player in the party
 ** The player is taken directly, 'party' deletes it afterwards.
 **
 ** @param	player	the player to be set
 ** @param	n	the number of the player
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_player(Player* player, unsigned const n)
{
  DEBUG_ASSERTION((n < this->playerno()),
		  "Party::set_player(player, n):\n"
		  "'n' is not valid");

  DEBUG_ASSERTION(!(::game_status & GAMESTATUS::GAME),
		  "Party::set_player(player, n):\n"
		  "  game_status is in the game.");

  delete this->players_[n];
  this->players_[n] = player;
  this->players_[n]->set_no(n);

  if (::ui != NULL)
    ::ui->player_update(this->player(n));

  return ;
}

/**
 ** switches the player in the party
 ** The player is taken directly, the replaced player is returned
 **
 ** @param	player	the player to be switched
 ** @param	n	the number of the player
 **
 ** @return	replaced player
 **		(has to be deleted!)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
Player*
Party::switch_player(Player* player, unsigned const n)
{
  DEBUG_ASSERTION((n < this->playerno()),
		  "Party::switch_player(player, n):\n"
		  "'n' is not valid: " << n);

  DEBUG_ASSERTION(!(::game_status & GAMESTATUS::GAME),
		  "Party::switch_player(player, n):\n"
		  "  game_status is in the game.");

  Player* const player_bak = this->players_[n];
  this->players_[n] = player;
  this->players_[n]->set_no(n);

  if (::ui != NULL)
    ::ui->player_update(this->player(n));

  return player_bak;
} // Player* Party::switch_player(Player* player, unsigned const n)

/**
 ** switches the position of the players
 **
 ** @param	n0	the first player
 ** @param	n1	the second player
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
void
Party::switch_players(unsigned const n0, unsigned const n1)
{
  DEBUG_ASSERTION((n0 < this->playerno()),
		  "Party::switch_players(n0, n1):\n"
		  "'n0' is not valid");

  DEBUG_ASSERTION((n1 < this->playerno()),
		  "Party::switch_players(n0, n1):\n"
		  "'n1' is not valid");

  DEBUG_ASSERTION(!(::game_status & GAMESTATUS::GAME),
		  "Party::switch_players(n0, n1):\n"
		  "  game_status is in the game.");

  std::swap(this->players_[n0], this->players_[n1]);
  this->players_[n0]->set_no(n0);
  this->players_[n1]->set_no(n1);

  ::ui->players_switched(this->player(n0), this->player(n1));

  return ;
} // void Party::switch_players(unsigned const n0, unsigned const n1)

/**
 **
 ** set the name of the player 'player_no' to 'new_name'
 **
 ** @param	player_no	the number of the player
 ** @param	new_name	the new name
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_playername(unsigned const player_no,
		      string const& new_name)
{
  DEBUG_ASSERTION((player_no < this->playerno()),
		  "Party::set_playername():  Illegal Value for player number: "
		  << player_no);

  this->players_[player_no]->set_name(new_name);

  return ;
} // void Party::set_playername(player_no, new_name)

/**
 **
 ** sets the type of the player 'player_no' to 'new_type'
 **
 ** @param	player_no	the number of the player
 ** @param	new_type	the new type
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 ** @todo	keep the aiconfig
 **
 **/
void
Party::set_playertype(unsigned const player_no,
		      Player::Type const new_type)
{
  DEBUG_ASSERTION((player_no < this->playerno()),
		  "Party::set_playertype(): "
		  << "  Illegal Value for player number: "
		  << player_no); 

  DEBUG_ASSERTION(this->players()[player_no],
		  "Party::set_playertype(): "
		  << "  player " << player_no << " does not exists");

  if (this->player(player_no).type() == new_type)
    return ;

  // create the new player
  this->set_player(Player::new_(new_type, this->player(player_no)),
		   player_no);

  return ;
} // void Party::set_playertype(unsigned const player_no, Player::Type const new_type)

/**
 **
 ** sets the starting seed of the party to be random
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_random_seed_first()
{
  this->set_seed_first(UINT_MAX);

  return ;
} // void Party::set_random_seed_first()

/**
 **
 ** sets the seed
 **
 ** @param	seed_new	new seed
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_seed(unsigned const seed_new)
{
  if (seed_new == UINT_MAX)
    this->set_random_seed();
  else
    this->seed_ = seed_new % PSEUDO_RAND_MAX;

  return ;
} // void Party::set_seed(unsigned const seed_new)

/**
 **
 ** sets the seed of the party to a random value
 ** (uses the system clock)
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 ** @todo	'better' random value
 **
 **/
void
Party::set_random_seed()
{
  // *** not so elegant
  //this->seed_ = static_cast<unsigned>(clock()) % PSEUDO_RAND_MAX;

  this->seed_ = static_cast<unsigned>(time(NULL) % PSEUDO_RAND_MAX);
  this->seed_next();

  return ;
} // void Party::set_random_seed()

/**
 **
 ** calculates the next seed with the old one
 **
 ** @param	-
 **
 ** @return	the new seed
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::seed_next()
{
  ::pseudo_rand_set(this->seed());

  // don't take the next random value but skip 48 values
  // (because 48 values are used for the distribution of the cards
  //  and we do want totally new hands)
  for (unsigned i = 0; i < 49; i++)
    this->seed_ = ::pseudo_rand_next();

  if (SEED_OUT) {
    // count the seeds
    static int seed_count = 0;
    seed_count += 1;

    // when a random number has already been tested, stop
    static vector<bool> tested(PSEUDO_RAND_MAX, false);
    if (tested[this->seed()] || (seed_count == PSEUDO_RAND_MAX)) {
      cout << "\nNumber of seeds: " << seed_count
	<< " (" << PSEUDO_RAND_MAX << ")" << endl;
      ::game_status = GAMESTATUS::QUIT;
      ::exit(0);
    }

    tested[this->seed()] = true;
  } // if (SEED_OUT)

  return this->seed();
} // unsigned Party::seed_next()

/**
 **
 ** sets the first startplayer to random
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_random_startplayer_first()
{
  this->startplayer_first_ = UINT_MAX;

  return ;
} // void Party::set_random_startplayer_first()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of the current round
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **
 **/
unsigned
Party::roundno() const
{
  if (this->round_startgame_.empty())
    return 0;

  return (this->round_startgame_.size() - 1);
} // unsigned Party::roundno() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of remaining rounds
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **
 **/
unsigned
Party::remaining_rounds() const
{
  if (!this->rule()(Rule::NUMBER_OF_ROUNDS_LIMITED))
    return 0;

  return (this->rule()(Rule::NUMBER_OF_ROUNDS) - this->roundno());
} // unsigned Party::remaining_rounds() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of the current round
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **
 **/
unsigned
Party::gameno() const
{
  if (this->game_)
    return (this->game_summaries().size() - 1);
  else
    return this->game_summaries().size();
} // unsigned Party::gameno() const

/**
 **
 ** -> result
 **
 ** @param	gameno	number of the game
 **
 ** @return	whether game 'gameno' starts a new round
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
bool
Party::starts_new_round(unsigned const gameno) const
{
  DEBUG_ASSERTION((gameno <= this->gameno()),
		  "Party::starts_new_round:\n"
		  "  'gameno' = " << gameno
		  << " > " << this->gameno() << " = this->gameno()");

  for (vector<unsigned>::const_iterator s = this->round_startgame().begin();
       s != this->round_startgame().end();
       ++s) {
    if (*s == gameno) {
      return true;
    }
    else if (*s > gameno) {
      return false;
    }
  }

  return false;
} // bool Party::starts_new_round(unsigned const gameno) const

/**
 **
 ** -> result
 **
 ** @param	gameno	number of the game
 **
 ** @return	the round game 'gameno' is in
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::round_of_game(unsigned const gameno) const
{
  DEBUG_ASSERTION((gameno <= this->gameno()),
		  "Party::round_of_game:\n"
		  "  'gameno' = " << gameno
		  << " > " << this->gameno() << " = this->gameno()");

  for (unsigned r = 1; r <= this->roundno(); ++r)
    if (this->round_startgame()[r] > gameno)
      return r - 1;

  return this->roundno();
} // unsigned Party::round_of_game(unsigned const gameno) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the game summary of the last game
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
GameSummary const&
Party::last_game_summary() const
{
  return *this->game_summaries().back();
} // GameSummary const& Party::last_game_summary() const;

/**
 **
 ** -> result
 **
 ** @param	gameno	number of the game
 **
 ** @return	the game summary of game 'gameno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
GameSummary const&
Party::game_summary(unsigned const gameno) const
{
  DEBUG_ASSERTION((gameno < this->game_summaries().size()),
		  "Party::game_summary(gameno):\n"
		  "  gameno = " << gameno << " is too great "
		  "(maximal " << this->game_summaries().size());
  return *this->game_summaries()[gameno];
} // GameSummary const& Party::game_summary(unsigned const gameno) const;

/**
 **
 ** -> result
 **
 ** @param	roundno	number of round
 **
 ** @return	the number of the first game in the round
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::round_startgame(unsigned const roundno) const
{
  return this->round_startgame()[roundno];
} // unsigned Party::round_startgame(unsigned const roundno) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	how many points has been made so far
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum() const
{
  if (this->game_summaries().empty())
    return 0;
  return this->pointsum_till_game(this->game_summaries().size() - 1);
} // int Party::pointsum() const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	how many points has been made till game 'gameno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_game(unsigned const gameno) const
{
  int points = 0;

  for (unsigned g = 0; g <= gameno; ++g)
    points += this->game_summary(g).points();

  return points;
} // int Party::pointsum_till_game(unsigned const gameno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 **
 ** @return	how many points has been made till round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_round(unsigned const roundno) const
{
  int points = 0;

  for (unsigned r = 0; r <= roundno; ++r)
    points += this->pointsum_in_round(r);

  return points;
} // int Party::pointsum_till_round(unsigned const roundno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 **
 ** @return	how many points has been made in round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_in_round(unsigned const roundno) const
{
  unsigned const gameno_end
    = ( (roundno == this->roundno())
       ? this->gameno() + ((::game_status == GAMESTATUS::GAME_FINISHED)
			   ? 1
			   : 0)
       : this->round_startgame(roundno + 1) );

  int points = 0;

  for (unsigned g = this->round_startgame(roundno); g < gameno_end; ++g)
    points += this->game_summary(g).points();

  return points;
} // int Party::pointsum_in_round(unsigned const roundno) const

/**
 **
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	how many points 'player' has made so far
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum(Player const& player) const
{
  return this->pointsum(player.no());
} // int Party::pointsum(Player player) const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made so far
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum(unsigned const playerno) const
{
  if (this->game_summaries().empty())
    return 0;
  return this->pointsum_till_game(this->game_summaries().size() - 1, playerno);
} // int Party::pointsum(unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	gameno		number of the game
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made till game 'gameno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_game(unsigned const gameno, unsigned const playerno) const
{
  int points = 0;

  for (unsigned g = 0; g <= gameno; ++g)
    points += this->game_summary(g).points(playerno);

  return points;
} // int Party::pointsum_till_game(unsigned const gameno, unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made till round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_round(unsigned const roundno,
			   unsigned const playerno) const
{
  int points = 0;

  for (unsigned r = 0; r <= roundno; ++r)
    points += this->pointsum_in_round(r, playerno);

  return points;
} // int Party::pointsum_till_round(unsigned const roundno, unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made in round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_in_round(unsigned const roundno, unsigned const playerno) const
{
  unsigned const gameno_end
    = ( (roundno == this->roundno())
       ? this->gameno() + ((::game_status == GAMESTATUS::GAME_FINISHED)
			   ? 1
			   : 0)
       : this->round_startgame(roundno + 1) );

  int points = 0;

  for (unsigned g = this->round_startgame(roundno); g < gameno_end; ++g)
    points += this->game_summary(g).points(playerno);

  return points;
} // int Party::pointsum_in_round(unsigned const roundno, unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the rang of 'player'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
unsigned
Party::rang(Player const& player) const
{
  return this->rang(player.no());
} // unsigned Party::rang(Player player) const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	the rang of the player
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
unsigned
Party::rang(unsigned const playerno) const
{
  unsigned rang = 0;
  int const points = this->pointsum(playerno);

  for (unsigned p = 0; p < this->playerno(); ++p)
    if ((p != playerno)
	&& (points < this->pointsum(p)))
      rang += 1;

  return rang;
} // unsigned Party::rang(unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the number of solo games 'player' has played
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 ** @todo	not calculate duty soli
 **
 **/
unsigned
Party::lustsolo_games(Player const& player) const
{
  return this->lustsolo_games(player.no());
} // unsigned Party::lustsolo_games(Player player) const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	the number of solo games the player has played
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 ** @todo	not calculate duty soli
 **
 **/
unsigned
Party::lustsolo_games(unsigned const playerno) const
{
  unsigned n = 0;

  for (vector<GameSummary*>::const_iterator
       game_summary = this->game_summaries().begin();
       game_summary != this->game_summaries().end();
       ++game_summary)
    if ((*game_summary)->is_solo()
	&& ((*game_summary)->soloplayer_no() == playerno) )
      n += 1;

  return n;
} // unsigned Party::lustsolo_games(unsigned const playerno) const

/**
 ** write the party into the output stream
 ** The current game is not written.
 **
 ** @param	ostr	output stream
 **
 ** @return	whether the writing was successful
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **
 ** @todo	output of the game?
 **/
bool
Party::write(ostream& ostr) const
{
  if (::game_status < GAMESTATUS::PARTY_PLAY) {
    ostr << "first seed = " << Unsigned(this->seed_first()) << "\n"
      << "first startplayer = " << Unsigned(this->startplayer_first()) << "\n"
      << "\n";
  } else {
    ostr << "first seed = " << Unsigned(this->real_seed_first()) << "\n"
      << "first startplayer = " << Unsigned(this->real_startplayer_first()) << "\n"
      << "\n";
  }

  ostr << "Rules \n"
    << "{\n"
    << this->rule()
    << "}\n";

  ostr<< '\n';

  ostr << "Players\n"
    << "{\n";
  for (vector<Player*>::const_iterator player
       = this->players().begin();
       player != this->players().end();
       player++)
    ostr << "{\n" << **player << "}\n";
  ostr << "}\n";

  ostr << '\n';

  ostr << "game summaries\n";
  ostr << "{\n";
  for (vector<GameSummary*>::const_iterator s = this->game_summaries().begin();
       s != this->game_summaries().end();
       ++s)
    ostr << "{\n" << **s << "}\n";
  ostr << "}\n";

  return ostr.good();
} // bool Party::write(ostream& ostr) const

/**
 ** reads the party from the input stream
 **
 ** @param	istr	input stream
 **
 ** @return	whether the reading was successful
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
Party::read(istream& istr)
throw (ReadException)
{
  // party config values
  Unsigned first_seed = UINT_MAX - 1;
  Unsigned first_startplayer = UINT_MAX - 1;
  Rule* rule = NULL;
  vector<Player*> players;
  vector<GameSummary*> game_summaries;

  Config config; // the config read
  int depth = 0; // the depth in the blocks
  try {
    do { // while (istr)
      istr >> config;

      if (istr.eof())
	break;

      if (istr.fail())
	break;

      if ((config.name == "") && (config.value == ""))
	break;

      if (config.value.empty()) {
	if (config.name == "{") {
	  depth += 1;
	} else if (config.name == "}") {
	  depth -= 1;
	  // finished with the party
	  if (depth == -1)
	    istr.putback('}');
	  if (depth <= 0)
	    break;

	} else if (config.name == "Rules") {
	  if (rule)
	    throw ReadException("Party read: rule already read");
	  rule = new Rule();
	  if (!rule->read(istr))
	    throw ReadException("Party read: rule failed");

	} else if (config.name == "Players") {
	  if (!players.empty())
	    throw ReadException("Party read: multiple players");

	  istr >> config;
	  if (! (config.value.empty()
		 && (config.name == "{")) )
	    throw ReadException("Party read: players: expected '{'");
	  while (istr.peek() != '}') {
	    Player* player = Player::new_(istr);
	    if (player == NULL)
	      throw ReadException("Party read: players: read of player failed");

	    players.push_back(player);

	    // overread empty lines
	    while ( (istr.peek() == '\n')
		   || (istr.peek() == '\r'))
	      istr.get();
	  } // while (istr.peek() != '}')

	  istr >> config;
	  if (! (config.value.empty()
		 && (config.name == "}")) )
	    throw ReadException("Party read: players: expected '}'");

	} else if (config.name == "game summaries") {
	  {
	    string const line = read_line(istr);
	    if (line != "{")
	      throw ReadException("Party read: game summaries: expected '{', "
				  " found '" + line + "'");
	  }
	  GameSummary* game_summary = NULL;
	  while (istr.good()
		 && istr.peek() != '}') {
	    try {
	      game_summary = new GameSummary(*rule, istr);
	    } catch(...) {
	      delete game_summary;
	      throw;
	    }
	    game_summaries.push_back(game_summary);
	  } // while (istr.good() && istr.peek() !=- '}')
	  {
	    string const line = read_line(istr);
	    if (line != "}")
	      throw ReadException("Party read: game summaries: expected '}', "
				  " found '" + line + "'");
	  }
	} else { // if (config.name == ...)
	  throw ReadException("Party read: unknown line "
			      "'" + config.name + "'");
	} // if (config.name == ...)
      } else { // if !(config.value.empty())
	if (config.name == "first seed") {
	  istringstream istr2(config.value);
	  istr2 >> first_seed;
	  if (!istr2.good() && !istr2.eof())
	    throw ReadException("Party read: error loading first seed");
	} else if (config.name == "first startplayer") {
	  istringstream istr2(config.value);
	  istr2 >> first_startplayer;
	  if (!istr2.good() && !istr2.eof())
	    throw ReadException("Party read: error loading first seed");
	} else { // if (config.name == ...)
	  throw ReadException("Party read: unknown line "
			      "'" + config.name + " = " + config.value + "'");
	} // if (config.name == ...)
      } // if !(config.value.empty())

    } while (istr.good());

    if (!istr.good() && !istr.eof())
      throw ReadException("Party read: unknown error in the input stream");

    if (first_seed == UINT_MAX - 1)
      throw ReadException("Party read: first seed not loaded");

    if (first_startplayer == UINT_MAX - 1)
      throw ReadException("Party read: first startplayer not loaded");

    if (!rule)
      throw ReadException("Party read: rule not loaded");

    if (players.size() != (*rule)(Rule::NUMBER_OF_PLAYERS_IN_GAME))
      throw ReadException("Party read: incorrect number of players");

  } catch (...) {
    delete rule;
    for (vector<Player*>::iterator p = players.begin();
	 p != players.end();
	 ++p)
      delete *p;
    for (vector<GameSummary*>::iterator g = game_summaries.begin();
	 g != game_summaries.end();
	 ++g)
      delete *g;
    throw;
  } // try

  ::game_status = GAMESTATUS::PARTY_LOADED;
  delete this->rule_;
  this->rule_ = rule;

  for (vector<Player*>::iterator p = this->players_.begin();
       p != this->players_.end();
       ++p)
    delete *p;
  this->players_ = players;
  for (unsigned p = 0; p < this->playerno(); ++p)
    this->player(p).set_no(p);

  this->seed_first_
    = this->real_seed_first_
    = first_seed;
  this->startplayer_first_
    = this->real_startplayer_first_
    = first_startplayer;

  for (vector<GameSummary*>::iterator g = this->game_summaries_.begin();
       g != this->game_summaries_.end();
       ++g)
    delete *g;
  this->game_summaries_ = game_summaries;

  if (this->game_summaries_.empty()) {
    this->seed_ = this->seed_first();
    this->startplayer_ = this->startplayer_first();
    this->round_startgame_.clear();
  } else { // if !(this->game_summaries_.empty())
    this->seed_ = this->game_summaries_.back()->seed();
    this->startplayer_ = this->game_summaries_.back()->startplayer_no();

    { // calculate round startgame
      unsigned gameno = 0;
      this->round_startgame_.clear();
      this->round_startgame_.push_back(0);
      for (vector<GameSummary*>::const_iterator
	   gs = this->game_summaries_.begin();
	   gs != this->game_summaries_.end();
	   ++gs, ++gameno)
	if (! (GAMETYPE::is_solo((*gs)->game_type())
	       && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS)))
	  if ((((*gs)->startplayer_no() + 1) % this->playerno())
	      == this->startplayer_first())
	    this->round_startgame_.push_back(gameno + 1);
    } // calculate round startgame

    // we want to start the following game
    if ( !(GAMETYPE::is_solo(this->game_summaries().back()->game_type())
	   && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS)))
      (this->startplayer_ += 1) %= this->playerno();
    this->seed_next();
  } // if !(this->game_summaries_.empty())

  return (istr.good() || istr.eof());
} // bool Party::read(istream& istr)

/**
 **
 ** saves the party in the given file
 **
 ** @param	filename	file to save the party in
 **
 ** @return	whether the party could be saved
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
Party::save(string const& filename) const
{
  ofstream ostr(filename.c_str());
  if (ostr.good()) {
    ostr
      << "#!FreeDoko\n"
      << "# FreeDoko party (version " << ::version << ")\n"
      << '\n';

    this->write(ostr);
  }

  return ostr.good();
} // bool Party::save(string const& filename) const

/**
 ** loads the party in the given file
 **
 ** @param	filename	file to load the party from
 **
 ** @return	whether the loading was successful
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
Party::load(string const& filename)
throw (ReadException)
{
  try {
    ifstream istr(filename.c_str());
    if (istr.fail())
      throw ReadException("Party load: open failed");

    this->read(istr);

    if (!istr.good() && !istr.eof())
      throw ReadException("Party load: unknown error");
  } catch (ReadException const& exception) {
    ::ui->information(::translation("Message::party load: load error '%sfilename%'",
				    filename)
		      + "\n"
		      + exception.message(),
		      INFORMATION::PROBLEM);
    return false;
  } // try

  return true;
} // bool Party::load(string const& filename)

/**
 ** inits the party:
 ** get the info of the first startplayer, the first seed,
 ** the ruleset and the players
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
void
Party::init()
{
  for (vector<Player*>::const_iterator player = this->players().begin();
       player != this->players().end();
       player++)
    ::ui->name_update(**player);

  if (   dynamic_cast<UI_Wrap*>(::ui)
      && (dynamic_cast<UI_Wrap*>(::ui)->ui->type() == UI_TYPE::DUMMY)) {
      this->set_playertype(0, Player::AI);
  } // if (::ui->type() == UI_TYPE::NONE)


  // set the seed
  this->set_seed(this->seed_first());
  // set the startplayer
  if (this->startplayer_first() == UINT_MAX) {
    this->startplayer_ = (static_cast<unsigned>(::time(NULL))
			  % this->playerno());
  } else {
    this->startplayer_ = this->startplayer_first();
  }

  this->real_seed_first_ = this->seed();
  this->real_startplayer_first_ = this->startplayer();

  if (::setting(Setting::SAVE_PARTY_CHANGES))
    this->save_changes();

  return ;
} // void Party::init()

/**
 **
 ** the party is opened
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::open()
{
  return ;
} // void Party::open()

/**
 **
 ** play the party:
 ** play games until the user ends the party
 ** or the party is ended according to the rules
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::play()
{
  using namespace GAMESTATUS;

  ::ui->party_start();
  if (this->gameno() == 0)
    this->round_startgame_.push_back(0);
  try {
    while (::game_status == PARTY_PLAY) {
      if (this->round_startgame_.back()
	  == this->gameno())
	::ui->party_start_round(this->roundno());

      if (SEED_OUT)
	this->startplayer_ = 0;

      // create a new game
      ::game_status = GAME_NEW;
      this->game_ = new Game(*this, *(this->players_[this->startplayer()]));
      try {
	this->game().play();
	if (SEED_OUT) {
	  for (vector<Player*>::const_iterator p = this->players().begin();
	       p != this->players().end();
	       ++p)
	    (*p)->game_close(this->game());
	  ::ui->game_close();

	  delete this->game_;
	  this->game_ = NULL;
	  this->seed_next();
	  ::game_status = PARTY_PLAY;
	  continue;
	}

	DEBUG_ASSERTION((::game_status == GAME_PLAY),
			"Party::play():\n"
			"  wrong gamestatus (needs 'GAME_PLAY': " << ::game_status);

	::game_status = GAME_FINISHED;
	this->game().finish();

	this->game_summaries_.push_back(new GameSummary(this->game()));
	for (unsigned p = 0; p < this->playerno(); ++p)
	  this->player(p).db_->evaluate(this->player(p),
					*this->game_summaries().back());

	if (::setting(Setting::SAVE_PARTY_CHANGES))
	  this->save_changes();

	::ui->game_finish();
      } catch (...) {
	for (vector<Player*>::const_iterator p = this->players().begin();
	     p != this->players().end();
	     ++p)
	  (*p)->game_close(this->game());
	::ui->game_close();

	delete this->game_;
	this->game_ = NULL;

	throw;
      } // try

      for (vector<Player*>::const_iterator p = this->players().begin();
	   p != this->players().end();
	   ++p)
	(*p)->game_close(this->game());
      ::ui->game_close();

      ::game_status = PARTY_PLAY;
#ifdef OUTDATED
      // 0.6.6
      if ((this->game().type() == GAMETYPE::NORMAL)
	  || ((this->game().type() == GAMETYPE::POVERTY)
	      && (this->rule()(Rule::POVERTY_SHIFT))
	     )
	  || (this->game().type() == GAMETYPE::MARRIAGE))
#endif
	if (!GAMETYPE::is_solo(this->game().type())
	    || !this->rule()(Rule::LUSTSOLO_PLAYER_LEADS)) {

	  if ( ((this->startplayer_ + 1) % this->playerno())
	      == this->startplayer_first_) {
	    // end of round

	    if (this->rule()(Rule::NUMBER_OF_ROUNDS_LIMITED)
		&& (this->roundno() + 1 >= this->rule()(Rule::NUMBER_OF_ROUNDS)) )
	      ::game_status = GAMESTATUS::PARTY_FINISHED;
	    else { 
	      this->round_startgame_.push_back(this->gameno() + 1);
	    }
	  } // if (round ended)

	  (this->startplayer_ += 1) %= this->playerno();
	} // if (next player)

      delete this->game_;
      this->game_ = NULL;

      if (MUTATE_RULE)
	this->rule_->mutate();
      this->seed_next();
    } // while (game_status == PARTY_PLAY)
  } catch (GameStatus const& gamestatus) {
    if (gamestatus != GAMESTATUS::PARTY_FINISHED)
      throw;

    ::game_status = GAMESTATUS::PARTY_FINISHED;
  } // try

  if (::setting(Setting::SAVE_PARTY_CHANGES))
    this->remove_auto_save();

  return ;
} // void Party::play()

/**
 **
 ** the party is closed
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::close()
{
  this->round_startgame_.clear();

  for (vector<GameSummary*>::iterator gs = this->game_summaries_.begin();
       gs != this->game_summaries_.end();
       ++gs)
    delete *gs;
  this->game_summaries_.clear();

  return ;
} // void Party::close()

/**
 ** save the file
 **
 ** @param	-
 **
 ** @return	whether the saving was successful
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
bool
Party::auto_save() const
{
#ifdef USE_NETWORK
  if (::server)
    return false;
#endif
  // save the party in the private data directory
  return this->save(::setting(Setting::PRIVATE_DATA_DIRECTORY)
		    + "/"
		    + ::setting(Setting::PARTIES_DIRECTORY)
		    + "/"
		    + "current");
} // bool Party::auto_save() const

/**
 ** remove the auto saved file
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::remove_auto_save() const
{
  std::remove((::setting(Setting::PRIVATE_DATA_DIRECTORY)
	       + "/"
	       + ::setting(Setting::PARTIES_DIRECTORY)
	       + "/"
	       + "current").c_str());

  return;
} // void Party::remove_auto_save() const

/**
 ** save the changes
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
Party::save_changes() const
{
  ::ui->set_busy();
  try {
    this->auto_save();
    // save the rules in the private data directory
    this->rule().save(::setting(Setting::PRIVATE_DATA_DIRECTORY)
		      + "/"
		      + ::setting(Setting::RULES_DIRECTORY)
		      + "/"
		      + "last");

    { // save the players in the private data directory
      unsigned n = 0;
      for (vector<Player*>::const_iterator player = this->players().begin();
	   player != this->players().end();
	   ++n, ++player) {
	if ((*player)->type() != Player::NETWORK) {
	  (*player)->save(::setting(Setting::PRIVATE_DATA_DIRECTORY)
			  + "/player." + DK::Utils::String::to_string(n + 1));
	}
      }
    } // save the players in the private data directory
    ::ui->set_not_busy();
  } catch(...) {
    ::ui->set_not_busy();
    throw;
  } // try

  return ;
} // void Party::save_changes() const

/**
 ** write the party into the output stream
 **
 ** @param	ostr	output stream
 ** @param	party	party
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
ostream&
operator<<(ostream& ostr, Party const& party)
{
  party.write(ostr);

  return ostr;
} // ostream& operator<<(ostream& ostr, Party const& party)
