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

/**
 **
 ** -> result
 **
 ** @param       -
 **
 ** @return      number of trumps in the game
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 */
unsigned
Game::trumps_no() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::trumps_no()");

  // number of trumps
  unsigned trumps = 0;

  switch (this->type()) {
  case GAMETYPE::NORMAL:
  case GAMETYPE::FOX_HIGHEST_TRUMP:
  case GAMETYPE::POVERTY:
  case GAMETYPE::GENSCHER:
  case GAMETYPE::MARRIAGE:
  case GAMETYPE::MARRIAGE_SOLO:
  case GAMETYPE::MARRIAGE_SILENT:
  case GAMETYPE::SOLO_DIAMOND:
  case GAMETYPE::SOLO_HEART:
  case GAMETYPE::SOLO_SPADE:
  case GAMETYPE::SOLO_CLUB:
    trumps = 2 * this->rule()(Rule::NUMBER_OF_CARD_COLORS); // jack and queen
    trumps += (this->rule()(Rule::NUMBER_OF_CARD_VALUES)
	       - 2); // rest trump color
    if (this->rule()(Rule::DOLLEN)
	&& (this->type() != GAMETYPE::SOLO_HEART))
      trumps += 1;
    break;
  case GAMETYPE::SOLO_JACK:
  case GAMETYPE::SOLO_QUEEN:
  case GAMETYPE::SOLO_KING:
    trumps = this->rule()(Rule::NUMBER_OF_CARD_COLORS);
    break;
  case GAMETYPE::SOLO_QUEEN_JACK:
  case GAMETYPE::SOLO_KING_JACK:
  case GAMETYPE::SOLO_KING_QUEEN:
    trumps = 2 * this->rule()(Rule::NUMBER_OF_CARD_COLORS);
    break;
  case GAMETYPE::SOLO_KOEHLER:
    trumps = 3 * this->rule()(Rule::NUMBER_OF_CARD_COLORS);
    break;
  case GAMETYPE::SOLO_MEATLESS:
  case GAMETYPE::FIVE_NINES:
  case GAMETYPE::SIX_KINGS:
    trumps = 0;
    break;
  } // this->type()
  trumps *= this->rule()(Rule::NUMBER_OF_SAME_CARDS);

  DEBUG_RETURNING(trumps,
		  INFO_GAME && INFO_VALUE,
		  "Game::trumps_no()");
} // unsigned Game::trumps_no() const

/**
 **
 ** -> result
 **
 ** @param       -
 **
 ** @return      number of played trumps
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 ** @todo        perhaps use a variable,
 **              so that this function does not take much time
 **
 */
unsigned
Game::trumps_played_no() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::trumps_played_no()");

  // number of trumps
  unsigned trumps = 0;
  for (vector<Trick>::const_iterator trick = this->trick_p.begin();
       trick != this->trick_p.end();
       trick++)
    for (unsigned c = 0; c < trick->actcardno(); c++)
      if (trick->card(c).istrump())
	trumps += 1;

  DEBUG_RETURNING(trumps,
		  INFO_GAME && INFO_VALUE,
		  "Game::trumps_played_no()");
} // unsigned Game::trumps_played_no() const

/**
 **
 ** -> result
 **
 ** @param       -
 **
 ** @return      trumpcolor
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 */
Card::Color
Game::trumpcolor() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::trumpcolor()");

  Card::Color color = Card::NOCARDCOLOR;
  switch (this->type()) {
  case GAMETYPE::NORMAL:
  case GAMETYPE::POVERTY:
  case GAMETYPE::FOX_HIGHEST_TRUMP:
  case GAMETYPE::GENSCHER:
  case GAMETYPE::MARRIAGE:
  case GAMETYPE::MARRIAGE_SOLO:
  case GAMETYPE::MARRIAGE_SILENT:
  case GAMETYPE::SOLO_DIAMOND:
    color = Card::DIAMOND;
    break;
  case GAMETYPE::SOLO_HEART:
    color = Card::HEART;
    break;
  case GAMETYPE::SOLO_SPADE:
    color = Card::SPADE;
    break;
  case GAMETYPE::SOLO_CLUB:
    color = Card::CLUB;
    break;
  case GAMETYPE::SOLO_JACK:
  case GAMETYPE::SOLO_QUEEN:
  case GAMETYPE::SOLO_KING:
  case GAMETYPE::SOLO_QUEEN_JACK:
  case GAMETYPE::SOLO_KING_JACK:
  case GAMETYPE::SOLO_KING_QUEEN:
  case GAMETYPE::SOLO_KOEHLER:
  case GAMETYPE::SOLO_MEATLESS:
  case GAMETYPE::FIVE_NINES:
  case GAMETYPE::SIX_KINGS:
    color = Card::NOCARDCOLOR;
    break;
  } // switch(this->type())

  DEBUG_RETURNING(color,
		  INFO_GAME && INFO_VALUE,
		  "Game::trumpcolor()");
} // Card::Color Game::trumpcolor() const

/**
 **
 ** -> result
 **
 ** @param       team    Team
 **
 ** @return      whether the team has get a trick
 **
 ** @version     0.4.4
 **
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 */
bool
Game::hastrick(const TEAM::Team& team) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::hastrick(team)");

  DEBUG_RETURNING((this->numberoftricks_of_team(team) > 0),
		  INFO_GAME && INFO_VALUE,
		  "Game::hastrick(team)");
} // bool Game::hastrick(const TEAM::Team& team) const

/**
 **
 ** -> result
 **
 ** @param       player  player
 **
 ** @return      number of tricks, the player has won so far
 **              (with the current trick)
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 */
unsigned
Game::numberoftricks_of_player(Player const& player) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::numberoftricks_of_player(player)");

  unsigned number = 0;

  vector<Trick>::const_iterator trick;
  for (trick = this->trick_p.begin();
       trick != this->trick_p.end();
       trick++)
    if ((trick->winnerplayer() == player)
	&& trick->intrickpile())
      number += 1;

  DEBUG_RETURNING(number,
		  INFO_GAME && INFO_VALUE,
		  "Game::numberoftricks_of_player(player)");
} // unsigned Game::numberoftricks_of_player(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param       player  player
 ** @param       trickno last trick to view
 **
 ** @return      number of tricks, the player has won til the trick 'trickno'
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 **
 */
unsigned
Game::numberoftricks_of_player(Player const& player,
			       unsigned trickno) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::numberoftricks_of_player(player, trickno)");

  unsigned number = 0;

  if (trickno != UINT_MAX)
    trickno += 1;
  vector<Trick>::const_iterator trick;
  for (trick = this->trick_p.begin();
       ((trickno > 0)
	&& (trick != this->trick_p.end()));
       trick++, trickno--)
    if ((trick->winnerplayer() == player)
	&& trick->intrickpile())
      number += 1;

  DEBUG_RETURNING(number,
		  INFO_GAME && INFO_VALUE,
		  "Game::numberoftricks_of_player(player, trickno)");
} // unsigned Game::numberoftricks_of_player(Player const& player, unsigned trickno) const

/**
 **
 ** -> result
 **
 ** @param       team    team
 **
 ** @return      number of tricks, the team has won so far
 **              (without the current trick)
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 */
unsigned
Game::numberoftricks_of_team(Team const& team) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::numberoftricks_of_team(team)");

  unsigned number = 0;

  for (vector<Player*>::const_iterator player = this->player_begin();
       player != this->player_end();
       player++)
    if ((*player)->team() == team)
      number += this->numberoftricks_of_player(**player);

  DEBUG_RETURNING(number,
		  INFO_GAME && INFO_VALUE,
		  "Game::numberoftricks_of_team(team)");
} // unsigned Game::numberoftricks_of_team(Team const& team) const

/**
 **
 ** -> result
 **
 ** @param       player  player
 **
 ** @return      points, the player has got
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 */
unsigned
Game::points_of_player(Player const& player) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::points_of_player(player)");

  unsigned points = 0;

  vector<Trick>::const_iterator trick;
  for (trick = this->trick_p.begin();
       trick != this->trick_p.end();
       trick++)
    if ((trick->winnerplayer() == player)
	&& (trick->intrickpile()))
      points += trick->points();

  DEBUG_RETURNING(points,
		  INFO_GAME && INFO_VALUE,
		  "Game::points_of_player(player)");
} // unsigned Game::points_of_player(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param       player  player
 ** @param       trickno last trick to view
 **
 ** @return      points, the player has got til trickno
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 */
unsigned
Game::points_of_player(Player const& player, unsigned trickno) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::points_of_player(player, trickno)");

  unsigned points = 0;

  if (trickno != UINT_MAX)
    trickno += 1;
  vector<Trick>::const_iterator trick;
  for (trick = this->trick_p.begin();
       ((trickno > 0)
	&& (trick != this->trick_p.end()));
       trick++, trickno--)
    if ((trick->winnerplayer() == player)
	&& trick->intrickpile())
      points += trick->points();

  DEBUG_RETURNING(points,
		  INFO_GAME && INFO_VALUE,
		  "Game::points_of_player(player, trickno)");
} // unsigned Game::points_of_player(Player const& player, unsigned trickno) const

/**
 **
 ** -> result
 **
 ** @param       team    team
 **
 ** @return      points, the team has got
 **
 ** @version     0.4.4
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 **/
unsigned
Game::points_of_team(Team const& team) const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::points_of_team(team)");

  unsigned points = 0;

  for (vector<Player*>::const_iterator player = this->player_begin();
       player != this->player_end();
       player++)
    if ((*player)->team() == team)
      points += this->points_of_player(**player);


  DEBUG_RETURNING(points,
		  INFO_GAME && INFO_VALUE,
		  "Game::points_of_team(team)");
} // unsigned Game::points_of_team(Team const& team) const

/**
 **
 ** -> result
 **
 ** @param       -
 **
 ** @return      the winner team
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 ** @version     0.4.4
 **
 ** @todo        announcements
 **
 **/
Team
Game::winnerteam() const
{
  DEBUG_CALLING(INFO_GAME && INFO_VALUE,
		"Game::winnerteam() ");

  DEBUG_ASSERTION((this->tricks_remaining_no() == 0),
		  "Game::winnerteam():\n"
		  "  game is not finished, yet\n"
		  << "current no: " << this->trick_current_no() << '\n'
		  << "remaining no: " << this->tricks_remaining_no() << '\n');

  unsigned i;
  vector<Team> t;
  Announcement highest_announcement_re = ANNOUNCEMENT::NOANNOUNCEMENT;
  Announcement highest_announcement_contra = ANNOUNCEMENT::NOANNOUNCEMENT;

  t = vector<Team>( this->playerno() );

  for( i = 0; i < this->playerno(); i++ )
    t[ i ] = this->team( this->player( i ) );

  // calculate highest announcements
  for( i = 0; i < this->playerno(); i++ )
  {
    Player const& pl = this->player( i );
    if( this->team( this->player( i ) ) == TEAM::RE )
    {
      if( pl.announcement() > highest_announcement_re )
	highest_announcement_re = pl.announcement();
    }

    if( this->team( this->player( i ) ) == TEAM::CONTRA )
    {
      if( pl.announcement() > highest_announcement_contra )
	highest_announcement_contra = pl.announcement();
    }
  }


  Team winnerteam = TEAM::NOTEAM;

  // noone has made an announcement
  if (    ( highest_announcement_re == ANNOUNCEMENT::NOANNOUNCEMENT )
	  && ( highest_announcement_contra == ANNOUNCEMENT::NOANNOUNCEMENT ) )
  {
    if (this->points_of_team( TEAM::RE ) >120 )
    {
      winnerteam = TEAM::RE;   
    } else
    {
      winnerteam = TEAM::CONTRA;   
    }
  } // if (noone has announced)

  // re/contra has made an announcement and has kept it
  if ( (    ( highest_announcement_re == ANNOUNCEMENT::NO120)
	    && (this->points_of_team( TEAM::CONTRA ) < 120 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO90)
	    && (this->points_of_team( TEAM::CONTRA ) < 90 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO60)
	    && (this->points_of_team( TEAM::CONTRA ) < 60 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO30)
	    && (this->points_of_team( TEAM::CONTRA ) < 30 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO0)
	    && !this->hastrick( TEAM::CONTRA ) ) )
  {
    winnerteam = TEAM::RE;   
  }
  if ( (    ( highest_announcement_re == ANNOUNCEMENT::NO120_REPLY)
	    && (this->points_of_team( TEAM::RE ) >= 120 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO90_REPLY)
	    && (this->points_of_team( TEAM::RE ) >= 90 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO60_REPLY)
	    && (this->points_of_team( TEAM::RE ) >= 60 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO30_REPLY)
	    && (this->points_of_team( TEAM::RE ) >= 30 ))
       || ( ( highest_announcement_re == ANNOUNCEMENT::NO0_REPLY)
	    && this->hastrick( TEAM::RE ) ) )
  {
    winnerteam = TEAM::RE;   
  }
  if ( (    ( highest_announcement_contra == ANNOUNCEMENT::NO120)
	    && (this->points_of_team( TEAM::RE ) < 120 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO90)
	    && (this->points_of_team( TEAM::RE ) < 90 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO60)
	    && (this->points_of_team( TEAM::RE ) < 60 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO30)
	    && (this->points_of_team( TEAM::RE ) < 30 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO0)
	    && !this->hastrick( TEAM::RE ) ) )
  {
    winnerteam = TEAM::CONTRA;   
  }
  if ( (    ( highest_announcement_contra == ANNOUNCEMENT::NO120_REPLY)
	    && (this->points_of_team( TEAM::CONTRA ) >= 120 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO90_REPLY)
	    && (this->points_of_team( TEAM::CONTRA ) >= 90 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO60_REPLY)
	    && (this->points_of_team( TEAM::CONTRA ) >= 60 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO30_REPLY)
	    && (this->points_of_team( TEAM::CONTRA ) >= 30 ))
       || ( ( highest_announcement_contra == ANNOUNCEMENT::NO0_REPLY)
	    && this->hastrick( TEAM::CONTRA ) ) )
  {
    winnerteam = TEAM::CONTRA;   
  }

  // one player has made no announcement
  // but the other hasn't made enough points for his announcements
  if ( ( highest_announcement_re == ANNOUNCEMENT::NOANNOUNCEMENT )
       && (winnerteam == TEAM::NOTEAM) )
  {
    winnerteam = TEAM::RE;
  } // if ( highest_announcement_re == ANNOUNCEMENT::NOANNOUNCEMENT )

  if ( ( highest_announcement_contra == ANNOUNCEMENT::NOANNOUNCEMENT )
       && (winnerteam == TEAM::NOTEAM) )
  {
    winnerteam = TEAM::CONTRA;
  } // if ( highest_announcement_contra == ANNOUNCEMENT::NOANNOUNCEMENT )


  DEBUG_RETURNING(winnerteam,
		  INFO_GAME && INFO_VALUE,
		  "Game::winnerteam() ");
} // Team Game::winnerteam() const
