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

#include "../party/party.h"
#include "../party/rule.h"
#include "../card/trick.h"
#include "../ui/ui.h"

/**
 **
 ** -> result
 **
 ** @param	team	the team
 **
 ** @return	the announcement of the team 'team'
 **
 ** @version	0.6.1
 **
 ** @author	Diether Knof
 **
 **/
Game::AnnouncementWithTrickno
Game::announcement_of_team(Team const& team) const
{                                                                          
  DEBUG_ASSERTION(((team == TEAM::RE)
		   || (team == TEAM::CONTRA)
		   || ((this->type() == GAMETYPE::MARRIAGE)
		       && (this->marriage_selector()
			   != MARRIAGE_SELECTOR::TEAM_SET))),
		  "Game::announcement_of_team(team)\n"
		  "  invalid team = " << team);

  // In an undetermined marriage the contra team cannot have announced.
  // This case is treated here, because in this case there does not
  // exists a player of the team 'contra' so the assertion later on would fail.
  if ( (this->type() == GAMETYPE::MARRIAGE)
       && (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET)
       && (team == TEAM::CONTRA) )
    return AnnouncementWithTrickno();

  // the player of the team, that has made the highest announcement
  Player const* player = NULL;

  // take the highest announcement
  for(vector<Player*>::const_iterator p = this->players().begin();
      p != this->players().end();
      p++) {
    if ((*p)->team() == team)
      if (!player
	  || (this->announcement_of_player(**p).announcement
	      > this->announcement_of_player(*player).announcement))
	player = *p;
  } // for (p)

  if (player == NULL) {
    DEBUG_ASSERTION(this->isvirtual(),
		    "Game::announcement_of_team(team):\n"
		    "  no player of team '" << team << "' found!");
    return AnnouncementWithTrickno();
  }

  return this->announcement_of_player(*player);
} // Game::AnnouncementWithTrickno Game::announcement_of_team(Team const& team) const

/**
 **
 ** -> result
 **
 ** @param	player	the player
 **
 ** @return	the highest announcement of player 'player'
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.1
 **
 **/
Game::AnnouncementWithTrickno const&
Game::announcement_of_player(Player const& player) const
{                                                                          
  DEBUG_ASSERTION((player.no() < this->playerno()),
		  "Game::announcement_of_player(player)\n"
		  << "  illegal playerno of player = " << player.no());

  return this->announcement_[player.no()].back();
} // Game::AnnouncementWithTrickno const& Game::announcement_of_player(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param	player	the player
 **
 ** @return	all announcements of player 'player'
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.1
 **
 **/
vector<Game::AnnouncementWithTrickno> const&
Game::announcements_of_player(Player const& player) const
{
  DEBUG_ASSERTION((player.no() < this->playerno()),
		  "Game::announcement_of_player(player)\n"
		  << "  illegal playerno of player = " << player.no());

  return this->announcement_[player.no()];
} // vector<Game::AnnouncementWithTrickno> const& Game::announcements_of_player(Player const& player) const

/**********************************************************************
 **
 ** void Game::announcements_request()
 **
 ** Parameters:	-
 **
 ** Result:	-
 **
 ** Version:	0.4.4
 **
 ** Description:	requests announcements from the players
 **
 **********************************************************************/
void
Game::announcements_request()
{
  bool again = true; // whether to ask the players again

  while (again) {
    again = false;

    Announcement announcement;

    // ask for announcements
    for(vector<Player*>::const_iterator p = this->players().begin();
	p != this->players().end();
	p++) {
      Player& player = **p;
      announcement = player.announcement_request();
      if ((announcement > this->announcement_of_player(player).announcement)
	  && (this->announcement_valid(announcement, player))) {
	again = true;
	this->announcement_make(announcement, player);
      }
    } // for (p < this->playerno())
  } // while (again)

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

/**
 **
 ** 'player' announces 'announcement'
 **
 ** @param	announcement	announcement made
 ** @param	player		player who announces
 **
 ** @result	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
void
Game::announcement_make(Announcement announcement,
			Player const& player)
{
  // test whether the announcement is valid
  if (announcement_valid(announcement, player)) {
    if (ANNOUNCEMENT::is_reply(announcement)) {
      if (announcement == ANNOUNCEMENT::REPLY)
	announcement
	  = ANNOUNCEMENT::to_reply(announcement_of_team(opposite(player.team())
						       ).announcement);

      this->announcement_replied_on_.announcement
	= ANNOUNCEMENT::from_reply(announcement);
      this->announcement_replied_on_.trickno = this->tricks().size();
    }

    // set the announcement
    this->announcement_[player.no()].push_back(AnnouncementWithTrickno(announcement, this->tricks().size()));
    // update the team info
    this->set_teaminfo(player, player.team());

    // tell all players, that the announcement is made
    for(vector<Player*>::const_iterator p = this->players().begin();
	p != this->players().end();
	p++)
      (*p)->announcement_made(announcement, player);

    // tell the ui, that the announcement is made
    if (!this->isvirtual()) {
      ::ui->announcement_made(announcement, player);
      ::ui->gameplay_action(GameplayAction::Announcement(player.no(),
							 announcement));
    }
  } // if (announcement_valid(announcement, player)

  return ;
} // void Game::announcement_make(Announcement announcement, Player const& player)

/**
 **
 ** -> result
 **
 ** @param	announcement	the announcement
 ** @param	player		the player, who asks to make the announcement
 **
 ** @return	whether the announcement is valid
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.2
 **
 **/
bool
Game::announcement_valid(Announcement const& announcement,
			 Player const& player) const
{
  // test, whether status is in a game
  if ( !::in_running_game())
    return false;
  if (this->tricks_.empty())
    return false;

  // no announcement after the 'Rule::ANNOUNCEMENT_LAST' trick
  if (this->tricks_remaining_no()
      < (this->rule()(Rule::ANNOUNCEMENT_LAST)
	 + (this->trick_current().isfull() ? 1 : 0)))
    return false;

  // as long as the marriage is not determined, no announcement is valid
  if (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET)
    return false;


  Announcement const& team_announcement
    = this->announcement_of_team(player.team()).announcement;

  if (ANNOUNCEMENT::is_real(team_announcement)
      && is_reply(team_announcement))
    return false;

  // the announcement must be a better one
  if (team_announcement >= announcement)
    return false;


  if (is_reply(announcement)) {
    if (!this->rule()(Rule::ANNOUNCEMENT_REPLY))
      return false;

    if (team_announcement != ANNOUNCEMENT::NOANNOUNCEMENT)
      return false;

    AnnouncementWithTrickno const& opposite_announcement
      = announcement_of_team(opposite(player.team()));

    if (announcement != ANNOUNCEMENT::REPLY)
      if (announcement != ANNOUNCEMENT::to_reply(opposite_announcement.announcement))
	return false;

    // same trick - I can make my announcement
    // Remark: 'this->trick_current_no()' can be different from
    //         'this->trick_.size()'
    if (this->tricks().size() == opposite_announcement.trickno)
      return true;

    return false;
  } // if (announcement == ANNOUNCEMENT::REPLY)

  unsigned const marriage_deferral = (  (   (this->type() == GAMETYPE::MARRIAGE)
					 || (this->type() == GAMETYPE::MARRIAGE_SOLO))
				      ? (this->marriage_determination_trickno()
					 - 1)
				      : 0);
  bool valid = (((this->rule()(Rule::ANNOUNCEMENT_TILL_FULL_TRICK)
		  ? this->real_tricks_remaining_no()
		  : player.hand().cardsnumber())
		 + marriage_deferral)
		>= this->rule().remaining_cards(announcement));

  // verify that all announcements before are valid
  if (   (announcement > ANNOUNCEMENT::NO120)
      && (this->announcement_of_team(player.team()).announcement
	  != ANNOUNCEMENT::previous(announcement) ) )
    valid &= this->announcement_valid(ANNOUNCEMENT::previous(announcement),
				      player);

  return valid;
} // bool Game::announcement_valid(Announcement const& announcement, Player const& player)

/**
 **
 ** -> result
 **
 ** @param	announcement	the announcement
 ** @param	player		the player, who asks to make the announcement
 **
 ** @return	whether this is the last possibility to announce 'announcement'
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **
 **/
bool
Game::last_chance_to_announce(Announcement const& announcement,
			      Player const& player) const
{
  if (!this->announcement_valid(announcement, player))
    return false;

  unsigned const marriage_deferral = (  (this->type() == GAMETYPE::MARRIAGE)
				      ? (this->marriage_determination_trickno()
					 - 1)
				      : 0);

  return (((this->rule()(Rule::ANNOUNCEMENT_TILL_FULL_TRICK)
	    ? this->real_tricks_remaining_no()
	    : player.hand().cardsnumber())
	   + marriage_deferral)
	  == this->rule().remaining_cards(announcement));
} // bool Game::last_chance_to_announce(Announcement announcement, Player player) const
