/**********************************************************************
 *
 *   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 "constants.h"

#include "game.h"

#include "gameplay.h"
#include "../party/party.h"
#include "../party/rule.h"
#include "../player/player.h"
#include "../card/trick.h"

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

/**
 **
 ** constructor
 **
 ** @param	party		party, the game belongs to
 ** @param	startplayer	the startplayer of the game
 **
 ** @return	-
 **
 ** @version	0.6.1
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Game::Game(Party& party, Player& startplayer) :
  party_(&party),
  gameplay_(new Gameplay),
  players_(party.players_),
  startplayer_(&startplayer),
  player_current_(&startplayer),
  seed_(party.seed()),
  type_(GAMETYPE::NORMAL),
  soloplayer_(NULL),
  marriage_selector_(MARRIAGE_SELECTOR::TEAM_SET),
  marriage_determination_trickno_(0),
  poverty_cards_(),
  poverty_cardno_(UINT_MAX),
  poverty_shifted_(false),
  reservation_(),
  tricks_(),
  teaminfo_(party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME), TEAM::UNKNOWN),
  human_teaminfo_(party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME), TEAM::UNKNOWN),
  announcement_(party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME)),
  announcement_replied_on_(),
  swines_owner_(NULL),
  first_fox_catcher_(NULL),
  swines_announced_(false),
  hyperswines_owner_(NULL),
  hyperswines_announced_(false),
  finished_(false)
{
  dynamic_cast<UI_Wrap*>(::ui)->prepend(this->gameplay_);

  unsigned p = 0;
  for (vector<Player*>::iterator player = this->players().begin();
       player != this->players().end();
       player++, p++) {
    (*player)->new_game(*this);
    (*player)->set_no(p);
  } // for (player \in this->players())

  return ;
} // Game::Game(Party& party, Player const& startplayer)

/**
 **
 ** constructor
 **
 ** @param	game	game to copy
 ** @param	players	the players for the new game
 **
 ** @return	-
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
Game::Game(Game const& game, vector<Player*>& players) :
  party_(game.party_),
  gameplay_(NULL),
  players_(players),
  startplayer_(game.startplayer_),
  player_current_(game.player_current_),
  seed_(game.seed_),
  type_(game.type_),
  soloplayer_(game.soloplayer_),
  marriage_selector_(game.marriage_selector_),
  marriage_determination_trickno_(game.marriage_determination_trickno_),
  poverty_cards_(game.poverty_cards_),
  poverty_shifted_(game.poverty_shifted_),
  tricks_(game.tricks_),
  teaminfo_(game.teaminfo_),
  human_teaminfo_(game.human_teaminfo_),
  announcement_(game.announcement_),
  announcement_replied_on_(game.announcement_replied_on_),
  swines_owner_(game.swines_owner_),
  first_fox_catcher_(game.first_fox_catcher_),
  swines_announced_(game.swines_announced_),
  hyperswines_owner_(game.hyperswines_owner_),
  hyperswines_announced_(game.hyperswines_announced_),
  finished_(game.finished_)
{
  // set the players
  if (game.startplayer_)
    this->startplayer_ = players[game.startplayer_->no()];
  if (game.player_current_)
    this->player_current_ = players[game.player_current_->no()];
  if (game.soloplayer_)
    this->soloplayer_ = players[game.soloplayer_->no()];
  if (game.swines_owner_)
    this->swines_owner_ = players[game.swines_owner_->no()];
  if (game.first_fox_catcher_)
    this->first_fox_catcher_ = players[game.first_fox_catcher_->no()];
  if (game.hyperswines_owner_)
    this->hyperswines_owner_ = players[game.hyperswines_owner_->no()];

  // update the pointers of the tricks
  for (vector<Trick*>::iterator trick = this->tricks().begin();
       trick != this->tricks().end();
       trick++) {
    *trick = new Trick(**trick);
    (*trick)->game_ = this;
    (*trick)->startplayer_ = (*trick)->startplayer().no();
    (*trick)->winnerplayer_ = (*trick)->winnerplayer().no();
  }

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

  DEBUG_ASSERT(this->self_check());

  return ;
} // Game::Game(Game const& game)

/**
 **
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Game::~Game()
{
  if (this->gameplay_) {
    dynamic_cast<UI_Wrap*>(::ui)->remove(this->gameplay_);
    delete this->gameplay_;
  }

  for (vector<Trick*>::iterator t = this->tricks().begin();
       t != this->tricks().end();
       ++t)
    delete *t;

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

/**
 ** self check
 ** when an error is found, an ASSERTION is created
 **
 ** @param	-
 **
 ** @return	wether the self-check was successful (no error)
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
bool
Game::self_check() const
{
  if (!this->isvirtual())
    DEBUG_ASSERTION((this == &::party.game()),
		    "Game::self_check()\n"
		    "  this game is not virutal and not the game of the party");

  DEBUG_ASSERTION(this->party_,
		  "Game::self_check()\n"
		  "  no pointer to a party\n"
		  "  this = " << this);

  for (vector<Player*>::const_iterator p = this->players().begin();
       p != this->players().end();
       ++p) {
    DEBUG_ASSERTION((this == &(*p)->game()),
		    "Game::self_check()\n"
		    "  a player is not of this game");
    DEBUG_ASSERTION((*p)->self_check(),
		    "Game::self_check()\n"
		    "  self check of a player returned 'false'");
  }

  for (vector<Trick*>::const_iterator t = this->tricks().begin();
       t != this->tricks().end();
       ++t) {
    DEBUG_ASSERTION((this == &(*t)->game()),
		    "Game::self_check()\n"
		    "  a trick is not of this game");

    DEBUG_ASSERTION((*t)->self_check(),
		    "Game::self_check()\n"
		    "  self check of a trick returned 'false'");
  } // for (t \in this->tricks())

  if (this->startplayer_)
    DEBUG_ASSERTION((this == &this->startplayer_->game()),
		    "Game::self_check()\n"
		    "  the startplayer is not of the same game");

  if (this->player_current_)
    DEBUG_ASSERTION((this == &this->player_current_->game()),
		    "Game::self_check()\n"
		    "  the current player is not of the same game");

  if (this->soloplayer_)
    DEBUG_ASSERTION((this == &this->soloplayer_->game()),
		    "Game::self_check()\n"
		    "  the soloplayer is not of the same game");
  if (this->swines_owner_)
    DEBUG_ASSERTION((this == &this->swines_owner_->game()),
		    "Game::self_check()\n"
		    "  the swines owner is not of the same game");
  if (this->first_fox_catcher_)
    DEBUG_ASSERTION((this == &this->first_fox_catcher_->game()),
		    "Game::self_check()\n"
		    "  the first fox catcher is not of the same game");
  if (this->hyperswines_owner_)
    DEBUG_ASSERTION((this == &this->hyperswines_owner_->game()),
		    "Game::self_check()\n"
		    "  the hyperswines owner is not of the same game");


  return true;
} // bool Game::self_check() const

/**
 **
 ** output of the game
 **
 ** @param	ostr	output stream
 **
 ** @return	output stream
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
ostream&
Game::write(ostream& ostr) const
{
  unsigned i = 0;

  ostr << "Game:\n"
    << "-----\n"
    << "\n"
    << "seed" << ": " << this->seed() << "\n"
    << "current trick number: " << this->trick_current_no() << "\n"
    << "type: " << this->type() << "\n";

#ifdef OUTDATED
  // DK 2005-03-31
  // this information is saved in the party

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

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

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

  ostr << "\n"
    << "tricks:\n"
    << "\n";
  i = 0;
  for (vector<Trick*>::const_iterator trick = this->tricks().begin();
       trick != this->tricks().end();
       trick++, i++)
    ostr << i << ". trick:\n"
      << **trick
      << "\n";

  ostr << "\n";

  return ostr;
} // ostream& Game::write(ostream& ostr) const

/**
 **
 ** output of the tricks
 **
 ** @param	ostr	output stream
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
ostream&
Game::write_tricks(ostream& ostr) const
{
  ostr << "\n"
    << "tricks:\n"
    << "\n";
  unsigned i = 0;
  for (vector<Trick*>::const_iterator trick = this->tricks().begin();
       trick != this->tricks().end();
       trick++, i++)
    ostr << i << ". trick:\n"
      << **trick
      << "\n";

  ostr << "\n";

  return ostr;
} // ostream& Game::write_tricks(ostream& ostr) const

/**
 *
 ** output of the game
 *
 ** @param	ostr	output stream
 ** @param	game	game to be printed
 *
 ** @return	output stream
 *
 ** @version	0.4.4
 *
 ** @author	Borg Enders
 ** @author	Diether Knof
 *
 */
ostream&
operator<<(ostream& ostr,  Game const& game)
{
  game.write(ostr);

  return ostr;
} // ostream& operator<<(ostream& ostr,  Game const& game)

/**
 *
 ** -> result
 *
 ** @param	-
 *
 ** @return	whether this is a virtual game
 *
 ** @version	0.5.4
 *
 ** @author	Borg Enders
 ** @author	Diether Knof
 *
 */
bool
Game::isvirtual() const
{
  return (this != &this->party().game());
} // bool Game::isvirtual() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	whether the game is finished
 **
 ** @version	0.6.2
 **
 ** @author	Diether Knof
 *
 */
bool
Game::is_finished() const
{
  return this->finished_;
} // bool Game::is_finished() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the rules of the game
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 */
Party&
Game::party()
{
  return *this->party_;
} // PArty& Game::party()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the rules of the game
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 */
Rule const&
Game::rule() const
{
  return this->party().rule();
} // Rule const& Game::rule() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	current game-type
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @todo	show 'MARRIAGE_SILENT' if both club queens have been played
 **
 **/
GameType
Game::type() const
{
  if (!this->is_finished()
      && (this->type_ == GAMETYPE::MARRIAGE_SILENT) )
    return GAMETYPE::NORMAL;

  return this->type_;
} // GameType Game::type() const

/**
 **
 ** sets the gametype
 **
 ** @param	type	new gametype
 **
 ** @return	-
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
void
Game::set_type(GameType const type)
{
  this->type_ = type;

  return ;
} // void Game::set_type(GameType type)

/**
 ** -> result
 **
 ** @param     tcolor   tcolor to look at
 **
 ** @return    number of cards of 'tcolor'
 **
 ** @author    Diether Knof
 **
 ** @version   0.7.3
 **/
unsigned
Game::numberof(Card::TColor const& tcolor) const
{
  if (tcolor == Card::TRUMP) {
    switch (this->type()) {
    case GAMETYPE::NORMAL:
    case GAMETYPE::MARRIAGE:
    case GAMETYPE::MARRIAGE_SOLO:
    case GAMETYPE::MARRIAGE_SILENT:
    case GAMETYPE::GENSCHER:
    case GAMETYPE::POVERTY:
    case GAMETYPE::FOX_HIGHEST_TRUMP:
    case GAMETYPE::SOLO_CLUB:
    case GAMETYPE::SOLO_SPADE:
    case GAMETYPE::SOLO_HEART:
    case GAMETYPE::SOLO_DIAMOND:
      return ( (this->rule()(Rule::NUMBER_OF_CARD_VALUES) // trump color
	      + (this->rule()(Rule::NUMBER_OF_CARD_COLORS) - 1) // jack
	      + (this->rule()(Rule::NUMBER_OF_CARD_COLORS) - 1) // queen
	      + ((   this->rule()(Rule::DOLLEN)
		  && (this->trumpcolor() != Card::DOLLE.color()) )
		 ? 1
		 : 0)
	     ) * this->rule()(Rule::NUMBER_OF_SAME_CARDS) );
    case GAMETYPE::THROWN_NINES:
    case GAMETYPE::THROWN_KINGS:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::THROWN_NINES_AND_KINGS:
      return (2
	      * this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::SOLO_MEATLESS:
      return 0;
    case GAMETYPE::SOLO_JACK:
    case GAMETYPE::SOLO_QUEEN:
    case GAMETYPE::SOLO_KING:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::SOLO_QUEEN_JACK:
    case GAMETYPE::SOLO_KING_JACK:
    case GAMETYPE::SOLO_KING_QUEEN:
      return (2
	      * this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::SOLO_KOEHLER:
      return (3
	      * this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    } // switch (this->type())
  } else { // if !(tcolor == Card::TRUMP)
    switch (this->type()) {
    case GAMETYPE::NORMAL:
    case GAMETYPE::MARRIAGE:
    case GAMETYPE::MARRIAGE_SOLO:
    case GAMETYPE::MARRIAGE_SILENT:
    case GAMETYPE::GENSCHER:
    case GAMETYPE::POVERTY:
    case GAMETYPE::FOX_HIGHEST_TRUMP:
    case GAMETYPE::SOLO_CLUB:
    case GAMETYPE::SOLO_SPADE:
    case GAMETYPE::SOLO_HEART:
    case GAMETYPE::SOLO_DIAMOND:
      if (tcolor == this->trumpcolor())
	return 0;
      else if (   (tcolor == Card::HEART)
	       && this->rule()(Rule::DOLLEN))
	return ( (this->rule()(Rule::NUMBER_OF_CARD_VALUES) - 3)
		* this->rule()(Rule::NUMBER_OF_SAME_CARDS) );
      else
	return ( (this->rule()(Rule::NUMBER_OF_CARD_VALUES) - 2)
		* this->rule()(Rule::NUMBER_OF_SAME_CARDS) );
    case GAMETYPE::THROWN_NINES:
    case GAMETYPE::THROWN_KINGS:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * (this->rule()(Rule::NUMBER_OF_CARD_VALUES) - 1)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::THROWN_NINES_AND_KINGS:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * (this->rule()(Rule::NUMBER_OF_CARD_VALUES) - 2)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::SOLO_MEATLESS:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * this->rule()(Rule::NUMBER_OF_CARD_VALUES)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::SOLO_JACK:
    case GAMETYPE::SOLO_QUEEN:
    case GAMETYPE::SOLO_KING:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * (this->rule()(Rule::NUMBER_OF_CARD_VALUES) - 1)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::SOLO_QUEEN_JACK:
    case GAMETYPE::SOLO_KING_JACK:
    case GAMETYPE::SOLO_KING_QUEEN:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * (this->rule()(Rule::NUMBER_OF_CARD_VALUES) - 2)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    case GAMETYPE::SOLO_KOEHLER:
      return (this->rule()(Rule::NUMBER_OF_CARD_COLORS)
	      * (this->rule()(Rule::NUMBER_OF_CARD_VALUES) - 3)
	      * this->rule()(Rule::NUMBER_OF_SAME_CARDS));
    } // switch (this->type())
  } // if !(tcolor == Card::TRUMP)
  DEBUG_ASSERTION(false,
		  "Game::numberof(" << tcolor << ")\n"
		  "  after return");
  return 0;
} // unsigned Game::numberof(Card::TColor tcolor) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the cards, that are shifted by the poverty-player
 **
 ** @version	0.5
 **
 ** @author	Diether Knof
 **
 **/
unsigned
Game::poverty_cardno() const
{
  DEBUG_ASSERTION((this->type() == GAMETYPE::POVERTY),
		  "Game::poverty_cardno():\n"
		  "  invalid gametype");

  return this->poverty_cardno_;
} // unsigned Game::poverty_cardno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of cards shifted by the poverty-player
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
HandCards const*
Game::poverty_cards() const
{
  DEBUG_ASSERTION((this->type() == GAMETYPE::POVERTY),
		  "Game::poverty_cards():\n"
		  "  invalid gametype");

  return this->poverty_cards_;
} // HandCards const* Game::poverty_cards() const

/**
 **
 ** -> result
 **
 ** @param	game1	game 1 to compare
 ** @param	game2	game 2 to compare
 **
 ** @return	whether the games are equal (they lie in the same memory)
 **
 ** @version	0.4.4
 **
 ** @author	Diether Knof
 **
 **/
bool
operator==(Game const& game1, Game const& game2)
{
  return (&game1 == &game2);
} // bool operator==(Game const& game1, Game const& game2)

/**
 **
 ** -> result
 **
 ** @param	game1	game 1 to compare
 ** @param	game2	game 2 to compare
 **
 ** @return	whether the games are different
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
bool
operator!=(Game const& game1, Game const& game2)
{
  return !(game1 == game2);
} // bool operator!=(Game const& game1, Game const& game2)
