/**********************************************************************
 *
 *   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 "../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_CALLING(INFO_GAME && INFO_VALUE,
		"Game::announcement_of_team(team)"); 

  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->player_p.begin();
      p != this->player_p.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!");
  DEBUG_RETURNING(AnnouncementWithTrickno(),
		  INFO_GAME && INFO_VALUE,
		  "Game::announcement_of_team(team)");      
  }

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

  DEBUG_ASSERTION((player.no() < this->playerno()),
		  "Game::announcement_of_player(player)\n"
		  << "  illegal playerno of player = " << player.no());

  DEBUG_RETURNING(this->announcement_p[player.no()].back(),
		  INFO_GAME && INFO_VALUE,
		  "Game::announcement_of_player(player)");      
} // 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_CALLING(INFO_GAME && INFO_VALUE,
		"Game::announcements_of_player(player)"); 

  DEBUG_ASSERTION((player.no() < this->playerno()),
		  "Game::announcement_of_player(player)\n"
		  << "  illegal playerno of player = " << player.no());

  DEBUG_RETURNING(this->announcement_p[player.no()],
		  INFO_GAME && INFO_VALUE,
		  "Game::announcements_of_player(player)");      
} // vector<Game::AnnouncementWithTrickno> const& Game::announcements_of_player(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the announcement on which was replied
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.1
 **
 **/
Game::AnnouncementWithTrickno const&
Game::announcement_replied_on() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::announcement_replied_on()"); 

  DEBUG_RETURNING(this->announcement_replied_on_p,
		  INFO_GAME && INFO_VALUE,
		  "Game::announcement_replied_on()");      
} // Game::AnnouncementWithTrickno const& Game::announcement_replied_on() const

/**********************************************************************
 **
 ** void Game::announcements_request()
 **
 ** Parameters:	-
 **
 ** Result:	-
 **
 ** Version:	0.4.4
 **
 ** Description:	requests announcements from the players
 **
 **********************************************************************/
void
Game::announcements_request()
{
  DEBUG_CALLING(INFO_GAME && INFO_OTHER_FUNCTION,
		"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->player_p.begin();
	p != this->player_p.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)

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_OTHER_FUNCTION,
		  "Game::announcements_request()");
} // void Game::announcements_request()

/**********************************************************************
 **
 ** Announcement const& Game::announcement_make(Announcement const& announcement,
 *				       Player const& player)
 **
 ** Parameters:	announcement	- the announcement
 *		player		- the player, who makes the announcement
 **
 ** Result:	the made announcement
 *		(NOANNOUNCEMENT, if the announcement was not valid)
 **
 ** Version:	0.4.4
 **
 ** Description:	sets the announcement of the player
 **
 **********************************************************************/
void
Game::announcement_make(Announcement announcement,
			Player const& player)
{
  DEBUG_CALLING(INFO_GAME && INFO_OTHER_FUNCTION,
		"Game::announcement_make(announcement, 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_p.announcement
	= ANNOUNCEMENT::from_reply(announcement);
      this->announcement_replied_on_p.trickno = this->trick_p.size();
    }

    // set the announcement
    this->announcement_p[player.no()].push_back(AnnouncementWithTrickno(announcement, this->trick_p.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->player_p.begin();
	p != this->player_p.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)

  DEBUG_RETURNING(VOID,
		  INFO_GAME && INFO_OTHER_FUNCTION,
		  "Game::announcement_make(announcement, player)");
} // 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
{
  DEBUG_CALLING(INFO_GAME && INFO_OTHER_FUNCTION,
		"Game::announcement_valid(announcement, player)");

  // test, whether status is in a game
  if ( (::game_status != GAMESTATUS::GAME_PLAY)
       && (::game_status != GAMESTATUS::GAME_SHOW_FULL_TRICK)
       && (::game_status != GAMESTATUS::GAME_SHOW_LAST_TRICK))
    DEBUG_RETURNING(false,
		    INFO_GAME && INFO_OTHER_FUNCTION,
		    "Game::announcement_valid(announcement, player)"
		    " = false");

  // This can (only) be the case in a marriage.
  if ( (player.team() != TEAM::RE)
       && (player.team() != TEAM::CONTRA) )
    DEBUG_RETURNING(false,
		    INFO_GAME && INFO_OTHER_FUNCTION,
		    "Game::announcement_valid(announcement, player)"
		    " = false");

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

  if (ANNOUNCEMENT::is_real(team_announcement)
      && is_reply(team_announcement))
    DEBUG_RETURNING(false,
		    INFO_GAME && INFO_OTHER_FUNCTION,
		    "Game::announcement_valid(announcement, player)");

  // as long as the marriage is not determined, no announcement is valid
  if (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET)
    DEBUG_RETURNING(false,
		    INFO_GAME && INFO_OTHER_FUNCTION,
		    "Game::announcement_valid(announcement, player)");

  if (is_reply(announcement)) {
    if (!this->rule()(Rule::ANNOUNCEMENT_REPLY))
      DEBUG_RETURNING(false,
		      INFO_GAME && INFO_OTHER_FUNCTION,
		      "Game::announcement_valid(announcement, player)");

    if (team_announcement != ANNOUNCEMENT::NOANNOUNCEMENT)
      DEBUG_RETURNING(false,
		      INFO_GAME && INFO_OTHER_FUNCTION,
		      "Game::announcement_valid(announcement, player)");

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

    if (announcement != ANNOUNCEMENT::REPLY)
      if (announcement != ANNOUNCEMENT::to_reply(opposite_announcement.announcement))
	DEBUG_RETURNING(false,
			INFO_GAME && INFO_OTHER_FUNCTION,
			"Game::announcement_valid(announcement, player)");

    // same trick - I can make my announcement
    // Remark: 'this->trick_current_no()' can be different from
    //         'this->trick_p.size()'
    if (this->trick_p.size() == opposite_announcement.trickno)
      DEBUG_RETURNING(true,
		      INFO_GAME && INFO_OTHER_FUNCTION,
		      "Game::announcement_valid(announcement, player)");

    DEBUG_RETURNING(false,
		    INFO_GAME && INFO_OTHER_FUNCTION,
		    "Game::announcement_valid(announcement, player)");
  } // 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));

    DEBUG_RETURNING(valid,
		    INFO_GAME && INFO_OTHER_FUNCTION,
		    "Game::announcement_valid(announcement, player)");
  } // 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) {
    COUT << this->tricks_remaining_no()
      << " + " << (this->trick_current().isfull() ? 1 : 0)
      << " + " << marriage_deferral
      << " >= " << this->rule().remaining_tricks(ANNOUNCEMENT::NO120)
      << endl;
    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)));

  DEBUG_RETURNING(valid,
		  INFO_GAME && INFO_OTHER_FUNCTION,
		  "Game::announcement_valid(announcement, player)");
} // bool Game::announcement_valid(Announcement const& announcement, Player const& player)
