/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2005  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 "../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);

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

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

  // This can (only) be the case in a marriage.
  if ( (player.team() != TEAM::RE)
       && (player.team() != TEAM::CONTRA) )
    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;

  // as long as the marriage is not determined, no announcement is valid
  if (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET)
    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)

  bool valid = (team_announcement < announcement);
  unsigned const marriage_deferral = ((this->type() == GAMETYPE::MARRIAGE)
				      ? (this->marriage_determination_trickno()
					 - 1)
				      : 0);

  if (announcement == ANNOUNCEMENT::NO120) {
    if (announcement_of_team(opposite(player.team())).announcement
	>= ANNOUNCEMENT::NO120)
      valid &= ((this->tricks_remaining_no()
		 + (this->trick_current().isfull() ? 1 : 0)
		 + marriage_deferral
		 + 1)
		>= this->rule().remaining_tricks(ANNOUNCEMENT::NO120));
    else
      valid &= ((this->tricks_remaining_no()
		 + (this->trick_current().isfull() ? 1 : 0)
		 + marriage_deferral)
		>= this->rule().remaining_tricks(ANNOUNCEMENT::NO120));

    return valid;
  } // if (announement == ANNOUNCEMENT::NO120)

  // no announcement after the 'Rule::ANNOUNCEMENT_LAST' trick
  valid &= (this->tricks_remaining_no()
	    >= (this->rule()(Rule::ANNOUNCEMENT_LAST)
		+ (this->trick_current().isfull() ? 1 : 0)));

#ifdef OUTDATED
  // Diether Knof: Version 0.6.4

  // if the marriage determination is too late, only a 'no 120' can be said.
  if (team_announcement == ANNOUNCEMENT::NOANNOUNCEMENT) {
    valid &= ( ( this->tricks_remaining_no()
		 + (this->trick_current().isfull() ? 1 : 0)
		 + marriage_deferral)
	       >= this->rule().remaining_tricks(ANNOUNCEMENT::NO120));
  }

  // in the marriage test whether the 'no 120' was 
  if (team_announcement == ANNOUNCEMENT::NO120)
    valid &= ( ( (this->trickno()
		  - this->announcement_of_team(player.team()).trickno)
		 + marriage_deferral)
	       >= this->rule().remaining_tricks(ANNOUNCEMENT::NO120));
#endif

  // test whether all announcements before are valid
  for (unsigned a = ((team_announcement
		      == ANNOUNCEMENT::NOANNOUNCEMENT)
		     ? ANNOUNCEMENT::NO120
		     : team_announcement + 1);
       a <= unsigned(announcement);
       a++)
    valid &= ((this->tricks_remaining_no()
	       + (this->trick_current().isfull() ? 1 : 0)
	       + marriage_deferral)
	      >= this->rule().remaining_tricks(Announcement(a)));

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