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

#include "trick.h"
#include "../player/player.h"
#include "../game/game.h"
#include "../party/rule.h"
#include "../party/party.h"
#include "../misc/setting.h"

/**
 ** constructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard::HandCard() :
  Card(),
  hand_(NULL)
{ }

/**
 ** constructor
 **
 ** @param	hand	hand this card belongs to
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard::HandCard(Hand const& hand) :
  Card(),
  hand_(&hand)
{ }

/**
 ** constructor
 **
 ** @param	card	card (without a hand)
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard::HandCard(Card const& card) :
  Card(card),
  hand_(NULL)
{ }

/**
 ** constructor
 **
 ** @param	hand	hand this card belongs to
 ** @param	card	card
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard::HandCard(Hand const& hand, Card const& card) :
  Card(card),
  hand_(&hand)
{ }

/**
 ** constructor
 **
 ** @param	hand	hand this card belongs to
 ** @param	color	color of the card
 ** @param	value	value of the card
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard::HandCard(Hand const& hand,
		   Card::Color const color,
		   Card::Value const value) :
  Card(color, value),
  hand_(&hand)
{
} // HandCard::HandCard(Hand hand, Card::Color color, Card::Value value)

/**
 ** copy constructor
 **
 ** @param	card	card to copy
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard::HandCard(HandCard const& card) :
  Card(card),
  hand_(&card.hand())
{ }

/**
 ** copy operator
 **
 ** @param	card	card to copy
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard&
HandCard::operator=(HandCard const& card)
{
  static_cast<Card&>(*this) = card;
  this->hand_ = &card.hand();

  return *this;
} // HandCard::HandCard(HandCard card)

/**
 ** copy operator
 ** keeps the hand
 **
 ** @param	card	card to copy
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard&
HandCard::operator=(Card const& card)
{
  static_cast<Card&>(*this) = card;

  return *this;
} // HandCard::HandCard(Card card)

/**
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
HandCard::~HandCard()
{
} // HandCard::~HandCard()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the player this card belongs to
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
Player const&
HandCard::player() const
{
  return this->hand().player();
} // Player HandCard::player() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the corresponding game
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
Game const&
HandCard::game() const
{
  return this->player().game();
} // Game HandCard::game() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	'TRUMP' is the card is a trump,
 **		else the color
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
Card::TColor
HandCard::tcolor() const
{
  return (this->istrump()
	  ? Card::TRUMP
	  : this->color());
} // Card::TColor HandCard::tcolor() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is trump
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::istrump() const
{
  return this->Card::istrump(this->game());
} // bool HandCard::istrump() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is a dolle
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::isdolle() const
{
  return this->Card::isdolle(this->game());
} // bool HandCard::isdolle() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is a swine
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::isswine() const
{
  return ((this->istrumpace()
	   && (this->game().swines_announced())));
} // bool HandCard::isswine()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is a hyperswine
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::ishyperswine() const
{
  if (this->game().rule()(Rule::WITH_NINES))
    return ((this->istrumpnine()
	     && (this->game().hyperswines_announced())));
  else
    return ((this->istrumpking()
	     && (this->game().hyperswines_announced())));
} // bool HandCard::ishyperswine()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card can be a genscher card
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::possible_genscher() const
{
  return (this->hand().has_possible_genscher()
	   && this->istrumpking());
} // bool HandCard::possible_genscher()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if the card can be or is a swine
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::possible_swine() const
{
  return (this->isswine()
	  || (   this->hand().has_swines()
	      && this->istrumpace()));
} // bool HandCard::possible_swine()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if the card can be or is a hyperswine
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::possible_hyperswine() const
{
  return (this->ishyperswine()
	  || (this->hand().has_hyperswines()
	      && (this->game().rule()(Rule::WITH_NINES)
		  ? this->istrumpnine()
		  : this->istrumpking())
	     )
	 );
} // bool HandCard::possible_hyperswine()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is a fox
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::isfox() const
{
  return (GAMETYPE::is_normal(this->game().type())
	  && this->istrumpace()
	  && !this->isswine());
} // bool HandCard::isfox()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is a trump ace
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::istrumpace() const
{
  return ((this->value() == ACE)
	  && this->istrump());
} // bool HandCard::istrumpace()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is a trump nine
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::istrumpnine() const
{
  return ((this->value() == NINE)
	  && this->istrump());
} // bool HandCard::istrumpnine()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	true if card is a trump king
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::istrumpking() const
{
  return ((this->value() == KING)
	  && this->istrump());
} // bool HandCard::istrumpking() const

/**
 ** -> result
 **
 ** @note	if both cards are equal, the first (this) is not less than
 **		the second (card)
 **		(but the dollen -- see rules)
 **
 ** @param	card	the card to compare with
 **
 ** @return	true, if the card is less than 'b', else false
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 ** @todo	test rule 'DOLLE_CONTRARY_IN_LAST_TRICK'
 **/
bool
HandCard::less(HandCard const& card) const
{
  if (!*this)
    return true;

  HandCard const& a = *this;
  HandCard const& b = card;
  Game const& game = this->game();
  Rule const& rule = game.rule();

  // both cards are not trump
  if (!a.istrump() && !b.istrump()) {
    if (a.color() == b.color()) // same color
      return (a.value() < b.value());
    else // different color
      return false;
  }

  // a is trump but b not
  if (a.istrump() && !b.istrump())
    return false;

  // a is not trump but b is
  if (!a.istrump() && b.istrump())
    return true;


  // The difficult case: both cards are trump

  switch(game.type()) {
    using namespace GAMETYPE;
  case NORMAL:
  case POVERTY:
  case FOX_HIGHEST_TRUMP:
  case GENSCHER:
  case MARRIAGE:
  case MARRIAGE_SOLO:
  case MARRIAGE_SILENT:
  case SOLO_DIAMOND:
  case SOLO_CLUB:
  case SOLO_HEART:
  case SOLO_SPADE:

    // Hyperswines
    if (rule(Rule::HYPERSWINES)) {
      if (a.possible_hyperswine())
	return false;

      if (b.possible_hyperswine())
	return true;
    }

    // Swines
    if (rule(Rule::SWINES)) {
      if (a.possible_swine())
	return false;

      if (b.possible_swine())
	return true;
    }

    // Dollen
    if (rule(Rule::DOLLEN)) {
      if (a == Card::DOLLE) {
	if (b == Card::DOLLE) { // both cards are dollen
	  if ((rule(Rule::DOLLEN_FIRST_OVER_SECOND_WITH_SWINES)
	       && game.swines_owner()) )
	    return false;
	  if (rule(Rule::DOLLEN_CONTRARY_IN_LAST_TRICK)
	      && game.is_last_trick()) {
	    // we are in the last trick
	    // and the rule 'DOLLEN_CONTRARY_IN_LAST_TRICK' is valid
	    if (rule(Rule::DOLLEN_SECOND_OVER_FIRST)) {
	      return false;
	    } else { // if !(rule(Rule::DOLLEN_SECOND_OVER_FIRST)
	      return true;
	    } // if !(rule(Rule::DOLLEN_SECOND_OVER_FIRST)
	  } else { // if !(game.is_last_trick())
	    if (rule(Rule::DOLLEN_SECOND_OVER_FIRST)) {
	      return true;
	    } else { // if !(rule(Rule::DOLLEN_SECOND_OVER_FIRST)
	      return false;
	    } // if !(rule(Rule::DOLLEN_SECOND_OVER_FIRST)
	  } // if !(game.is_last_trick())
	} else { // if !(b == Card::DOLLE)
	  return false;
	} // if !(b == Card::DOLLE)
      } else { // if !(a == Card::DOLLE)
	if (b == Card::DOLLE)
	  return true;
      } // if !(a == Card::DOLLE)
    } // if (rule.dollen)

    if (a.value() == Card::QUEEN) { // 'a' is a QUEEN
      if (b.value() == Card::QUEEN) // both cards are QUEENs
	return (a.color() < b.color());
      else // 'b' is lesser than a QUEEN (JACK or DIAMOND)
	return false;
    }

    if (a.value() == Card::JACK) { // 'a' is a JACK
      if (b.value() == Card::QUEEN) // 'b' is a QUEEN
	return true;
      else if (b.value() == Card::JACK) // 'a' and 'b' are JACKs
	return (a.color() < b.color());
      else // 'b' is a DIAMOND/CLUB/SPADE/HEART
	return false;
    }

    // 'a' is a simple ace/ten/king/nine
    if ( (b.value() == Card::QUEEN) ||
	 (b.value() == Card::JACK) ) // 'b' is higher than an ace/ten/king/nine
      return true;

    // both cards are simple ace/ten/king/nine
    return (a.value() < b.value());
    break;
  case SOLO_JACK:
  case SOLO_QUEEN:
  case SOLO_KING:
    return (a.color() < b.color());
    break;
  case SOLO_QUEEN_JACK:
    if (a.value() != b.value()) // one card is queen, one is jack
      return (a.value() == Card::JACK);
    // both card are JACK/QUEEN
    return (a.color() < b.color());
    break;
  case SOLO_KING_JACK:
    if (a.value() != b.value()) // one card is king, one is jack
      return (a.value() == Card::JACK);
    // both card are JACK/KING
    return (a.color() < b.color());
    break;
  case SOLO_KING_QUEEN:
    if (a.value() != b.value()) // one card is king, one is queen
      return (a.value() == Card::QUEEN);
    // both card are QUEEN/KING
    return (a.color() < b.color());
    break;
  case SOLO_KOEHLER:
    if (a.value() == b.value()) // both cards are the same value
      return (a.color() < b.color());
    // both cards have different values
    if (a.value() == Card::JACK)
      return true;
    if (b.value() == Card::JACK)
      return false;
    // one card is queen, one is king
    // -- hey, that is like in 'SOLO_KING_QUEEN', take that code
    return (a.value() == Card::QUEEN);
    break;
  case SOLO_MEATLESS:
    // cannot be, because no card is trump
    DEBUG_ASSERTION(false,
		    "  SOLO_MEATLESS with trump ");

    break;
  case FIVE_NINES:
    return (a.color() < b.color());
    break;
  case SIX_KINGS:
    return (a.color() < b.color());
    break;
  } // switch(game.type())

  DEBUG_ASSERTION(false,
		  "Card::less(card, game):\n"
		  "  after 'switch(game.type())");

  return false;
} // bool HandCard::less(Card const& card) const

/**
 ** -> result
 **
 ** @note	if both cards are equal, the first (this) is not less than
 **		the second (card)
 **		(but the dollen -- see rules)
 **
 ** @param	card	the card to compare with
 **
 ** @return	true, if the card is less than 'b', else false
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::less(Card const& card) const
{
  return this->less(HandCard(this->hand(), card));
} // bool HandCard::less(Card card) const

/**
 ** -> result
 **
 ** @param	a	first card
 ** @param	b	second card
 **
 ** @return	whether the card 'a' is left (-1) or right (1) from card 'b'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
int
HandCard::relative_position(HandCard const& a, HandCard const& b)
{
  return ::setting(Setting::CARDS_ORDER).relative_position(a, b);
} // static int HandCard::relative_position(HandCard a, HandCard b)

/**
 ** -> result
 **
 ** @param	trick	current trick
 **
 ** @return	whether it is valid to play the card in the trick
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
bool
HandCard::isvalid(Trick const& trick) const
{
  Hand const& hand = this->hand();

  // test the card
  DEBUG_ASSERTION(*this,
		  "HandCard::isvalid(Trick):\n"
		  "  empty Card");

  // test whether the card is in the hand
  DEBUG_ASSERTION(hand.getpos(*this) != UINT_MAX,
		  "HandCard::isvalid(Trick):\n"
		  "  Card is not in the hand"
		  ": " << *this << "\n" << hand);

  // The first person can play all cards
  if (trick.isstartcard())
    return true;

  // if there is a card with the same tcolor on the hand as the startcard,
  // the card has to have the same tcolor
  if (hand.existstcolor(trick.startcard().tcolor()))
    return (trick.startcard().tcolor() == this->tcolor());

  // the tcolor of the startcard is not in the hand
  // the player can play any card
  return true;
} // bool HandCard::isvalid(Trick trick) const

