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

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the player who has swines
 **		NULL, if no player has swines	
 **
 ** @version	0.4.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const*
Game::swines_owner() const
{
  if (!this->swines_announced())
    return NULL;

  return this->swines_owner_;
} // Player const* Game::swines_owner() const

/**
 **
 ** -> result
 ** for the rules look into the code
 **
 ** @param	player	the player who wants to announce swines
 **
 ** @return	whether 'player' can announce swines
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **
 **/
bool
Game::swines_announcement_valid(Player const& player) const
{
  // rule must allow swines
  if (!this->rule()(Rule::SWINES))
    return false;

  // the swines must not be announced
  if (this->swines_announced())
    return false;

  // check cards number
  if (this->rule()(Rule::SWINE_ONLY_SECOND)
      ? (player.hand().numberoftrumpaces() != 1)
      : (player.hand().numberoftrumpaces()
	 != this->rule()(Rule::NUMBER_OF_SAME_CARDS)))
    return false;

  // 'swines announcement begin': must be in the reservation
  // (in a poverty before the shifting)
  if (this->rule()(Rule::SWINES_ANNOUNCEMENT_BEGIN)
      && (::game_status == GAMESTATUS::GAME_RESERVATION)
      && !this->poverty_shifted())
    return true;

  // normal case, announcement in the game: can announce
  if (!this->rule()(Rule::SWINES_ANNOUNCEMENT_BEGIN)
      && !this->rule()(Rule::SWINE_ONLY_SECOND))
    return true;

  // 'swine only second': first fox must be caught by the same team
  if (this->rule()(Rule::SWINE_ONLY_SECOND)
      && (player.hand().numberofalltrumpaces()
	  == this->rule()(Rule::NUMBER_OF_SAME_CARDS))
      && (this->first_fox_catcher())
      && (   (this->teaminfo(*this->first_fox_catcher())
	      == this->teaminfo(player))
	  && (   ::is_real(this->teaminfo(player))
	      || (this->first_fox_catcher()->no() == player.no()) )
	 )
     )
    return true;

  return false;
} // bool Game::swines_announcement_valid(Player const& player) const

/**
 **
 ** announce swines if they can be announced and they have not yet been
 ** announced
 **
 ** @param	player	player who wants to announce swines
 **
 ** @return	whether the swines are announced
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
bool
Game::swines_announce(Player& player)
{
  DEBUG_ASSERTION(!this->swines_announced(),
		  "Game::swines_announce():\n"
		  "  swines have already been announced.\n"
		  "  virtual = " << (this->isvirtual() ? "true" : "false"));

  if (this->swines_announced())
    return false;

  if (!this->swines_announcement_valid(player))
    return false;


  this->swines_owner_ = &player;
  this->swines_announced_ = true;
  this->swines_owner_->hand_sort();

  if (this->rule()(Rule::SWINE_ONLY_SECOND))
    // the swines owner has to say his team
    this->teaminfo_update();

  if (!this->isvirtual())
    ::ui->swines_announced(*(this->swines_owner()));

  for (vector<Player*>::iterator p = this->players().begin();
       p != this->players().end();
       p++)
    (*p)->swines_announced(player);

  return true;
} // bool Game::swines_announce(Player& player)

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the player who has hyperswines
 **		NULL, if no player has hyperswines	
 **
 ** @version	0.4.5
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Player const*
Game::hyperswines_owner() const
{
  if (!this->hyperswines_announced())
    return NULL;

  return this->hyperswines_owner_;
} // Player const* Game::hyperswines_owner() const

/**
 **
 ** -> result
 ** for the rules look into the code
 **
 ** @param	player	the player who wants to announce hyperswines
 **
 ** @return	whether 'player' can announce hyperswines
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **
 **/
bool
Game::hyperswines_announcement_valid(Player const& player) const
{
  if (!this->rule()(Rule::HYPERSWINES))
    return false;

  if (this->hyperswines_announced())
    return false;

  if (!this->swines_announced()
      && !this->swines_announcement_valid(player))
    return false;

#ifdef WORKAROUND
  // in the following case we have a problem
  // * virtual games
  // * cards informations
  //   - the player can have diamond ace and diamond nine 
  //   - the player cannot have all four cards (2 diamond ace, 2 diamond nines)
  if (this->isvirtual()
      && !this->swines_announced())
    return false;
#endif

  if (   !this->rule()(Rule::SWINES_AND_HYPERSWINES_JOINT)
      && (this->swines_owner() == &player))
    return false;

  if ((this->rule()(Rule::WITH_NINES)
       ? player.hand().numberoftrumpnines()
       : player.hand().numberoftrumpkings())
      != this->rule()(Rule::NUMBER_OF_SAME_CARDS))
    return false;

  if (   this->rule()(Rule::HYPERSWINES_ANNOUNCEMENT_BEGIN)
      && !(   (::game_status == GAMESTATUS::GAME_RESERVATION)
	   && ( (this->type() != GAMETYPE::POVERTY)
	       || !this->soloplayer_ ) ) )
    return false;

  return true;
} // bool Game::hyperswines_announcement_valid(Player const& player) const

/**
 **
 ** announce hyperswines if they can be announced
 ** If the player has not annouced 'swines', they are announced automatically
 **
 ** @param	player	player who wants to announce hyperswines
 **
 ** @return	whether the hyperswines are announced
 **
 ** @version	0.6.6
 **
 ** @author	Diether Knof
 **
 **/
bool
Game::hyperswines_announce(Player& player)
{
  DEBUG_ASSERTION(!this->hyperswines_announced(),
		  "Game::hyperswines_announce():\n"
		  "  hyperswines have already been announced.\n"
		  "  virtual = " << (this->isvirtual() ? "true" : "false"));

  if (this->hyperswines_announced())
    return false;

  if (!this->hyperswines_announcement_valid(player))
    return false;

  if (!this->swines_announced())
    this->swines_announce(player);

  this->hyperswines_owner_ = &player;
  this->hyperswines_announced_ = true;
  this->hyperswines_owner_->hand_sort();

  if (!this->isvirtual())
    ::ui->hyperswines_announced(*(this->hyperswines_owner()));

  for (vector<Player*>::iterator p = this->players().begin();
       p != this->players().end();
       p++)
    (*p)->hyperswines_announced(player);

  return true;
} // bool Game::hyperswines_announce(Player& player)
// bool Game::hyperswines_announced() const

/**
 **
 ** test the reservations for swines
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @version	0.6.6
 **
 ** @author	Diether Knof
 **
 **/
void
Game::test_swines_from_reservations()
{
  if (this->rule()(Rule::SWINES)
      || SEED_OUT) {
    for (unsigned i = 0;
	 i < this->playerno();
	 i++,
	 this->player_current_
	 = &(this->player_following(this->player_current()))) {
      if ( (this->reservation(this->player_current()).swines
	    || SEED_OUT)
	  && this->swines_announcement_valid(this->player_current()) ) {
	this->swines_announce(this->player_current());

	if (SEED_OUT)
	  cout << "  Player " << this->player_current().no() << ": swines\n";
      } // if (reservation)
    } // for (p < playerno())
  } // if (rule()(Rule::SWINES))

  // testing hyperswines
  if (this->rule()(Rule::HYPERSWINES)
      || SEED_OUT) {
    if (this->swines_announced()) {
      for (unsigned i = 0;
	   i < this->playerno();
	   i++,
	   this->player_current_
	   = &(this->player_following(this->player_current()))) {
	if ( (this->reservation(this->player_current()).hyperswines
	      || SEED_OUT)
	    && this->hyperswines_announcement_valid(this->player_current()) ) {
	  this->hyperswines_announce(this->player_current());

	  if (SEED_OUT)
	    cout << "  Player " << this->player_current().no()
	      << ": hyperswines\n";
	} // if (reservation)
      } // for (p < playerno())
    } // if (this->swines_announced())
  } // if (rule()(Rule::HYPERSWINES))

  return ;
} // void Game::test_swines_from_reservations()
