/**********************************************************************
 *
 *   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/rule.h"
#include "../player/player.h"
#include "../ui/ui.h"

/**
 **
 ** -> result
 **
 ** @param	player the player
 **
 ** @return	the team of player 'player'
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Team
Game::teaminfo(Player const& player) const
{                                                                          
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::teaminfo(player)"); 

  DEBUG_ASSERTION((player.no() < this->teaminfo_p.size()),
		  "Game::teaminfo(player):\n"
		  "  player.no() is too great");

  DEBUG_RETURNING(this->teaminfo_p[player.no()],
		  INFO_GAME && INFO_VALUE,
		  "Game::teaminfo(player)");      
} // Team Game::teaminfo(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param	player the player
 **
 ** @return	the team of player 'player' as known by the humans
 **
 ** @version	0.6.1
 **
 ** @author	Diether Knof
 **
 **/
Team
Game::teaminfo_for_humans(Player const& player) const
{                                                                          
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::teaminfo_for_humans(player)"); 

  Team team = this->teaminfo(player);
  // check whether a human player knows more about the team
  for (vector<Player*>::const_iterator p = this->player_begin();
       p != this->player_end();
       p++)
    if( ((*p)->type() == Player::HUMAN)
	&& (((*p)->teaminfo(player) == TEAM::RE)
	    || ((*p)->teaminfo(player) == TEAM::CONTRA))) {
      // a human player knows the team - take that
      team = (*p)->teaminfo(player);
      break;
    }

  if ((team != TEAM::RE)
      && (team != TEAM::CONTRA))
  { // testing for 're' and 'contra'
    // if there are two 're' players, the other must be 'contra'
    // if there are three 'contra' players, the last must be 're'
    static unsigned recursion_depth = 0;

    if (recursion_depth < this->playerno()) {
      recursion_depth += 1;

      unsigned re_no = 0;
      unsigned contra_no = 0;
      // count the players in the teams
      for (vector<Player*>::const_iterator p = this->player_begin();
	   p != this->player_end();
	   p++)
	if (this->teaminfo_for_humans(**p) == TEAM::RE)
	  re_no += 1;
	else if (this->teaminfo_for_humans(**p) == TEAM::CONTRA)
	  contra_no += 1;

      if (re_no == this->rule()(Rule::NUMBER_OF_PLAYERS_PER_TEAM))
	team = TEAM::CONTRA;
      if (contra_no == this->playerno() - 1)
	team = TEAM::RE;

      recursion_depth -= 1;
    } // if (recursion_depth < this->playerno())
  } // testing for 're' and 'contra'

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

/**
 **
 ** set the teaminfo of the player 'player' to 'team'
 **
 ** @param	player	player
 ** @param	team	team to set
 **
 ** @return	-
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 */
void
Game::teaminfo_set(Player const& player, Team const& team)
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::teaminfo_set(player, team)");

  DEBUG_ASSERTION(((team == TEAM::RE)
		   || (team == TEAM::CONTRA)),
		  "Game::teaminfo_set(player, team):\n"
		  "  'team' not valid: " << team);

  if (this->teaminfo_p[player.no()] != team) {
    this->teaminfo_p[player.no()] = team;

    if (!this->isvirtual())
      ::ui->teaminfo_update(player);

    this->teaminfo_update();
  } // if (this->teaminfo_p[player.no()] != team)

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_VALUE,
		  "Game::teaminfo_set(player, team)");
} // void teaminfo_set(Player const& player, Team const& team);

/**
 **
 ** sets the teaminfo at the start of the game
 **
 ** @param -
 **
 ** @return -
 **
 ** @version 0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @todo	marriage: team of the players (maybe re, maybe contra, ?)
 **
 */
void
Game::teaminfo_set_at_gamestart()
{                                                                  
  DEBUG_CALLING(INFO_GAME && INFO_GAMEPLAY,
		"Game::teaminfo_set_at_gamestart() ");

  if (this->type() == GAMETYPE::NORMAL) {
    this->teaminfo_p = vector<Team>(this->playerno(), TEAM::UNKNOWN);
  } else if (type() == GAMETYPE::POVERTY) {
    // is done in 'this->poverty_shift()'

  } else if (this->type() == GAMETYPE::MARRIAGE) {
    // set all teams to unknown but of the soloplayer
    this->teaminfo_p = vector<Team>(this->playerno(), TEAM::UNKNOWN);
    this->teaminfo_p[this->soloplayer().no()] = TEAM::RE;

    for (vector<Player*>::iterator player = this->player_p.begin();
	 player != this->player_p.end();
	 player++)
      (*player)->teaminfo_p[(*player)->no()] = TEAM::MAYBE_CONTRA;
  } else if (GAMETYPE::is_solo(this->type())) {
    // set all teams to contra but of the soloplayer
    this->teaminfo_p = vector<Team>(this->playerno(), TEAM::CONTRA);
    this->teaminfo_p[this->soloplayer().no()] = TEAM::RE;
  } // if (this->type())

  // give all players the teaminfo
  for (vector<Player*>::iterator player = this->player_p.begin();
       player != this->player_p.end();
       player++)
    (*player)->teaminfo_update();

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_GAMEPLAY,
		  "Game::teaminfo_set_at_gamestart() ");
} // void Game::teaminfo_set_at_gamestart()

/**
 **
 ** updates the global teaminfo
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
void
Game::teaminfo_update()
{
  DEBUG_CALLING(INFO_GAME && INFO_GAMEPLAY,
		"Game::teaminfo_update()");

  if (this->type() != GAMETYPE::NORMAL)
    DEBUG_RETURNING(VOID,
		    INFO_GAME && INFO_GAMEPLAY,
		    "Game::teaminfo_update()");

  // save the teaminfo
  vector<Team> const teaminfo_bak = this->teaminfo_p;

  Team swines_owner_teaminfo_before = TEAM::UNKNOWN;
  Team first_fox_catcher_teaminfo_before = TEAM::UNKNOWN;
  if (this->rule()(Rule::SWINE_ONLY_SECOND)
      && this->swines_owner()
      && this->first_fox_catcher()) {
    swines_owner_teaminfo_before
      = this->teaminfo(*(this->swines_owner()));
    first_fox_catcher_teaminfo_before
      = this->teaminfo(*(this->swines_owner()));
  } // if (rule swine only second)

  using namespace TEAM;

  if (!this->trick_current().isstartcard()) {

    Player const& player
      = this->trick_current().player_of_card(this->trick_current().actcardno() - 1);

    if ((this->teaminfo(player) != RE)
	&& (this->teaminfo(player) != CONTRA)) {
      // the player has played a club queen
      if (this->trick_current().card_of_player(player) == Card::CLUB_QUEEN)
	this->teaminfo_p[player.no()] = RE;

      // the startcard is trump, but the player has not played a trump
      if (this->trick_current().startcard().istrump()
	  && (!this->trick_current().card_of_player(player).istrump()))
	this->teaminfo_p[player.no()] = CONTRA;

    } // if (team of 'player' unknown
  } // if (!this->trick_current().isstartcard())

  { // count the played club queens for a silent marriage
    for (vector<Player*>::const_iterator p = this->player_p.begin();
	 p!= this->player_p.end();
	 p++) {
      Player const& player = **p;
      if (this->teaminfo_p[player.no()] == TEAM::RE) {
	// We only want to know, whether the player has played his second
	// club queen, so he must already be known as re.
	unsigned no = 0;
	for (vector<Trick>::const_iterator t = this->trick_p.begin();
	     t != this->trick_p.end();
	     ++t) {
	  if (t->cardno_of_player(player) < t->actcardno())
	    if (t->card_of_player(player) == Card::CLUB_QUEEN)
	      no += 1;
	} // for (t \in this->trick_p)

	if (no == this->rule()(Rule::NUMBER_OF_SAME_CARDS)) {
	  // all other players must be contra
	  for (vector<Team>::iterator t = this->teaminfo_p.begin();
	       t != this->teaminfo_p.end();
	       t++)
	    if (*t != TEAM::RE)
	      *t = TEAM::CONTRA;
	} // if (both club queens played)
      } // if (this->teaminfo_p[player.no()] == TEAM::RE)
    } // for (player)
  } // count the played club queens for a silent marriage

  { // count the 're's and 'contra's
    unsigned re_no = 0;
    unsigned contra_no = 0;
    for (vector<Team>::const_iterator t = this->teaminfo_p.begin();
	 t != this->teaminfo_p.end();
	 t++) {
      if (*t == TEAM::RE)
	re_no += 1;
      else if (*t == TEAM::CONTRA)
	contra_no += 1;
    } // for (t)

    if (re_no == this->rule()(Rule::NUMBER_OF_PLAYERS_PER_TEAM)) {
      for (vector<Team>::iterator t = this->teaminfo_p.begin();
	   t != this->teaminfo_p.end();
	   t++)
	if (*t != TEAM::RE) {
	  *t = TEAM::CONTRA;
	} // if (*t != TEAM::RE)
    } // if (re_no == 2)
    if (contra_no == this->playerno() - 1) {
      for (vector<Team>::iterator t = this->teaminfo_p.begin();
	   t != this->teaminfo_p.end();
	   t++)
	if (*t != TEAM::CONTRA) {
	  *t = TEAM::RE;
	} // if (*t != TEAM::CONTRA)
    } // if (contra_no == this->playerno() - 1)

    if (contra_no == this->playerno() - 2) {
      if (!this->trick_current().isstartcard()) {
	Player const& player
	  = this->trick_current().player_of_card(this->trick_current().actcardno() - 1);
	if ((this->teaminfo(player) == TEAM::RE)
	    && (this->trick_current().startcard().istrump()
		&& !this->trick_current().card_of_player(player).istrump())) {
	  // the player is re but can play no more trump

	  // all players but two are contra, one is re and does not have
	  // any more club queen
	  // so the remaining player must be re
	  for (vector<Team>::iterator t = this->teaminfo_p.begin();
	       t != this->teaminfo_p.end();
	       t++)
	    if (*t != TEAM::CONTRA) {
	      *t = TEAM::RE;
	    } // if (*t != TEAM::CONTRA)
	} // // if (the player is re but can play no more trump)
      } // if (!this->trick_current().isstartcard())
    } // if (contra_no == this->playerno() - 2)
  } // count the 're's and 'contra's

  // set the teams known by the players
  for (vector<Player*>::iterator player = this->player_p.begin();
       player != this->player_p.end();
       player++)
    (*player)->teaminfo_update();

#ifndef POSTPHONED
  // *** to be removed
  if (!this->isvirtual()) {
    for (vector<Player*>::iterator player = this->player_p.begin();
	 player != this->player_p.end();
	 player++) {
      if (this->teaminfo_p[(*player)->no()] != teaminfo_bak[(*player)->no()])
	::ui->teaminfo_update(**player);
    }
  }
#endif
  // test for catcher of the first fox of swines
  if (this->rule()(Rule::SWINE_ONLY_SECOND)
      && this->swines_owner()
      && this->first_fox_catcher()
      && ((this->teaminfo(*(this->swines_owner())) == TEAM::RE)
	  || (this->teaminfo(*(this->swines_owner())) == TEAM::CONTRA))
      && ((this->teaminfo(*(this->first_fox_catcher())) == TEAM::RE)
	  || (this->teaminfo(*(this->first_fox_catcher())) == TEAM::CONTRA))
      && ( (this->teaminfo(*(this->swines_owner()))
	    != swines_owner_teaminfo_before)
	   || (this->teaminfo(*(this->first_fox_catcher()))
	       != first_fox_catcher_teaminfo_before))
     ) {
    this->swines_owner_p->hand_sort();
    if (!this->isvirtual())
      ::ui->redraw_all();
  } // if (swine only second: team changed of swine owner or first fox catcher)

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_GAMEPLAY,
		  "Game::teaminfo_update()");
} // void Game::teaminfo_update()

/**
 **
 ** -> result
 **
 ** @param	player - the player
 **
 ** @return	the team of player 'player'
 **
 ** @version	0.4.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Team
Game::team(Player const& player) const
{                                                                          
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::team(player)"); 

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