/**********************************************************************
 *
 *   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"

#ifdef USE_UI_GTK

#include "hands.h"
#include "gtk_gui.h"
#include "card.h"
#include "playfield.h"
#include "table.h"

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

namespace GTK_DOKO {

  /**
   *
   * Handg::Handg(Handgs* hands, Player const& player)
   *
   * @param	hands	the hands
   *		player	the player, this hand belongs to
   *
   * @return	-
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  Handg::Handg(Handgs* hands, Player const& player) :
    HTINBase(hands, player)
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_INIT,
		    "Handg::Handg(hands, player)");

      DEBUG_RETURNING(VOID,
		      INFO_GTK_HAND && INFO_GTK_INIT,
		      "Handg::Handg(hands, player)");
    } // Handg::Handg(Handgs* hands, Player const& player)

  /**
   *
   * -> result
   *
   * @param	-
   *
   * @return	the hands, this hand belong to
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  Handgs&
    Handg::hands()
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_VALUE,
		    "Handg::hands()");

      DEBUG_RETURNING(static_cast<Handgs&>(this->group()),
		      INFO_GTK_HAND && INFO_GTK_VALUE,
		      "Handg::hands()");
    } // Handgs& Handg::hands()

  /**
   *
   * -> result
   *
   * @param	-
   *
   * @return	starting x position of the hand
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  int
    Handg::start_x()
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_VALUE,
		    "Handg::start_x()");

      int value = 0; // return value

      switch(this->player().no()) {
      case 0:
      case 2:
	value = (this->hands().start_x()
		 + this->hands().hand(const_cast<Game const&>(::party.game())
				      .player(1)).width()
		 + HAND_MARGIN_LEFT
		 + this->hands().table().icongroup(const_cast<Game const&>(::party.game())
						   .player(1)).width()
		 + HAND_MARGIN_LEFT);
	break;
      case 1:
	value = this->hands().start_x();
	break;
      case 3:
	value = this->hands().start_x() + this->hands().width() - this->width();
	break;
      default:
	DEBUG_ASSERTION(false,
			"Handg::start_x():\n"
			"  `this->player().no()` not valid (not 0, 1, 2, 3)");
      } // switch(this->player().no())

      DEBUG_RETURNING(value,
		      INFO_GTK_HAND && INFO_GTK_VALUE,
		      "Handg::start_x()");
    } // Handg::start_x()

  /**
   *
   * -> result
   *
   * @param	-
   *
   * @return	Starting y position of the hand
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  int
    Handg::start_y()
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_VALUE,
		    "Handg::start_y()");

      int value = 0; // return value

      switch(this->player().no()) {
      case 0:
	value = this->hands().start_y() + this->hands().height() - this->height();
	break;
      case 1:
      case 3:
	value = (this->hands().start_y()
		 + this->hands().table().hand(const_cast<Game const&>(::party.game())
					      .player(2)).height()
		 + HAND_MARGIN_TOP
		 + this->hands().table().icongroup(const_cast<Game const&>(::party.game())
						   .player(2)).height()
		 + HAND_MARGIN_TOP);
	break;
      case 2:
	value = this->hands().start_y();
	break;
      default:
	DEBUG_ASSERTION(false,
			"Handg::start_y():\n"
			"  `this->player().no()` not valid (not 0, 1, 2, 3)");
      } // switch(this->player().no())

      DEBUG_RETURNING(value,
		      INFO_GTK_HAND && INFO_GTK_VALUE,
		      "Handg::start_y()");
    } // Handg::start_y()

  /**
   *
   * -> result
   *
   * @param	-
   *
   * @return	width of the hand
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  unsigned
    Handg::width()
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_VALUE,
		    "Handg::width()");

      unsigned value = 0; // return value

      switch(this->player().no()) {
      case 0:
      case 2:
	value = (this->hands().width()
		 - this->hands().hand(const_cast<Game const&>(::party.game())
				      .player(3)).width()
		 - HAND_MARGIN_RIGHT
		 - this->hands().table().icongroup(const_cast<Game const&>(::party.game())
						   .player(3)).width()
		 - HAND_MARGIN_RIGHT
		 - HAND_MARGIN_LEFT
		 - this->hands().table().icongroup(const_cast<Game const&>(::party.game())
						   .player(1)).width()
		 - HAND_MARGIN_LEFT
		 - this->hands().hand(const_cast<Game const&>(::party.game())
				      .player(1)).width());
	break;
      case 1:
      case 3:
	value = this->gui().cards().width(this->player());
	break;
      default:
	DEBUG_ASSERTION(false,
			"Handg::width():\n"
			"  `this->player().no()` not valid (not 0, 1, 2, 3)");
      } // switch(this->player().no())

      DEBUG_RETURNING(value,
		      INFO_GTK_HAND && INFO_GTK_VALUE,
		      "Handg::width()");
    } // unsigned Handg::width()

  /**
   *
   * -> result
   *
   * @param	-
   *
   * @return	height of the hand
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  unsigned
    Handg::height()
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_VALUE,
		    "Handg::height()");

      unsigned value = 0; // return value

      switch(this->player().no()) {
      case 0:
      case 2:
	value = this->gui().cards().height(this->player());
	break;
      case 1:
      case 3:
	value = (this->hands().height()
		 - this->hands().hand(const_cast<Game const&>(::party.game())
				      .player(0)).height()
		 - HAND_MARGIN_BOTTOM
		 - this->hands().table().icongroup(const_cast<Game const&>(::party.game())
						   .player(0)).height()
		 - HAND_MARGIN_BOTTOM
		 - HAND_MARGIN_TOP
		 - this->hands().table().icongroup(const_cast<Game const&>(::party.game())
						   .player(2)).height()
		 - HAND_MARGIN_TOP
		 - this->hands().hand(const_cast<Game const&>(::party.game())
				      .player(2)).height());
	break;
      default:
	DEBUG_ASSERTION(false,
			"Handg::height():\n"
			"  `this->player().no()` not valid (not 0, 1, 2, 3)");
      } // switch(this->player().no())

      DEBUG_RETURNING(value,
		      INFO_GTK_HAND && INFO_GTK_VALUE,
		      "Handg::height()");
    } // unsigned Handg::height()

  /**
   *
   * -> result
   *
   * @param	-
   *
   * @return	Step of the cards in the hand
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  double
    Handg::cards_step_x()
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_VALUE,
		    "Handg::cards_step_x()");

      double value = 0; // return value

      switch(this->player().no()) {
      case 0:
      case 2:
	value = ( (this->width() - this->gui().cards().width(this->player()))
		  / double(this->player().hand().cardsnumber_all() - 1));
	break;
      case 1:
      case 3:
	value = 0;
	break;
      default:
	DEBUG_ASSERTION(false,
			"Handg::cards_step_y():\n"
			"  `this->player().no()` not valid (not 0, 1, 2, 3)");
      } // switch(this->player().no())

      DEBUG_RETURNING(value,
		      INFO_GTK_HAND && INFO_GTK_VALUE,
		      "Handg::cards_step_x()");
    } // Handg::cards_step_x()

  /**
   *
   * -> result
   *
   * @param	-
   *
   * @return	Step of the cards in the hand
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  double
    Handg::cards_step_y()
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_VALUE,
		    "Handg::cards_step_y()");

      double value = 0; // return value

      switch(this->player().no()) {
      case 0:
      case 2:
	value = 0;
	break;
      case 1:
      case 3:
	value = ((this->height() - this->gui().cards().height(this->player()))
		 / double(this->player().hand().cardsnumber_all() - 1));
	break;
      default:
	DEBUG_ASSERTION(false,
			"Handg::cards_step_y():\n"
			"  `this->player().no()` not valid (not 0, 1, 2, 3)");
      } // switch(this->player().no())

      DEBUG_RETURNING(value,
		      INFO_GTK_HAND && INFO_GTK_VALUE,
		      "Handg::cards_step_y()");
    } // Handg::cards_step_y()

  /**
   *
   * draws the hand in the table
   *
   * @param	update	Whether the hand should be updated (default: true)
   *
   * @return	-
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   */
  void
    Handg::draw(bool const update)
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_DRAW,
		    "Handg::draw(update)");

      const ::Hand& hand
	= ((::game_status & GAMESTATUS::GAME) ?
	   this->player().hand() :
	   ::Hand());

      // redraw the background
      this->hands().table().background_draw(this->start_x(), this->start_y(),
					    this->width() + 1, this->height() + 1);

      // draw the card or the background
      for (unsigned i = 0; i < hand.cardsnumber_all(); i++) {
	if (!hand.played(i)) {
	  if ((::party.game().rule()(Rule::SHOW_ALL_HANDS)
	       && ::setting(Setting::SHOW_ALL_HANDS))
	      || (this->player().type() & Player::HUMAN)) {
	    this->gui().cards().card(hand.card_all(i), this->player()
				    ).draw(this->hands().table().pixmap(), 
					   int(this->start_x()
					       + i * this->cards_step_x()),
					   int(this->start_y()
					       + i * this->cards_step_y())
					  );
	  } else {
	    this->gui().cards().back(this->player()
				    ).draw(this->hands().table().pixmap(), 
					   int(this->start_x()
					       + i * this->cards_step_x()),
					   int(this->start_y()
					       + i * this->cards_step_y())
					  );
	  }
	} else {
	  if (game_status == GAMESTATUS::GAME_FINISHED) {
	    if ( (this->gui().playfield().table().last_trick().trickno()
		  == UINT_MAX)
		 || (this->gui().playfield().table().last_trick().trickno() <
		     hand.played_trick(i)))
	      // draw all cards, if the game is finished
	      this->gui().cards().card(hand.card_all(i), this->player()
				      ).draw(this->hands().table().pixmap(), 
					     int(this->start_x()
						 + i * this->cards_step_x()),
					     int(this->start_y()
						 + i * this->cards_step_y())
					    );
	  } // if (::game_status == GAMESTATUS::GAME_FINISHED)
	} // if (!hand.played(i)
      } // for (i < hand.cardsnumber())

      // update the DrawingArea
      if (update)
	this->hands().table().update(this->start_x(),
				     this->start_y(),
				     this->width() + 1,
				     this->height() + 1);

      DEBUG_RETURNING(VOID,
		      INFO_GTK_HAND && INFO_GTK_DRAW,
		      "Handg::draw(update)");
    } // int Handg::draw(update)

  /**
   *
   * void Handg::play_card(Card const& card)
   *
   * @param	card	card, that is to be removed
   *
   * @return	-
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   * Description:	removes the given card -- redraws the hand
   *
   */
  void
    Handg::play_card(Card const& card)
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_UPDATE,
		    "Handg::play_card()");

      this->draw();

      DEBUG_RETURNING(VOID,
		      INFO_GTK_HAND && INFO_GTK_UPDATE,
		      "Handg::play_card()");
    } // void Handg::play_card(card)

  /**
   *
   * bool Handg::mouse_click(GdkEventButton* button_event)
   *
   * @param	button_event	data of the button event
   *
   * @return	whether the click has been accepted
   * 
   * @version	0.4.4
   *
   * @author	Diether Knof
   *
   * Description:	a mouse button is pressed:
   *		if it is a human's turn, pick the selected card
   *
   */
  bool
    Handg::mouse_click(GdkEventButton* button_event)
    {
      DEBUG_CALLING(INFO_GTK_HAND && INFO_GTK_EVENT,
		    "Handg::mouse_click()");

      // whether the mouse click leads to an action
      bool accepted = false;

      // look, if the mouse is over the hand of the player
      if ( (button_event->x >= this->start_x() ) &&
	   (button_event->x < (this->start_x() + this->width())
	   ) && 
	   (button_event->y >= this->start_y() ) &&
	   (button_event->y < (this->start_y() + this->height())
	   )
	 ) {

	Hand const& hand = this->player().hand();

	// search the card, that is below the mouse
	int n; // number of the card
	// the searching is from the right, because the right cards
	// lie over the left
	for (n = hand.cardsnumber_all() - 1; n >= 0; n--) {
	  if ( (button_event->x >= this->start_x() + n * this->cards_step_x())
	       &&
	       (button_event->x
		< (this->start_x()
		   + n * this->cards_step_x()
		   + this->gui().cards().width(this->player())
		  )
	       )
	       &&
	       (button_event->y >= (this->start_y() + n * this->cards_step_y()))
	       &&
	       (button_event->y
		< (this->start_y()
		   + n * this->cards_step_y()
		   + this->gui().cards().height(this->player())))
	     ) {
	    // test, whether the card has already been played
	    if (!hand.played(n)) {
	      // test, if the point is transparent
	      if (!(this->gui().cards().card(hand.card_all(n), this->player())
		    .is_transparent(unsigned(button_event->x
					     - (this->start_x()
						+ n * this->cards_step_x())),
				    unsigned(button_event->y
					     - (this->start_y()
						+ n * this->cards_step_y())))
		   )
		 ) {
		break;
	      } // if (point under the mouse not transparent
	    } // if (card not played)
	  } // if (mouse over the card}
      } // for(n)
      // the mouse is over a card
      if (n >= 0) {
	this->gui().select_card_all(n);
	accepted = true;
      }
    } // if (mouse over hand)

  DEBUG_RETURNING(accepted,
		  INFO_GTK_HAND && INFO_GTK_EVENT,
		  "Handg::mouse_click()");
} // bool Handg::mouse_click(GdkEventButton)

} // namespace GTK_DOKO
#endif // #ifdef USE_UI_GTK
