/**********************************************************************
 *
 *   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 "../player/player.h"
#include "../card/trick.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_p(&party),
  player_p(party.player_),
  startplayer_p(&startplayer),
  player_current_p(&startplayer),
  seed_p(party.seed()),
  type_p(GAMETYPE::NORMAL),
  soloplayer_p(NULL),
  marriage_selector_p(MARRIAGE_SELECTOR::TEAM_SET),
  marriage_determination_trickno_p(0),
  poverty_cards_p(),
  poverty_cardno_p(UINT_MAX),
  reservation_p(),
  trick_p(),
  teaminfo_p(party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME), TEAM::UNKNOWN),
  announcement_p(party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME)),
  announcement_replied_on_p(),
  swines_owner_p(NULL),
  first_fox_catcher_p(NULL),
  swines_announced_p(false),
  hyperswines_owner_p(NULL),
  hyperswines_announced_p(false),
  finished_p(false)
{
  DEBUG_CALLING(INFO_GAME && INFO_INIT,
		"Game::Game(party, startplayer)");

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

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_INIT,
		  "Game::Game(party, startplayer)");
} // 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_p(game.party_p),
  player_p(players),
  startplayer_p(game.startplayer_p),
  player_current_p(game.player_current_p),
  seed_p(game.seed_p),
  type_p(game.type_p),
  soloplayer_p(game.soloplayer_p),
  marriage_selector_p(game.marriage_selector_p),
  marriage_determination_trickno_p(game.marriage_determination_trickno_p),
  poverty_cards_p(game.poverty_cards_p),
  trick_p(game.trick_p),
  teaminfo_p(game.teaminfo_p),
  announcement_p(game.announcement_p),
  announcement_replied_on_p(game.announcement_replied_on_p),
  swines_owner_p(game.swines_owner_p),
  first_fox_catcher_p(game.first_fox_catcher_p),
  swines_announced_p(game.swines_announced_p),
  hyperswines_owner_p(game.hyperswines_owner_p),
  hyperswines_announced_p(game.hyperswines_announced_p),
  finished_p(game.finished_p)
{
  DEBUG_CALLING(INFO_GAME && INFO_INIT,
		"Game::Game(game)");

  // set the players
  if (game.startplayer_p)
    this->startplayer_p = players[game.startplayer_p->no()];
  if (game.player_current_p)
    this->player_current_p = players[game.player_current_p->no()];
  if (game.soloplayer_p)
    this->soloplayer_p = players[game.soloplayer_p->no()];
  if (game.swines_owner_p)
    this->swines_owner_p = players[game.swines_owner_p->no()];
  if (game.first_fox_catcher_p)
    this->first_fox_catcher_p = players[game.first_fox_catcher_p->no()];
  if (game.hyperswines_owner_p)
    this->hyperswines_owner_p = players[game.hyperswines_owner_p->no()];

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

  // 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);
  }

  DEBUG_ASSERT(this->self_check());

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_INIT,
		  "Game::Game(game)");
} // Game::Game(Game const& game)

/**
 **
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Game::~Game()
{
  for (vector<Trick*>::iterator t = this->trick_p.begin();
       t != this->trick_p.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_p,
		  "Game::self_check()\n"
		  "  no pointer to a party\n"
		  "  this = " << this);

  for (vector<Player*>::const_iterator p = this->player_p.begin();
       p != this->player_p.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->trick_p.begin();
       t != this->trick_p.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->trick_p)

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

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

  if (this->soloplayer_p)
    DEBUG_ASSERTION((this == &this->soloplayer_p->game()),
		    "Game::self_check()\n"
		    "  the soloplayer is not of the same game");
  if (this->swines_owner_p)
    DEBUG_ASSERTION((this == &this->swines_owner_p->game()),
		    "Game::self_check()\n"
		    "  the swines owner is not of the same game");
  if (this->first_fox_catcher_p)
    DEBUG_ASSERTION((this == &this->first_fox_catcher_p->game()),
		    "Game::self_check()\n"
		    "  the first fox catcher is not of the same game");
  if (this->hyperswines_owner_p)
    DEBUG_ASSERTION((this == &this->hyperswines_owner_p->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
 *
 ** @version	0.4.4
 *
 ** @author	Borg Enders
 ** @author	Diether Knof
 *
 */
ostream&
Game::out(ostream& ostr) const
{
  DEBUG_CALLING(INFO_GAME && INFO_IO,
		"Game::out(ostr)");

  unsigned i = 0;

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

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

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

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

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

  ostr << "\n";

  DEBUG_RETURNING(ostr,
		  INFO_GAME && INFO_IO,
		  "Game::out(ostr)");
} // ostream& Game::out(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.out(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 != &::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
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::is_finished()");

  DEBUG_RETURNING(this->finished_p,
		  INFO_GAME && INFO_VALUE,
		  "Game::is_finished()");
} // bool Game::is_finished() const

/**
 *
 ** -> result
 *
 ** @param	-
 *
 ** @return	the party the game belongs to
 *
 ** @version	0.4.4
 *
 ** @author	Borg Enders
 ** @author	Diether Knof
 *
 */
Party const&
Game::party() const
{
  DEBUG_ASSERTION(this->party_p,
		  "Game::party():\n"
		  "  party_p == NULL\n"
		  "  this = " << this);

  return *this->party_p;
} // Party const& Game::party() const

/**
 *
 ** -> result
 *
 ** @param	-
 *
 ** @return	the party the game belongs to
 *
 ** @version	0.4.4
 *
 ** @author	Borg Enders
 ** @author	Diether Knof
 *
 */
Party&
Game::party()
{
  DEBUG_ASSERTION(this->party_p,
		  "Game::party():\n"
		  "  party_p == NULL");

  return *this->party_p;
} // Party& Game::party()

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

/**
 *
 ** -> result
 *
 ** @param	-
 *
 ** @return	seed of the game
 *
 ** @version	0.4.4
 *
 ** @author	Borg Enders
 ** @author	Diether Knof
 *
 */
unsigned
Game::seed() const
{
  return this->seed_p;
} // unsigned Game::seed() const

/**
 **
 ** sets the seed of the game
 **
 ** @param	seed	new seed
 **
 ** @return	seed of the game
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
void
Game::seed_set(unsigned const& seed)
{
  this->seed_p = seed;

  return ;
} // void Game::seed_set(unsigned const& seed)

/**
 *
 ** -> 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
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::type()");

  if (!this->is_finished()
      && (this->type_p == GAMETYPE::MARRIAGE_SILENT) )
    DEBUG_RETURNING(GAMETYPE::NORMAL,
		    INFO_GAME && INFO_VALUE,
		    "Game::type()");

  DEBUG_RETURNING(this->type_p,
		  INFO_GAME && INFO_VALUE,
		  "Game::type()");
} // GameType Game::type() const

/**
 *
 ** set the game type
 *
 ** @param	type	new GameType
 *
 ** @return	(new) game-type
 *
 ** @version	0.4.4
 *
 ** @author	Borg Enders
 ** @author	Diether Knof
 *
 */
void
Game::type_set(GameType const& type)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::type_set(GameType)");

  this->type_p = type;

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_VALUE,
		  "Game::type_set(GameType)");
} // void Game::type_set(GameType const& type)

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the cards, that are shifted by the poverty-player
 **
 ** @version	0.5
 **
 ** @author	Diether Knof
 **
 **/
unsigned
Game::poverty_cardno() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::poverty_cardno()");

  DEBUG_ASSERTION((this->type() == GAMETYPE::POVERTY),
		  "Game::poverty_cardno():\n"
		  "  invalid gametype");

  DEBUG_RETURNING(this->poverty_cardno_p,
		  INFO_GAME && INFO_VALUE,
		  "Game::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
 **
 **/
Hand const*
Game::poverty_cards() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::poverty_cards()");

  DEBUG_ASSERTION((this->type() == GAMETYPE::POVERTY),
		  "Game::poverty_cards():\n"
		  "  invalid gametype");

  DEBUG_RETURNING(this->poverty_cards_p,
		  INFO_GAME && INFO_VALUE,
		  "Game::poverty_cards()");
} // Hand 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)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"operator==(Game, Game)");

  DEBUG_RETURNING(&game1 == &game2,
		  INFO_GAME && INFO_VALUE,
		  "operator==(Game, Game)");
} // 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)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"operator!=(Game, Game)");

  DEBUG_RETURNING(!(game1 == game2),
		  INFO_GAME && INFO_VALUE,
		  "operator!=(Game, Game)");
} // bool operator!=(Game const& game1, Game const& game2)

