/**********************************************************************
 *
 *   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 "team_information.heuristics.h"

#include "ai.h"
#include "team_information.h"
#include "cards_information.h"

#include "../../card/trick.h"

// For configuration values search for '*Value*'

namespace TeamInformationHeuristic {

/* Further ideas
 *
 * - if the last player has a known team and he pfunds, than the winnerplayer
 *   could be in the same team
 *   (this assumes, that the last player knows more then I do)
 *
 */

  // the value according to the team
  static int team_value(int const value, Team const& team);

// how to determine the team information
//#define TEAMINFO(player) ai.teaminfo(player)
//#define TEAMINFO(player) ai.game().teaminfo(player)

  // big macro :-(
  // returns the team information of the player given by 'ai'
  // Since the team information is used in order to interpret the play of
  // the other players, the team of the ai itself cannot be assumed to be
  // known in general. So I take into account the team points the ai has
  // counted for itself!
#define TEAMINFO(player) ( (   ((player).no() == ai.no()) \
			    && !::is_real(ai.game().teaminfo(ai)) ) \
			  ? ( (ai.team_information().team_value(ai) \
			       >= (ai.value(Aiconfig::TRUSTING) \
				   ? 10 : 20) ) \
			     ? TEAM::RE \
			     : ( (ai.team_information().team_value(ai) \
				  <= -(ai.value(Aiconfig::TRUSTING) \
				       ? 10 : 20) ) \
				? TEAM::CONTRA \
				: TEAM::UNKNOWN ) \
			    ) \
			  : ai.teaminfo(player) )
  // *Value*


  /**
   ** the value according to the team
   ** RE:     +value 
   ** CONTRA: -value 
   ** else:   0
   **
   ** @param	value	value
   ** @param	team	team
   **
   ** @return	the value according to the team
   **
   ** @author	Diether Knof
   **
   ** @version	0.7.1
   **
   ** @todo	other function name
   **/
  int
    team_value(int const value, Team const& team)
    {
      switch (team) {
      case TEAM::RE:
	return value;
      case TEAM::CONTRA:
	return -value;
      default:
	return 0;
      }
    } // static int team_value(int value, Team team);

  /**
   ** the startplayer in a trick
   ** if the startplayer plays a color which has already been played
   ** the team of the last player gets a value
   **
   ** @param	player	the player to look at
   ** @param	card	the card played by the player
   ** @param	trick	the current trick
   ** @param	ai	the ai that analyses
   **
   ** @return	value according to this heuristic
   **
   ** @author	Diether Knof
   **
   ** @version	0.7.1
   **
   ** @todo	other function name
   **/
  int
    startplayer_color(Player const& player,
		      HandCard const& card,
		      Trick const& trick,
		      Ai const& ai)
    {
      /* Idea:
       * If a player starts with a color another player is known not to have,
       * it is likely the two players play in the same team.
       */

      // startplayer
      if (trick.startplayerno() != player.no())
	return 0;

      // color trick
      if (card.istrump())
	return 0;

      if (ai.color_runs(card.color()) == 0) {

	// first color run and no ace: add some points for the second player
	if (card.value() != Card::ACE)
	  return team_value(2, TEAMINFO(player.game().player_following(player))); // *Value*

	return 0;
      } else { // if !(first run)

	// * not first color run: add some points for the last player who has
	//   jabbed the color so far
	for (Player const* p = &player.game().player_previous(player);
	     p != &player;
	     p = &player.game().player_previous(*p))
	  if (ai.cards_information().of_player(*p).does_not_have(card.color()))
	    return team_value(3, TEAMINFO(*p)); // *Value*
      } // if !(first run)

      return 0;
    } // static int startplayer_color(...)

  /**
   ** the penultimate (second last) player has pfund
   ** either the player has pfund for the player behind
   ** or the winnerplayer so far
   **
   ** @param	player	the player to look at
   ** @param	card	the card played by the player
   ** @param	trick	the current trick
   ** @param	ai	the ai that analyses
   **
   ** @return	value according to this heuristic
   **
   ** @author	Diether Knof
   **
   ** @version	0.7.1
   **
   ** @todo	improve (use hand information of the last player)
   **/
  int
    penultimate_pfund(Player const& player,
		      HandCard const& card,
		      Trick const& trick,
		      Ai const& ai)
    {
      /* Idea:
       * The second last player pfunds.
       * If the winnercard so far is low, the last player is in the same team.
       * If the winnercard so far is high, the winnerplayer is in the same team.
       */

      // the player must have played the penultimate card
      if (trick.actcardno() != player.game().playerno() - 1)
	return 0;

      // the player must not be the winner
      if (trick.winnerplayerno() == player.no())
	return 0;

      // the player must have pfund at least ten points
      if (card.points() < 10)
	return 0;

      // it must be a trump trick (but it need not be a trump pfund)
      if (trick.startcard().tcolor() != Card::TRUMP)
	return 0;

      // if the winnercard is small assume that the last player jabs
      // else assume that the last player cannot jab
      // ToDo: check whether there are cards above the winner card
      if (trick.winnercard().less(Card(Card::HEART, Card::QUEEN))) // *Value*
	return team_value(5, TEAMINFO(player.game().player_following(player))); // *Value*
      else
	return team_value(5, TEAMINFO(trick.winnerplayer())); // *Value*

      return 0;
    } // static int penultimate_pfund(...)

  /**
   ** the last player in a trick
   ** if the player jabs, the value is -winnerplayer
   ** if the player pfunds, the value is +winnerplayer
   **
   ** @param	player	the player to look at
   ** @param	card	the card played by the player
   ** @param	trick	the current trick
   ** @param	ai	the ai that analyses
   **
   ** @return	value according to this heuristic
   **
   ** @author	Diether Knof
   **
   ** @version	0.7.1
   **/
  int
    last_player(Player const& player,
		HandCard const& card,
		Trick const& trick,
		Ai const& ai)
    {
      /* Idea:
       * If the last player in a trick jabs the trick, he does play against
       * the winnerplayer so far,
       * else if he even makes a pfund, he plays with the winnerplayer.
       * Giving a card for a special point gives a much higher credit.
       */

      // the player must have played the last card of the trick
      if (!trick.isfull())
	return 0;

      // it must not be a color trick
      // or the player must not have played the same color
      if (   (trick.startcard().tcolor() != Card::TRUMP)
	  && (card.tcolor() == trick.startcard().tcolor()) )
	return 0;

      if (trick.winnerplayerno() == player.no()) {
	// the player has jabbed, so check who was the winnerplayer before
	HandCard const* winnercard = &trick.card(0);
	for (unsigned p = 1; p < trick.actcardno() - 1; ++p)
	  if (winnercard->less(trick.card(p)))
	    winnercard = &trick.card(p);

	// ToDo: check whether the player has jabbed in order to play
	//       the ace of a color

	// the value depends on the heigh of the card
	if (winnercard->value() == Card::JACK)
	  return team_value(-15, TEAMINFO(winnercard->player())); // *Value*
	if (winnercard->value() == Card::QUEEN)
	  return team_value(-25, TEAMINFO(winnercard->player())); // *Value*
	if (   (*winnercard == Card::DOLLE)
	    || winnercard->isswine() )
	  return team_value(-35, TEAMINFO(winnercard->player())); // *Value*

      } else { // if !(trick.winnerplayerno() == player.no())
	// the player has not jabbed
	// check whether the player could jab the trick
	// or if the player has even made a pfund
	// or, better, added a card for a special point

	HandCard const& card = trick.card_of_player(player);

	// check whether the player has added a card for a special point
	// especially a fox is taken into account
	if (!trick.specialpoints().empty()) {
	  int value = 0;
	  Specialpointsvector const specialpoints = trick.specialpoints();
	  for (Specialpointsvector::const_iterator
	       sp = specialpoints.begin();
	       sp != specialpoints.end();
	       ++sp) {
	    if (sp->player_of_no == player.no())
	      value += 50 * sp->value(); // *Value*
	    else if (sp->type == SPECIALPOINT::DOPPELKOPF)
	      value += 50; // *Value*
	  }
	  return team_value(value, TEAMINFO(trick.winnerplayer()));
	} // if (!trick.specialpoints().empty())

	// check whether the player has made a pfund
	if (card.points() >= 10) {
	  return team_value(10 // *Value*
			    + static_cast<int>(trick.points()) / 4, // *Value*
			    TEAMINFO(trick.winnerplayer()));
	} // if (trick.card(player).value() >= 10)

	// check whether the player could jab the trick
	Card const limit_card = Card(Card::HEART, Card::QUEEN); // *Value*
	if (trick.winnercard().less(limit_card))
	  return team_value(static_cast<int>(trick.points()) / 3, // *Value*
			    TEAMINFO(trick.winnerplayer()));
	else
	  return team_value(-static_cast<int>(trick.points()) / 5, // *Value*
			    TEAMINFO(trick.winnerplayer()));
      } // if !(trick.winnerplayerno() == player.no())

      // ToDo
      // * player jabs with card > club queen ==> contra

      return 0;
    } // static int last_player(...)

  /**
   ** the player does not have the color
   ** when an ace is the winnercard: jab -> opposite, no jab -> same
   ** perhaps only for the first color run
   **
   ** @param	player	the player to look at
   ** @param	card	the card played by the player
   ** @param	trick	the current trick
   ** @param	ai	the ai that analyses
   **
   ** @return	value according to this heuristic
   **
   ** @author	Diether Knof
   **
   ** @version	0.7.1
   **
   ** @todo	improve (check color run, search ToDo in code)
   **/
  int
    does_not_have_color(Player const& player,
			HandCard const& card,
			Trick const& trick,
			Ai const& ai)
    {
      /* Idea:
       * A player does not have a color and the winnercard of the color is
       * an ace.
       * If he jabs, he does not play with the winnerplayer
       * (different behaviour for Fox, trump ten because the player may
       *  want to bring home the points)
       * If the playe does not jab, he plays with the winnerplayer.
       */

      // not the first player
      if (!(trick.actcardno() != 1))
	return 0;

      // a color trick
      if (!(trick.startcard().tcolor() != Card::TRUMP))
	return 0;

      // the player does not have the color
      if (!(card.tcolor() != trick.startcard().tcolor()))
	return 0;

      // search the winnercard but without the last player
      HandCard const* winnercard = &trick.card(0);
      for (unsigned c = 1; c < trick.actcardno() - 1; ++c)
	if (winnercard->less(trick.card(c)))
	  winnercard = &trick.card(c);
      Team const winnerteam = TEAMINFO(winnercard->player());

      // the winner card must be an ace
      if (!(winnercard->value() == Card::ACE))
	return 0;

      if (card.isfox())
	return team_value(-0, winnerteam); // *Value*
      else if (   card.istrump()
	       && (card.value() == Card::TEN)
	       && !card.isdolle())
	return team_value(-1, winnerteam); // *Value*
      else if (card.istrump())
	// ToDo
	// The player could have jabbed in order to play a color ace
	// so check whether all colors have already been run
	// and check in the next trick whether the player does play a color ace
	return team_value(-5, winnerteam); // *Value*
      else
	return team_value(trick.points() / 2, winnerteam); // *Value*

      return 0;
    } // static int does_not_have_color(...)

  /**
   ** the player pfunds in a color trick (in the same color)
   **
   ** @param	player	the player to look at
   ** @param	card	the card played by the player
   ** @param	trick	the current trick
   ** @param	ai	the ai that analyses
   **
   ** @return	value according to this heuristic
   **
   ** @author	Diether Knof
   **
   ** @version	0.7.1
   **/
  int
    pfund_in_color_trick(Player const& player,
			 HandCard const& card,
			 Trick const& trick,
			 Ai const& ai)
    {
      /* Idea:
       * In the first run of a color trick:
       * If the player pfunds (p.e. ten), he plays with the winnerplayer
       * (t.i. the one who has played the ace),
       * else (p.e. nine) he plays against the player.
       *
       * Note: In order to return something != 0 the team of the winnerplayer
       * must be known. So the normal case will be that a player has announced
       * 're' and played a color ace.
       */

      // not the first player
      if (trick.actcardno() == 1)
	return 0;

      // a color trick
      if (trick.startcard().tcolor() == Card::TRUMP)
	return 0;

      // first run of the color trick
      if (ai.color_runs(trick.startcard().tcolor()) != 0)
	return 0;

      // the player does serve the color
      if (card.tcolor() != trick.startcard().tcolor())
	return 0;

      // search the winnercard but without the last player
      HandCard const* winnercard = &trick.card(0);
      for (unsigned c = 1; c < trick.actcardno() - 1; ++c)
	if (winnercard->less(trick.card(c)))
	  winnercard = &trick.card(c);
      Team const winnerteam = TEAMINFO(winnercard->player());

      // the winner card must be an ace or a trump
      if (   (winnercard->value() != Card::ACE)
	  && !winnercard->istrump() )
	return 0;

      // ToDo: search whether a player behind does not have the color

      // there are negative values because a small card shows that the player
      // does not wish to pfund
      // ToDo: test with nines
      switch (card.value()) {
      case Card::ACE:
	return team_value(11, winnerteam); // *Value*
      case Card::TEN:
	return team_value( 7, winnerteam); // *Value*
      case Card::KING:
	return team_value( 0, winnerteam); // *Value*
      case Card::NINE:
	return team_value(-5, winnerteam); // *Value*

      case Card::NOCARDVALUE:
      case Card::JACK:
      case Card::QUEEN: // not possible since they are trump
	DEBUG_ASSERTION(false,
			"TeamInformationHeuristic::pfund_in_color_trick(...)\n"
			"  card should be a color card ("
			<< trick.startcard().tcolor() << ")"
			" but is '" << card << "'"
		       );
	return 0;
      } // switch (card.value())

      return 0;
    } // static int pfund_in_color_trick(...)

#undef TEAMINFO

} // namespace TeamInformationHeuristic
