/**********************************************************************
 *
 *   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"

/**
 **
 ** 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_p),
  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()");

  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()");
} // Game::Game(Party& party, Player const& startplayer)

/**
 **
 ** constructor
 **
 ** @param	game	game to copy
 **
 ** @return	-
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
Game::Game(Game const& game) :
party_p(game.party_p),
  player_p(game.player_p),
  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 game of the tricks
  for (vector<Trick>::iterator trick = this->trick_p.begin();
       trick != this->trick_p.end();
       trick++) {
    trick->game_p = this;
  }

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

  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()
{
  DEBUG_CALLING(INFO_GAME && INFO_INIT,
		"Game::~Game()");

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


/**
 *
 ** 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"
    << "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"
    << "players" << ":\n";
  for (vector<Player*>::const_iterator player = this->player_p.begin();
       player != this->player_p.end();
       player++)
    ostr << **player << "\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)
{
  DEBUG_CALLING(INFO_GAME && INFO_OTHER_FUNCTION,
		"operator<<(ostr, game)");

  game.out(ostr);

  DEBUG_RETURNING(ostr,
		  INFO_GAME && INFO_OTHER_FUNCTION,
		  "operator<<(ostr, game)");
} // 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
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::isvirtual()");

  DEBUG_RETURNING((this != &::party.game()),
		  INFO_GAME && INFO_VALUE,
		  "Game::isvirtual()");
} // 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_CALLING(INFO_GAME && INFO_VALUE,
		"Game::party()");

  DEBUG_ASSERTION(this->party_p,
		  "Game::party():\n"
		  "  party_p == NULL");

  DEBUG_RETURNING(*(this->party_p),
		  INFO_GAME && INFO_VALUE,
		  "Game::party()");
} // 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_CALLING(INFO_GAME && INFO_VALUE,
		"Game::party()");

  DEBUG_ASSERTION(this->party_p,
		  "Game::party():\n"
		  "  party_p == NULL");

  DEBUG_RETURNING(*(this->party_p),
		  INFO_GAME && INFO_VALUE,
		  "Game::party()");
} // 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
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::rule()");

  DEBUG_RETURNING(this->party().rule(),
		  INFO_GAME && INFO_VALUE,
		  "Game::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
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::seed()");

  DEBUG_RETURNING(this->seed_p,
		  INFO_GAME && INFO_VALUE,
		  "Game::seed()");
} // 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
 **
 **/
unsigned
Game::seed_set(unsigned const& seed)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::seed_set(seed)");

  this->seed_p = seed;

  DEBUG_RETURNING(this->seed(),
		  INFO_GAME && INFO_VALUE,
		  "Game::seed_set(seed)");
} // unsigned 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 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)

