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

#include "hand_card.h"
#include "../game/game.h"
#include "../party/rule.h"

Card const Card::CLUB_QUEEN(Card::CLUB, Card::QUEEN);
Card const Card::CHARLIE(Card::CLUB, Card::JACK);
Card const Card::DOLLE(Card::HEART, Card::TEN);

/**********************************************************************
 *
 ** Card:Card()
 *
 ** Parameters: None
 *
 ** Result: None
 *
 ** Version: Alpha
 *
 ** Description: 
 **   initizializing of color NOCARDCOLOR and value NOCARDVALUE
 *
 **********************************************************************/

Card::Card() :
color_(NOCARDCOLOR),
value_(NOCARDVALUE)
{ 
  DEBUG_CALLING(INFO_CARD && INFO_INIT,"Card::Card()");
  DEBUG_RETURNING_N(INFO_CARD && INFO_INIT,"Card::Card()");
}

/**********************************************************************
 *
 ** Card:Card(Card const& c)
 *
 ** Parameters: 
 **   c:  Card
 *
 ** Result: None
 *
 ** Version: Alpha
 *
 ** Description: 
 **   initizializing of color and value as c
 *
 **********************************************************************/

Card::Card(Card const& c):
color_(c.color()), 
value_(c.value())
{
  DEBUG_CALLING(INFO_CARD && INFO_INIT,"Card::Card(c)");

  DEBUG_RETURNING_N(INFO_CARD && INFO_INIT,"Card::Card(c)");
}

/**********************************************************************
 *
 ** Card:Card(Color c, Value v)
 *
 ** Parameters: 
 **   c: Color of to be created Card
 **   v: Value of to be created Card
 *
 ** Result: None
 *
 ** Version: Alpha
 *
 ** Description: 
 **   initizializing of color with c and of value with v
 *
 **********************************************************************/

Card::Card(Color const c, Value const v):
color_(c), 
value_(v)
{
  DEBUG_CALLING(INFO_CARD && INFO_INIT,"Card::Card(c,v)");
  
  if ((color() == NOCARDCOLOR) || (value() == NOCARDVALUE))
    color_ = NOCARDCOLOR, value_ = NOCARDVALUE;

  DEBUG_RETURNING_N(INFO_CARD && INFO_INIT,"Card::Card(c,v)");
}

#ifdef OUTDATED
/**********************************************************************
 *
 ** Card:Card(unsigned const color_u, unsigned const value_u)
 *
 ** Parameters:	color_u	- number of the color
 *		value_u	- number of the value
 *
 ** Result:	-
 *
 ** Version:	0.4.2
 *
 ** Description:	constructor
 *
 **********************************************************************/

Card::Card(unsigned const color_u, unsigned const value_u) :
color_(NOCARDCOLOR), 
value_(NOCARDVALUE)
{
  DEBUG_CALLING(INFO_CARD && INFO_INIT,"Card::Card(c,v)");

  switch(color_u) {
  case 0:
    this->color_ = DIAMOND;
    break;
  case 1:
    this->color_ = HEART;
    break;
  case 2:
    this->color_ = SPADE;
    break;
  case 3:
    this->color_ = CLUB;
    break;
  default:
    DEBUG_ASSERTION(false,
		    "Card::Card(unsigned color_u, unsigned value_u):\n"
		    "  invalid 'color_u'");
    break;
  } // switch(color_u)

  switch(value_u) {
  case 0:
    this->value_ = NINE;
    break;
  case 1:
    this->value_ = JACK;
    break;
  case 2:
    this->value_ = QUEEN;
    break;
  case 3:
    this->value_ = KING;
    break;
  case 4:
    this->value_ = TEN;
    break;
  case 5:
    this->value_ = ACE;
    break;
  default:
    DEBUG_ASSERTION(false,
		    "Card::Card(unsigned color_u, unsigned value_u):\n"
		    "  invalid 'value_u'");
    break;
  } // switch(value_u)

  if ((color() == NOCARDCOLOR)
      || (value() == NOCARDVALUE)) {
    color_ = NOCARDCOLOR;
    value_ = NOCARDVALUE;
  }

  DEBUG_RETURNING(VOID,
		  INFO_CARD && INFO_INIT,
		  "Card::Card(c,v)");
} // Card::Card(unsigned const color_u, unsigned const value_u)
#endif

/**
 **
 ** Konstruktor:
 ** reads 'color' and 'value' from 'name'
 **
 ** @param	name	the card name
 **
 ** @return	-
 **
 ** @version	0.5.2
 **
 ** @author	Diether Knof
 **   
 **/
Card::Card(string const& name) :
color_(NOCARDCOLOR), 
value_(NOCARDVALUE)
{
  DEBUG_CALLING(INFO_CARD && INFO_VALUE,
		"Card::Card(name)");

  istringstream istr(name);

  istr >> *this;

  DEBUG_RETURNING(VOID,
		  INFO_CARD && INFO_VALUE,
		  "Card::Card(name)");
} // Card::Card(string const& name)



/**********************************************************************
 *
 ** Card::~Card()
 *
 ** Parameters: None
 *
 ** Result: None
 *
 ** Version: Alpha
 *
 ** Description: 
 **   Destructor for Card
 *
 **********************************************************************/
Card::~Card()
{
  DEBUG_CALLING(INFO_CARD && INFO_INIT,"Card::~Card()");
  DEBUG_RETURNING_N(INFO_CARD && INFO_INIT,"Card::~Card()");
}

/**
 **
 ** copy operator
 **
 ** @param	card	card to copy
 **
 ** @return	copy of the card
 **
 ** @version	0.5.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
Card&
Card::operator=(Card const& card)
{
  DEBUG_CALLING(INFO_CARD && INFO_VALUE,
		"Card::operator=(Card const& card)");

  this->color_ = card.color();
  this->value_ = card.value();

  DEBUG_RETURNING(*this,
		  INFO_CARD && INFO_VALUE,
		  "Card::operator=(Card const& card)");
} // Card& Card::operator=(Card const& card)

/**
 **
 ** read the card from the stream
 **
 ** @param	istr	stream to read the card from
 **
 ** @return	stream
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
istream&
Card::read(istream& istr)
{
  DEBUG_CALLING(INFO_CARD && INFO_IO,
		"Card::read(istr)");

  istr >> this->color_ >> this->value_;

  DEBUG_RETURNING(istr,
		  INFO_CARD && INFO_IO,
		  "Card::read(istr)");
} // istrean& Card::read(istream& istr)

/**********************************************************************
 *
 ** Card::operator bool() const
 *
 ** Parameters:  None
 *
 ** Result: if the card is a real card
 *
 ** Version: Alpha
 *
 ** Description: 
 **   
 *
 **********************************************************************/

Card::operator bool() const
{
  DEBUG_CALLING(INFO_CARD && INFO_VALUE, "Card::operator bool()");
  DEBUG_RETURNING( (color() != NOCARDCOLOR)
		   && (value() != NOCARDVALUE) ,
		   INFO_CARD && INFO_VALUE, "Card::operator bool()");
}


/**********************************************************************
 *
 ** unsigned Card::points() const
 *
 ** Parameters:	-
 *
 ** Result:	points of card
 *
 ** Version:	0.4.2
 *
 ** Description: -> result
 **   
 *
 **********************************************************************/
unsigned
Card::points() const
{
  DEBUG_CALLING(INFO_CARD && INFO_VALUE,
		"Card::points()");

  // based on structure of enum type card values 
  // here can value be direct returned
  DEBUG_RETURNING(value(),
		  INFO_CARD && INFO_VALUE,
		  "Card::points()");
} // unsigned Card::points() const

/**
 **
 ** -> result
 **
 ** @param	game	the game
 **
 ** @return	true if card is trump in the game
 **
 ** @version	0.6.8
 **
 ** @author	Diether Knof
 **
 **/
bool
Card::istrump(Game const& game) const
{
  return this->istrump(game.type(), game.rule()(Rule::DOLLEN));
} // bool Card::istrump(Game game) const

/**
 **
 ** -> result
 **
 ** @param	gametype	the gametype
 ** @param	dollen		whether dollen are allowed
 **
 ** @return	true if card is trump in the game
 **
 ** @version	0.5.4
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 **/
bool
Card::istrump(GameType const gametype, bool const dollen) const
{
  if (this->isdolle(gametype, dollen))
    return true;

  switch (gametype) {
  case GAMETYPE::FIVE_NINES:
    if (this->value() == NINE)
      return true;
    break;
  case GAMETYPE::SIX_KINGS:
    if (this->value() == KING)
      return true;
    break;
  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:
    if (this->color() == DIAMOND)
      return true;
    if ((this->value() == JACK) || (this->value() == QUEEN))
      return true;
    break;
  case GAMETYPE::SOLO_JACK:
    if (this->value() == JACK)
      return true;
    break;
  case GAMETYPE::SOLO_QUEEN:
    if (this->value() == QUEEN)
      return true;
    break;
  case GAMETYPE::SOLO_KING:
    if (this->value() == KING)
      return true;
    break;
  case GAMETYPE::SOLO_QUEEN_JACK:
    if ((this->value() == JACK) || (this->value() == QUEEN))
      return true;
    break;
  case GAMETYPE::SOLO_KING_JACK:
    if ((this->value() == JACK) || (this->value() == KING))
      return true;
    break;
  case GAMETYPE::SOLO_KING_QUEEN:
    if ((this->value() == QUEEN) || (this->value() == KING))
      return true;
    break;
  case GAMETYPE::SOLO_KOEHLER:
    if ((this->value() == JACK) || (this->value() == QUEEN) || (this->value() == KING))
      return true;
    break;
  case GAMETYPE::SOLO_CLUB:
    if (this->color() == CLUB)
      return true;
    if ((this->value() == JACK)
	|| (this->value() == QUEEN))
      return true;
    break;
  case GAMETYPE::SOLO_HEART:
    if (this->color() == HEART)
      return true;
    if ((this->value() == JACK)
	|| (this->value() == QUEEN))
      return true;
    break;
  case GAMETYPE::SOLO_SPADE:
    if (this->color() == SPADE)
      return true;
    if ((this->value() == JACK)
	|| (this->value() == QUEEN))
      return true;
    break;
  case GAMETYPE::SOLO_MEATLESS:
    break;
  } // switch(gametype)

  return false;
} // bool Card::istrump(GameType gametype, bool dollen) const

/**
 **
 ** -> result
 **
 ** @param	game	the game
 **
 ** @return	true if card is a dolle
 **
 ** @version	0.6.8
 **
 ** @author	Diether Knof
 **
 **/
bool
Card::isdolle(Game const& game) const
{
  return this->isdolle(game.type(), game.rule()(Rule::DOLLEN));
} // bool Card::isdolle(Game game) const

/**
 **
 ** -> result
 **
 ** @param	gametype	the gametype
 ** @param	dollen		whether dollen are allowed
 **
 ** @return	true if card is a dolle
 **
 ** @version	0.6.8
 **
 ** @author	Diether Knof
 **
 **/
bool
Card::isdolle(GameType const gametype, bool const dollen) const
{
  if (!dollen)
    return false;

  if (*this != Card::DOLLE)
    return false;

  switch (gametype) {
  case GAMETYPE::NORMAL:
  case GAMETYPE::POVERTY:
  case GAMETYPE::GENSCHER:
  case GAMETYPE::MARRIAGE:
  case GAMETYPE::MARRIAGE_SOLO:
  case GAMETYPE::MARRIAGE_SILENT:
  case GAMETYPE::SOLO_CLUB:
  case GAMETYPE::SOLO_HEART:
  case GAMETYPE::SOLO_SPADE:
  case GAMETYPE::SOLO_DIAMOND:
    return true;
  case GAMETYPE::SIX_KINGS:
  case GAMETYPE::FOX_HIGHEST_TRUMP:
  case GAMETYPE::SOLO_MEATLESS:
  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::FIVE_NINES:
    return false;
  } // switch (gametype)

  return false;
} // bool Card::isdolle(GameType gametype, bool dollen) 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
 **
 ** @version	0.6.8
 **
 ** @author	Diether Knof
 **
 **/
bool
Card::less(HandCard const& card) const
{
  if (!*this)
    return true;

  return HandCard(card.hand(), *this).less(card);
} // bool HandCard::less(Card card) const

/**
 **
 ** -> result
 **
 ** @param	a	first card
 ** @param	b	second card
 **
 ** @result	whether the two cards are equal
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
bool
operator==(Card const& a, Card const& b)
{
  return ((a.value() == b.value())
	  && (a.color() == b.color()));
} // bool operator==(Card a, Card b)

/**
 **
 ** -> result
 **
 ** @param	a	first card
 ** @param	b	second card
 **
 ** @result	whether the two cards are different
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
bool operator!=(Card const& a, Card const& b)
{
  return !(a == b);
} // bool operator!=(Card a, Card b)

/**********************************************************************
 *
 ** string name(Card const& card)
 *
 ** Parameters:  card - the card
 *
 ** Result:  name of the card
 *
 ** Version: 0.4.1
 *
 ** Description: -> result
 *
 **********************************************************************/
string
name(Card const& card)
{
  DEBUG_CALLING(INFO_CARD && INFO_NAME,
		"string name(Card const& card)");

  DEBUG_RETURNING(name(card.color()) + " " + name(card.value()),
		  INFO_CARD && INFO_NAME,
		  "string name(Card const& card)");
} // string name(Card const& card)

/**
 **
 ** writes the card in the output stream
 **
 ** @param	ostr	output stream
 ** @param	card	card
 **
 ** @return	output stream
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
ostream&
operator<<(ostream& ostr, Card const& card)
{
  DEBUG_CALLING(INFO_CARD && INFO_IO,
		"ostream& operator<<(ostr, card)");

  ostr << name(card);

  DEBUG_RETURNING(ostr,
		  INFO_CARD && INFO_IO,
		  "ostream& operator<<(ostr, card)");
} // ostream& operator<<(ostream& ostr, Card const& card);

/**
 **
 ** read the card out of the stream
 **
 ** @param	istr	input stream
 ** @param	card	card
 **
 ** @return	output stream
 **
 ** @version	0.5.4
 **
 ** @author	Diether Knof
 **
 **/
istream&
operator>>(istream& istr, Card& card)
{
  DEBUG_CALLING(INFO_CARD && INFO_BASISTYPES && INFO_IO,
		"ostream& operator<<(ostr, card)");

  card.read(istr);

  DEBUG_RETURNING(istr,
		  INFO_CARD && INFO_BASISTYPES && INFO_IO,
		  "ostream& operator<<(ostr, card)");
} // istream& operator>>(istream& istr, Card& card);
