/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2004  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 "constants.h"

#include "game.h"

#include "../party/party.h"
#include "../party/rule.h"
#include "../ui/ui.h"
#include "../misc/setting.h"

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	number of players in the game
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @todo	to be removed
 **
 **/
unsigned
Game::playerno() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::playerno()");

  DEBUG_RETURNING(this->rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME),
		  INFO_GAME && INFO_VALUE,
		  "Game::playerno()");
} // unsigned Game::playerno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	number of players in the game
 **
 ** @version	0.5.2
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
unsigned
Game::number_of_players() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::number_of_players()");

  DEBUG_RETURNING(this->rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME),
		  INFO_GAME && INFO_VALUE,
		  "Game::number_of_players()");
} // unsigned Game::number_of_players() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	number of humans in the game
 **
 ** @version	0.4.4
 **
 ** @author	Diether Knof
 **
 **/
unsigned
Game::humanno() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::humanno()");

  // count the number of human players
  unsigned human_no = 0;
  for (vector<Player*>::const_iterator player = this->player_begin();
       player != this->player_end();
       player++)
    if ((*player)->type() == Player::HUMAN)
      human_no += 1;

  DEBUG_RETURNING(human_no,
		  INFO_GAME && INFO_VALUE,
		  "Game::humanno()");
} // unsigned Game::humanno() const

/**
 **
 ** -> result
 **
 ** @param	player	player, whose number is returned
 **
 ** @return	number of the given player
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
unsigned
Game::no_of_player(Player const& player) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::no_of_player(player)");

#ifdef POSTPHONED
  unsigned n = 0;
  for (vector<Player*>::const_iterator p = this->player_begin();
       p != this->player_end();
       p++, n++)
    if (**p == player)
      DEBUG_RETURNING(n,
		      INFO_GAME && INFO_VALUE,
		      "Game::no_of_player(player)");

  DEBUG_ASSERTION(false,
		  "Game::no_of_player(player):\n"
		  "  player not found in the game-players");

  DEBUG_RETURNING(UINT_MAX,
		  INFO_GAME && INFO_VALUE,
		  "Game::no_of_player(player)");
#else
  DEBUG_RETURNING(player.no(),
		  INFO_GAME && INFO_VALUE,
		  "Game::no_of_player(player)");
#endif
} // unsigned Game::no_of_player(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	human player if there exists exactly one,
 **		else 'NULL'
 **
 ** @version	0.6.2
 **
 ** @author	Diether Knof
 **
 **/
Player*
Game::human_player()
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::human_player()");

  if (this->humanno() != 1)
    DEBUG_RETURNING(NULL,
		    INFO_GAME && INFO_VALUE,
		    "Game::human_player()");

  for (vector<Player*>::iterator p = this->player_p.begin();
       p != this->player_p.end();
       ++p)
    if ((*p)->type() == Player::HUMAN)
      DEBUG_RETURNING(*p,
		      INFO_GAME && INFO_VALUE,
		      "Game::human_player()");


  DEBUG_RETURNING(NULL,
		  INFO_GAME && INFO_VALUE,
		  "Game::human_player()");
} // Player* Game::human_player()

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	human player if there exists exactly one,
 **		else 'NULL'
 **
 ** @version	0.6.2
 **
 ** @author	Diether Knof
 **
 **/
Player const*
Game::human_player() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::human_player()");

  if (this->humanno() != 1)
    DEBUG_RETURNING(NULL,
		    INFO_GAME && INFO_VALUE,
		    "Game::human_player()");

  for (vector<Player*>::const_iterator p = this->player_p.begin();
       p != this->player_p.end();
       ++p)
    if ((*p)->type() == Player::HUMAN)
      DEBUG_RETURNING(*p,
		      INFO_GAME && INFO_VALUE,
		      "Game::human_player()");


  DEBUG_RETURNING(NULL,
		  INFO_GAME && INFO_VALUE,
		  "Game::human_player()");
} // Player const* Game::human_player() const

/**
 **
 ** -> result
 **
 ** @param	p	number of player which should be result of this method
 **
 ** @return	player number 'p'
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const&
Game::player(unsigned const& p) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player(p)");

  DEBUG_ASSERTION((p < this->number_of_players()),
		  "Game::player(p):\n"
		  "  'p' too great: " << p);

  DEBUG_RETURNING(*(this->player_p[p]),
		  INFO_GAME && INFO_VALUE,
		  "Game::player(p)");
} // Player const& Game::player(unsigned const& p) const

/**
 **
 ** -> result
 **
 ** @param	p	number of player which should be result of this method
 **
 ** @return	player number 'p'
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player&
Game::player(unsigned const& p)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player(p)");

  DEBUG_ASSERTION((p < this->number_of_players()),
		  "Game::player(p):\n"
		  "  'p' too great");

  DEBUG_RETURNING(*(this->player_p[p]),
		  INFO_GAME && INFO_VALUE,
		  "Game::player(p)");
} // Player& Game::player(unsigned const& p)

#ifdef OUTDATED
// 0.6.7
// now the players have to be set in the constructor
/**
 **
 ** set the players
 **
 ** @param	players	the new players
 **
 ** @return	-
 **
 ** @version	0.4.4
 **
 ** @author	Diether Knof
 **
 **/
void
Game::players_set(vector<Player*>& players)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::players_set(players)");

  DEBUG_ASSERTION((players.size() == this->number_of_players()),
		  "Game::players_set(players):\n"
		  "  the sizes of the vectors do not match");

  // update the pointers
  this->startplayer_p = players[this->startplayer().no()];
  this->player_current_p = players[this->player_current().no()];
  if (this->soloplayer_p != NULL)
    this->soloplayer_p = players[this->soloplayer().no()];
  // update the startplayer of the tricks
  for (vector<Trick>::iterator trick = this->trick_p.begin();
       trick != this->trick_p.end();
       trick++) {
    trick->startplayer_p = players[trick->startplayer().no()];
    trick->winnerplayer_p = players[trick->winnerplayer().no()];
  }

  // you have to set here, because the old players are used above
  this->player_p = players;

  // set the game of the players
  for (vector<Player*>::iterator player = this->player_p.begin();
       player != this->player_p.end();
       player++)
    (*player)->game_set(*this);

  for (vector<Trick>::iterator trick = this->trick_p.begin();
       trick != this->trick_p.end();
       trick++) {
    DEBUG_ASSERTION((&trick->startplayer().game() == this),
		    "");
  }

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_VALUE,
		  "Game::players_set(players)");
} // void Game::players_set(vector<Player*>& players)
#endif

/**
 **
 ** -> result
 **
 ** @param	player	player, whose following player is returned
 **
 ** @return	the next player after 'player'
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const&
Game::player_following(Player const& player) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_following(player)");

  unsigned n = 0;
  for (vector<Player*>::const_iterator p = this->player_begin();
       p != this->player_end();
       p++, n++)
    if (**p == player) {
      p++;
      if (p == this->player_end())
	// this was the last player - return the first
	DEBUG_RETURNING(this->player(0),
			INFO_GAME && INFO_VALUE,
			"Game::player_following(player)");
      else
	DEBUG_RETURNING(**p,
			INFO_GAME && INFO_VALUE,
			"Game::player_following(player)");
    } // if (**p == player)

  DEBUG_ASSERTION(false,
		  "Game::player_following(player):\n"
		  "  player not found in the game-players");

  DEBUG_RETURNING(player,
		  INFO_GAME && INFO_VALUE,
		  "Game::player_following(player)");
} // Player const& Game::player_following(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param	player	player, whose following player is returned
 **
 ** @return	the next player after 'player'
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player&
Game::player_following(Player& player)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_following(player)");

  unsigned n = 0;
  for (vector<Player*>::iterator p = this->player_p.begin();
       p != this->player_p.end();
       p++, n++)
    if (**p == player) {
      p++;
      if (p == this->player_p.end())
	// this was the last player - return the first
	DEBUG_RETURNING(this->player(0),
			INFO_GAME && INFO_VALUE,
			"Game::player_following(player)");
      else
	DEBUG_RETURNING(**p,
			INFO_GAME && INFO_VALUE,
			"Game::player_following(player)");
    } // if (**p == player)

  DEBUG_ASSERTION(false,
		  "Game::player_following(player):\n"
		  "  player not found in the game-players");

  DEBUG_RETURNING(player,
		  INFO_GAME && INFO_VALUE,
		  "Game::player_following(player)");
} // Player& Game::player_following(Player& player)

/**
 **
 ** -> result
 **
 ** @param	player	player, whose previous player is returned
 **
 ** @return	the previous player of 'player'
 **
 ** @version	0.6.4
 **
 ** @author	Borg Enders
 **
 **/
Player const&
Game::player_previous( Player const& player ) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_previous(player)");

  for (vector<Player*>::const_reverse_iterator p = this->player_p.rbegin();
       p != this->player_p.rend();
       p++)
    if (**p == player) {
      p++;
      if (p == this->player_p.rend())
	// this was the last player - return the first
	DEBUG_RETURNING(this->player(this->playerno() - 1),
			INFO_GAME && INFO_VALUE,
			"Game::player_previous(player)");
      else
	DEBUG_RETURNING(**p,
			INFO_GAME && INFO_VALUE,
			"Game::player_previous(player)");
    } // if (**p == player)

  DEBUG_ASSERTION(false,
		  "Game::player_previous(player):\n"
		  "  player not found in the game-players");

  DEBUG_RETURNING(player,
		  INFO_GAME && INFO_VALUE,
		  "Game::player_previous(player)");
} // Player const& Game::player_previous(Player const& player)

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	begin-iterator of the player-vector
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
vector<Player*>::iterator
Game::player_begin()
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_begin()");

  DEBUG_RETURNING(this->player_p.begin(),
		  INFO_GAME && INFO_VALUE,
		  "Game::player_begin()");
} // vector<Player*>::iterator Game::player_begin() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	end-iterator of the player-vector
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
vector<Player*>::iterator
Game::player_end()
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_end()");

  DEBUG_RETURNING(this->player_p.end(),
		  INFO_GAME && INFO_VALUE,
		  "Game::player_end()");
} // vector<Player*>::iterator Game::player_end() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	begin-iterator of the player-vector
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
vector<Player*>::const_iterator
Game::player_begin() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_begin()");

  DEBUG_RETURNING(this->player_p.begin(),
		  INFO_GAME && INFO_VALUE,
		  "Game::player_begin()");
} // vector<Player*>::const_iterator Game::player_begin() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	end-iterator of the player-vector
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
vector<Player*>::const_iterator
Game::player_end() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_end()");

  DEBUG_RETURNING(this->player_p.end(),
		  INFO_GAME && INFO_VALUE,
		  "Game::player_end()");
} // vector<Player*>::const_iterator Game::player_end() const

/**
 **
 ** sort the hands of the players
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
void
Game::players_hands_sort()
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::players_hands_sort()");

  if (::setting(Setting::CARDS_ORDER).sorted()) {
    for (vector<Player*>::iterator player = this->player_p.begin();
	 player != this->player_p.end();
	 player++) {
      (*player)->hand_sort();
    } // for (player)
  } // if (::setting(Setting::CARDS_ORDER).sorted())

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_VALUE,
		  "Game::players_hands_sort()");
} // void Game::players_hands_sort()

/**
 **
 ** mix the hands of the players
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
void
Game::players_hands_mix()
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::players_hands_mix()");

  if (!::setting(Setting::CARDS_ORDER).sorted()) {
    for (vector<Player*>::iterator player = this->player_p.begin();
	 player != this->player_p.end();
	 player++) {
      (*player)->hand_mix();
    } // for (player)
  } // if (::setting(Setting::CARDS_ORDER).mixed())

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_VALUE,
		  "Game::players_hands_mix()");
} // void Game::players_hands_mix()

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	player whose has started the game
 **
 ** @version	0.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const&
Game::startplayer() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::startplayer()");

  DEBUG_ASSERTION(this->startplayer_p,
		  "Game::startplayer:\n"
		  "  this->startplayer_p == NULL");

  DEBUG_RETURNING(*(this->startplayer_p),
		  INFO_GAME && INFO_VALUE,
		  "Game::startplayer()");
} // Player const& Game::startplayer() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	whether a soloplayer exists
 **
 ** @version	0.6.7
 **
 ** @author	Diether Knof
 **
 **/
bool
Game::exists_soloplayer() const
{
  return this->soloplayer_p;
} // bool Game::exists_soloplayer() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	soloplayer
 **
 ** @version	0.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const&
Game::soloplayer() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::soloplayer()");

  DEBUG_ASSERTION(this->soloplayer_p,
		  "Game::soloplayer:\n"
		  "  this->soloplayer_p == NULL");

  DEBUG_RETURNING(*(this->soloplayer_p),
		  INFO_GAME && INFO_VALUE,
		  "Game::soloplayer()");
} // Player const& Game::soloplayer() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	soloplayer
 **
 ** @version	0.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player&
Game::soloplayer()
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::soloplayer()");

  DEBUG_ASSERTION(this->soloplayer_p,
		  "Game::soloplayer:\n"
		  "  this->soloplayer_p == NULL");

  DEBUG_RETURNING(*(this->soloplayer_p),
		  INFO_GAME && INFO_VALUE,
		  "Game::soloplayer()");
} // Player& Game::soloplayer()


/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the player who takes the poverty
 **
 ** @version	0.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const&
Game::povertyPartner() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::soloplayer()");

  if (this->type() == GAMETYPE::POVERTY )
    for (vector<Player*>::const_iterator player = this->player_begin();
	 player != this->player_end();
	 player++)
      if ((*player)->team() == TEAM::RE && 
	  !(*player == this->soloplayer_p )) 
	DEBUG_RETURNING(**player,
			INFO_GAME && INFO_VALUE,
			"Game::soloplayer()");

  DEBUG_RETURNING( **(this->player_end()),
		   INFO_GAME && INFO_VALUE,
		   "Game::soloplayer()");
} // Player const& Game::povertyPartner() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	player whose turn it is
 **
 ** @version	0.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const&
Game::player_current() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_current()");

  DEBUG_ASSERTION(this->player_current_p,
		  "Game::player_current:\n"
		  "  this->player_current_p == NULL");

  DEBUG_RETURNING(*(this->player_current_p),
		  INFO_GAME && INFO_VALUE,
		  "Game::player_current()");
} // Player const& Game::player_current() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	player whose turn it is
 **
 ** @version	0.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player&
Game::player_current()
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::player_current()");

  DEBUG_ASSERTION(this->player_current_p,
		  "Game::player_current:\n"
		  "  this->player_current_p == NULL");

  DEBUG_RETURNING(*(this->player_current_p),
		  INFO_GAME && INFO_VALUE,
		  "Game::player_current()");
} // Player& Game::player_current()
