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

#include "trickpile.h"
#include "table.h"
#include "hand.h"

#include "ui.h"
#include "cards.h"

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

#include "../../utils/string.h"

namespace UI_GTKMM_NS {

#define TRICKPILE_TRICKS_STEP_BIG_PART \
  static_cast<int>(this->ui->cards->height() / 15.0)
#define TRICKPILE_TRICKS_STEP_SMALL_PART \
    static_cast<int>(this->ui->cards->width() / 30.0)
#define TRICKPILE_CARDS_STEP_BIG_PART \
    static_cast<int>(this->ui->cards->height() * 0)
#define TRICKPILE_CARDS_STEP_SMALL_PART \
    static_cast<int>(this->ui->cards->width() / 20.0)

    /**
     **
     ** Constructor
     **
     ** @param	table	the table
     ** @param	player	the player
     **
     ** @return	-
     ** 
     ** @author	Diether Knof
     **
     ** @version	0.6.1
     **
     **/
    TrickPile::TrickPile(Table* const table, Player const* const player) :
    HTIN(table, player)
    {
      return ;
    } // TrickPile::TrickPile(Table* const table, Player const* const player)

  /**
   **
   ** Destruktor
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.1
   **
   **/
  TrickPile::~TrickPile()
  {
    return ;
  } // TrickPile::~TrickPile()

  /**
   **
   ** draws the trickpile
   **
   ** @param	update	whether to update the area
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.1
   **
   **/
  void
    TrickPile::draw(bool const update)
    {
      if ((::game_status >= GAMESTATUS::GAME_INIT)
	  && (::game_status <= GAMESTATUS::GAME_FINISHED)) {
	int pos_x = this->pos_x();
	int pos_y = this->pos_y();
	int width = this->width();
	int height = this->height();

	// for the specialpoints update the values
	// I hope, there cannot be more specialpoints than cards, since that
	// is not tested here.
	switch(this->player->no()) {
	case 0:
	  pos_y += this->ui->cards->back().get_width();
	  pos_y -= this->ui->cards->back().get_height();
	  height -= this->ui->cards->back().get_width();
	  height += this->ui->cards->back().get_height();
	  break;
	case 1:
	  width -= this->ui->cards->back().get_width();
	  width += this->ui->cards->back().get_height();
	  break;
	case 2:
	  height -= this->ui->cards->back().get_width();
	  height += this->ui->cards->back().get_height();
	  break;
	case 3:
	  pos_x += this->ui->cards->back().get_width();
	  pos_x -= this->ui->cards->back().get_height();
	  width -= this->ui->cards->back().get_width();
	  width += this->ui->cards->back().get_height();
	  break;
	default:
	  DEBUG_ASSERTION(false,
			  "TrickPile::draw_tricks():\n"
			  "  player->no not valid: "
			  << this->player->no());
	  break;
	} // switch(this->player->no())

	if (update)
	  this->table().clear(Gdk::Rectangle(pos_x, pos_y, width, height));

	// draw the cards
	this->draw_tricks();
	if (this->player->game().rule()(Rule::SHOW_TRICKPILES_POINTS)
	    && (::setting(Setting::SHOW_TRICKPILES_POINTS)
		|| (::game_status == GAMESTATUS::GAME_FINISHED)))
	  this->draw_points();

	if (update)
	  this->table().update(Gdk::Rectangle(pos_x, pos_y, width, height));

      } // if (GAME_RESERVATION <= game_status < GAME_FINISHED)

      return ;
    } // void TrickPile::draw(bool const update = true)

  /**
   **
   ** draws the tricks in the trickpile
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.1
   **
   ** @todo	skip the cards, the human player(s) do know not to make a special points (because of further team informations)
   **
   **/
  void
    TrickPile::draw_tricks()
    {
      if (::game_status & GAMESTATUS::GAME) {
	unsigned trickno = this->player->number_of_tricks();

	if (this->table().in_game_review()) {
	  trickno = 0;
	  for (::TrickPile::const_iterator
	       t = this->player->trickpile().begin();
	       t != this->player->trickpile().end();
	       ++t)
	    if ((*t)->no() < this->table().game_review_trickno())
	      trickno += 1;
	    else
	      break;
	} // if (this->table().in_game_review())

	for (unsigned t = 0; t < trickno; t++) {
	  vector<Card> cards;
	  if (::setting(Setting::SHOW_SPECIAL_POINTS_IN_TRICKPILES))
	    cards = this->player->trickpile()[t]->specialpoints_cards(); 
	  unsigned const specialpoints_no = cards.size();
	  // ToDo: skip the cards, the human player(s) do know not to make a special points (because of further team informations)

	  while (cards.size() < this->player->game().playerno())
	    cards.push_back(Card());
	  for (unsigned c = 0; c < cards.size(); ++c) {
	    Card const& card = cards[c];
	    Rotation rotation = this->pile_rotation();
	    int x = (this->start_x()
		     + this->tricks_step_x() * t
		     + this->cards_step_x() * c);
	    int y = (this->start_y()
		     + this->tricks_step_y() * t
		     + this->cards_step_y() * c);
	    if (card) {
	      switch (this->player->no()) {
	      case 0:
		y += this->ui->cards->back(rotation).get_height();
		rotation = ROTATION::counterclockwise(rotation);
		y -= this->ui->cards->card(card, rotation).get_height();
		x -= this->cards_step_y() * c;
		break;
	      case 1:
		rotation = ROTATION::clockwise(rotation);
		y += this->cards_step_x() * c;
		break;
	      case 2:
		x += this->ui->cards->back(rotation).get_width();
		rotation = ROTATION::counterclockwise(rotation);
		x -= this->ui->cards->card(card, rotation).get_width();
		x -= this->cards_step_y() * c;
		break;
	      case 3:
		x += this->ui->cards->back(rotation).get_width();
		rotation = ROTATION::clockwise(rotation);
		x -= this->ui->cards->card(card, rotation).get_width();
		y -= this->cards_step_x() * c;
		break;
	      default:
		DEBUG_ASSERTION(false,
				"TrickPile::draw_tricks():\n"
				"  player->no not valid: "
				<< this->player->no());
		break;
	      } // switch (this->player->no())
	    } else { // if !(card)
	      x -= (specialpoints_no * this->cards_step_x());
	      y -= (specialpoints_no * this->cards_step_y());
	    } // if !(card)
	    this->table().pixmap->draw_pixbuf(this->table().gc,
					      this->ui->cards->card(card,
								    rotation),
					      0, 0,
					      x, y,
					      -1, -1,
					      Gdk::RGB_DITHER_NONE,
					      0, 0);
	  } // for (c < cards.size())
	} // for (t < this->player->number_of_tricks())
      } // if (::game_status & GAMESTATUS::GAME)

      return ;
    } // void TrickPile::draw_tricks()

  /**
   **
   ** draws the points in the trickpile
   **
   ** @param	update	whether to update the area (default: false)
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.1
   **
   **/
  void
    TrickPile::draw_points(bool const update)
    {
      unsigned number_of_tricks = this->player->number_of_tricks();
      this->table().trickpile_points_layout->set_text(DK::Utils::String::to_string(this->player->points_in_trickpile()));

      if (this->table().in_game_review()) {
	unsigned points = 0;
	number_of_tricks = 0;

	for (::TrickPile::const_iterator
	     t = this->player->trickpile().begin();
	     t != this->player->trickpile().end();
	     ++t)
	  if ((*t)->no() < this->table().game_review_trickno())
	    points += (*t)->points(),
		   number_of_tricks += 1;
	  else
	    break;

	this->table().trickpile_points_layout->set_text(DK::Utils::String::to_string(points));
      } // if (this->table().in_game_review())

      if (number_of_tricks == 0)
	return ;

      int number_of_cards = this->player->game().playerno();
      if (::setting(Setting::SHOW_SPECIAL_POINTS_IN_TRICKPILES)) {
	number_of_cards
	  -= this->player->trickpile().back()->specialpoints_cards().size(); 
	if (number_of_cards < 1)
	  number_of_cards = 1;
      }

      int layout_width, layout_height;
      this->table().trickpile_points_layout->get_pixel_size(layout_width,
							    layout_height);
      int const x = (this->start_x()
		     + (this->tricks_step_x()
			* (number_of_tricks - 1))
		     + (this->cards_step_x()
			* (number_of_cards - 1))
		     + this->ui->cards->width(this->pile_rotation()) / 2
		     - (layout_width / 2)
		    );
      int const y = (this->start_y()
		     + (this->tricks_step_y()
			* (number_of_tricks - 1))
		     + (this->cards_step_y()
			* (number_of_cards - 1))
		     + this->ui->cards->height(this->pile_rotation()) / 2
		     - (layout_height / 2)
		    );

      this->table().pixmap->draw_layout(this->table().trickpile_points_gc,
					x, y,
					this->table().trickpile_points_layout);

      if (update)
	this->table().update(Gdk::Rectangle(x, y, layout_width, layout_height));

      return ;
    } // void TrickPile::draw_points(bool const update = false)

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	rotation of the cards in the pile
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  Rotation
    TrickPile::pile_rotation() const
    {
      Rotation r = UP; // return value

      switch(this->position()) {
      case NORTH:
	r = LEFT;
	break;
      case SOUTH:
	r = RIGHT;
	break;
      case WEST:
	r = UP;
	break;
      case EAST:
	r = DOWN;
	break;
      } // switch(this->position())

      return r;
    } // Rotation TrickPile::pile_rotation() const

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	starting x position of the trickpile
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::pos_x() const
    {
      int value = this->start_x();

      switch(this->position()) {
      case NORTH:
      case EAST:
	value -= (this->width()
		  - this->ui->cards->width(this->pile_rotation()));
	break;
      case SOUTH:
      case WEST:
	break;
      } // switch(position)

      return value;
    } // int TrickPile::pos_x()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	starting y position of the trickpile
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::pos_y() const
    {
      int value = this->start_y();

      switch(this->position()) {
      case NORTH:
      case WEST:
	break;
      case SOUTH:
      case EAST:
	value -= (this->height()
		  - this->ui->cards->height(this->pile_rotation()));
	break;
      } // switch(position)

      return value;
    } // int TrickPile::pos_y()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	width of the trickpile
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::width() const
    {
      int const value = (this->ui->cards->width(this->pile_rotation())
			 + ((this->player->number_of_tricks() - 1)
			    * std::abs(this->tricks_step_x()))
			 + ((this->player->game().playerno() - 1)
			    * std::abs(this->cards_step_x()))
			);

      return value;
    } // int TrickPile::width()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	height of the trickpile
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::height() const
    {
      int const value = (this->ui->cards->height(this->pile_rotation())
			 + ((this->player->number_of_tricks() - 1)
			    * std::abs(this->tricks_step_y()))
			 + ((this->player->game().playerno() - 1)
			    * std::abs(this->cards_step_y()))
			);

      return value;
    } // int TrickPile::height()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	starting x position of the trickpile
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::start_x() const
    {
      int value = 0; // return value

      switch(this->position()) {
      case NORTH:
	value = (this->hand().start_x()
		 - this->ui->cards->width(this->pile_rotation())
		 - this->hand().margin_x());
	break;
      case SOUTH:
	value = (this->hand().start_x()
		 + this->hand().width()
		 + this->hand().margin_x());
	break;
      case WEST:
	value = this->hand().start_x();
	break;
      case EAST:
	value = (this->hand().start_x()
		 + this->hand().width()
		 - this->ui->cards->width(this->pile_rotation()));
	break;
      } // switch(this->position())

      return value;
    } // int TrickPile::start_x()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	starting y position of the trickpile
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::start_y() const
    {
      int value = 0; // return value

      switch(this->position()) {
      case NORTH:
	value = this->hand().start_y();
	break;
      case SOUTH:
	value = (this->hand().start_y()
		 + this->hand().height()
		 - this->ui->cards->height(this->pile_rotation()));
	break;
      case WEST:
	value = (this->hand().start_y()
		 + this->hand().height()
		 + this->hand().margin_y());
	break;
      case EAST:
	value = (this->hand().start_y()
		 - this->hand().margin_y()
		 - this->ui->cards->height(this->pile_rotation()));
	break;
      } // switch(this->position())

      return value;
    } // int TrickPile::start_y()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	x step of the tricks
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::tricks_step_x() const
    {
      int value = 0;

      if (!this->player->game().rule()(Rule::SHOW_TRICKS_IN_TRICKPILES)) {
	value = 0;
      } else { // if !(!this->player->game().rule()(Rule::SHOW_TRICKS_IN_TRICKPILES))

	switch(this->position()) {
	case NORTH:
	  value = -TRICKPILE_TRICKS_STEP_BIG_PART;
	  break;
	case SOUTH:
	  value = TRICKPILE_TRICKS_STEP_BIG_PART;
	  break;
	case WEST:
	  value = TRICKPILE_TRICKS_STEP_SMALL_PART;
	  break;
	case EAST:
	  value = -TRICKPILE_TRICKS_STEP_SMALL_PART;
	  break;
	} // switch(this->position())
      } // if !(!this->player->game().rule()(Rule::SHOW_TRICKS_IN_TRICKPILES))

      return value;
    } // int TrickPile::tricks_step_x()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	y step of the tricks
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::tricks_step_y() const
    {
      int value = 0;

      if (!this->player->game().rule()(Rule::SHOW_TRICKS_IN_TRICKPILES)) {
	value = 0;
      } else {  // if !(!this->player->game().rule()(Rule::SHOW_TRICKS_IN_TRICKPILES))

	switch(this->position()) {
	case NORTH:
	  value = TRICKPILE_TRICKS_STEP_SMALL_PART;
	  break;
	case SOUTH:
	  value = -TRICKPILE_TRICKS_STEP_SMALL_PART;
	  break;
	case WEST:
	  value = TRICKPILE_TRICKS_STEP_BIG_PART;
	  break;
	case EAST:
	  value = -TRICKPILE_TRICKS_STEP_BIG_PART;
	  break;
	} // switch(this->position())
      } // if !(!this->player->game().rule()(Rule::SHOW_TRICKS_IN_TRICKPILES))

      return value;
    } // int TrickPile::tricks_step_y()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	x step of the cards
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::cards_step_x() const
    {
      int value = 0;

      switch(this->position()) {
      case NORTH:
	value = -TRICKPILE_CARDS_STEP_BIG_PART;
	break;
      case SOUTH:
	value = TRICKPILE_CARDS_STEP_BIG_PART;
	break;
      case WEST:
	value = TRICKPILE_CARDS_STEP_SMALL_PART;
	break;
      case EAST:
	value = -TRICKPILE_CARDS_STEP_SMALL_PART;
	break;
      } // switch(this->position())

      return value;
    } // int TrickPile::cards_step_x()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	y step of the cards
   ** 
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  int
    TrickPile::cards_step_y() const
    {
      int value = 0;

      switch(this->position()) {
      case NORTH:
	value = TRICKPILE_CARDS_STEP_SMALL_PART;
	break;
      case SOUTH:
	value = -TRICKPILE_CARDS_STEP_SMALL_PART;
	break;
      case WEST:
	value = TRICKPILE_CARDS_STEP_BIG_PART;
	break;
      case EAST:
	value = -TRICKPILE_CARDS_STEP_BIG_PART;
	break;
      } // switch(this->position())

      return value;
    } // int TrickPile::cards_step_y()

  /**
   **
   ** -> result
   **
   ** @param	x	x position
   ** @param	y	y position
   **
   ** @return	the pointer to the trick under the mouse curser
   **		NULL if the pointer is over no trick
   **			or the trick cannot be shown (-> rules)
   ** 
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   ** @todo	transparency
   **
   **/
  ::Trick const*
    TrickPile::over_trick(int const x, int const y) const
    {
      if (::in_running_game()) {
	::TrickPile const& trickpile = this->player->trickpile();

	for (int t = trickpile.size() - 1; t >= 0; --t) {
	  if (trickpile[t]->no()
	      + this->player->game().rule()(Rule::SHOW_TRICKS_NUMBER)
	      < this->player->game().real_trick_current_no())
	    break;

	  int const c = this->player->game().playerno() - 1;

	  int const step_x = ((this->cards_step_x() > 0)
			      ? this->cards_step_x()
			      : -this->cards_step_x());
	  int const step_y = ((this->cards_step_y() > 0)
			      ? this->cards_step_y()
			      : -this->cards_step_y());
	  int const pos_x = (this->start_x()
			     + t * this->tricks_step_x()
			     + c * ((this->cards_step_x() > 0)
				    ? 0
				    : -step_x));

	  int const pos_y = (this->start_y()
			     + t * this->tricks_step_y()
			     + c * ((this->cards_step_y() > 0)
				    ? 0
				    : -step_y));

	  if ( (x >= pos_x)
	       && (x < (pos_x
			+ step_x * c
			+ this->ui->cards->back(this->pile_rotation()
					       ).get_width()))
	       && (y >= pos_y)
	       && (y < (pos_y
			+ step_y * c
			+ this->ui->cards->back(this->pile_rotation()
					       ).get_height()))
	     ) {
	    return trickpile[t];
	  }
	} // if (this->player has made the last trick)
      } // if (::in_running_game())

      return NULL;
    } // ::Trick const* TrickPile::over_trick(int const x, int const y)

} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
