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

#ifdef USE_UI_GTKMM

#include "table.h"
#include "reservation.h"
#include "poverty.h"
#include "game_info.h"
#include "game_finished.h"
#include "game_review.h"
#include "party_points.h"
#include "party_finished.h"
#include "full_trick.h"
#include "last_trick.h"

#include "trick.h"
#include "hand.h"
#include "trickpile.h"
#include "icongroup.h"
#include "name.h"

#include "ui.h"
#include "main_window.h"
#include "gametype_info.h"
#include "announcement_info.h"
#include "swines_info.h"
#include "marriage_info.h"
#include "genscher_selection.h"
#include "genscher_info.h"
#include "card_suggestion.h"

#include "../../party/party.h"
#include "../../game/game.h"
#include "../../player/player.h"
#include "../../card/hand.h"
#include "../../card/trick.h"

#include <gtkmm/drawingarea.h>
#include <gtkmm/main.h>

namespace UI_GTKMM_NS {

  /**
   **
   ** Constructor
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.6
   **
   **/
  Table::Table(Base* const parent) :
    Base(parent),
  Gtk::DrawingArea(),
  cursor_type(NONE),
  //drawing_area(Gtk::manage(new Gtk::DrawingArea())),
  //drawing_area(this),
  pixmap(NULL),
  gc(NULL),
  poverty_shift_arrow_gc(NULL),
  name_layout(NULL),
  name_gc(NULL),
  name_active_gc(NULL),
  trickpile_points_layout(NULL),
  trickpile_points_gc(NULL),
  reservation_(),
  poverty_(NULL),
  game_finished_(NULL),
  party_points_(new PartyPoints(this)),
  party_finished_(NULL),
  gametype_info_(NULL),
  full_trick_(NULL),
  last_trick_(NULL),
  card_suggestion_(NULL),
  announcement_info_(NULL),
  swines_info_(NULL),
  marriage_info_(NULL),
  genscher_selection_(NULL),
  genscher_info_(NULL),
  trick_(),
  hand_(),
  trickpile_(),
  icongroup_(),
  name_(),
  gameinfo_(),
    gametype_known_(false),
    get_card_(false)
  {
    this->ui->main_window->container->add(*(this));

    this->show();

    this->add_events(Gdk::BUTTON_PRESS_MASK);
    this->add_events(Gdk::POINTER_MOTION_MASK);

    return ;
  } // Table::Table(Base* const parent)

  /**
   **
   ** Destruktor
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  Table::~Table()
  {
    delete this->poverty_;
    delete this->game_finished_;
    delete this->party_finished_;
    delete this->party_points_;
    delete this->gametype_info_;
    delete this->full_trick_;
    delete this->last_trick_;
    delete this->card_suggestion_;
    delete this->announcement_info_;
    delete this->swines_info_;
    delete this->marriage_info_;
    delete this->genscher_selection_;
    delete this->genscher_info_;

    return ;
  } // Table::~Table()

  /**
   **
   ** the party is opened
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::party_open()
    {
      this->draw_all();

      return ;
    } // void Table::party_open()

  /**
   **
   ** create the party points
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::party_start()
    {
      for (unsigned p = 0; p < this->ui->party().playerno(); ++p)
	this->reservation_.push_back(new Reservation(this, &this->ui->party().player(p)));

      return ;
    } // void Table::party_start()

  /**
   **
   ** show the party finished window
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.8
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::party_finish()
    {
      if (!this->party_finished_)
	this->party_finished_ = new PartyFinished(this);

      this->draw_all();
      this->party_points_->party_finish();

      this->party_finished_->party_finished();

      return ;
    } // void Table::party_finish()

  /**
   **
   ** delete the party points
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::party_close()
    {
      this->party_points_->party_close();
      while (!this->reservation_.empty()) {
	delete this->reservation_.back();
	this->reservation_.pop_back();
      }


      return ;
    } // void Table::party_close()

  /**
   **
   ** create the htin-objects
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::game_open()
    {
      this->gametype_known_ = false;

      this->trick_ = new Trick(this);
      for (vector<Player*>::const_iterator p = this->ui->game().player_begin();
	   p != this->ui->game().player_end();
	   p++)
	this->hand_.push_back(new Hand(this, *p));
      for (vector<Player*>::const_iterator p = this->ui->game().player_begin();
	   p != this->ui->game().player_end();
	   p++)
	this->trickpile_.push_back(new TrickPile(this, *p));
      for (vector<Player*>::const_iterator p = this->ui->game().player_begin();
	   p != this->ui->game().player_end();
	   p++)
	this->icongroup_.push_back(new Icongroup(this, *p));
      for (vector<Player*>::const_iterator p = this->ui->game().player_begin();
	   p != this->ui->game().player_end();
	   p++)
	this->name_.push_back(new Name(this, *p));


      delete this->poverty_;
      this->poverty_ = new Poverty(this, this->ui->game());

      this->draw_all();

      return ;
    } // void Table::game_open()

  /**
   **
   ** the game is redistributed
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.7b
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::game_redistribute()
    {
      this->gametype_known_ = false;

      delete this->poverty_;
      this->poverty_ = new Poverty(this, this->ui->game());

      this->draw_all();

      return ;
    } // void Table::game_redistribute()

  /**
   **
   ** gets the reservation of the given player:
   **
   ** @param	player	player, whose reservation schould be get
   **
   ** @return	reservation of the player
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  ::Reservation
    Table::reservation_get(Player const& player)
    {
      ::Reservation const r = this->reservation_[player.no()]->get();

      return r;
    } // ::Reservation Table::reservation_get(player)

  /**
   **
   ** the game starts
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::game_start()
    {
      this->gametype_known_ = true;

      this->draw_all();

      // if we have a solo player show the info window
      if (this->ui->game().type() != GAMETYPE::NORMAL) {

	if (!FAST_PLAY) {
	  if (!this->gametype_info_) {
	    this->gametype_info_ = new GametypeInfo(this);
	    this->gameinfo_.push_back(this->gametype_info_);
	  }
	  this->gametype_info_->show_information();
	} // if (!FASTPLAY)
      } // if (this->ui->game().type() != GAMETYPE::NORMAL)

      this->draw_all();

      return ;
    } // void Table::game_start()

  /**
   **
   ** the player has to play a card
   **
   ** @param	player	the active player
   **
   ** @return	-
   **
   ** @version	0.6.8
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::card_get(Player const& player)
    {
      this->get_card_ = true;
      this->mouse_cursor_update();
      this->hand(player).draw();

      return ;
    } // void Table::card_get(Player const& player)

  /**
   **
   ** the player has played a card
   **
   ** @param	player	the active player
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::card_got()
    {
      this->get_card_ = false;

      return ;
    } // void Table::card_got()

  /**
   **
   ** the player has played a card
   **
   ** @param	player	the active player
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::card_played(Player const& player)
    {
      if (this->card_suggestion_)
	this->card_suggestion_->card_played();

      this->hand(player).draw();
      this->mouse_cursor_update();

      return ;
    } // void Table::card_played(Player const& player)

  /**
   **
   ** the trick is full
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::trick_full()
    {
      this->trick().draw();
      this->name(this->ui->trick().winnerplayer()).draw();

      if (!FAST_PLAY) {
	if (!this->full_trick_) {
	  this->full_trick_ = new FullTrick(this);
	  this->gameinfo_.push_back(this->full_trick_);
	}
	this->full_trick_->show_information(this->ui->trick());
      }

      return ;
    } // void Table::trick_full()

  /**
   **
   ** move the trick in the trickpile
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   ** @todo	animation
   **
   **/
  void
    Table::trick_move_in_pile()
    {
      this->trickpile(this->ui->trick().winnerplayer()).draw();
      this->trick_->draw();

      return ;
    } // void Table::trick_move_in_pile()

  /**
   **
   ** show the last trick
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::show_last_trick()
    {
      if (!((::game_status >= GAMESTATUS::GAME_PLAY)
	    && (::game_status < GAMESTATUS::GAME_FINISHED)))
	return ;

#ifdef OUTDATED
      // 0.6.7 - 2004-11-20
      Game const& game = this->ui->party().game();

      if (game.trick_current_no() == 0)
	return ;

      if (!this->last_trick_) {
	this->last_trick_ = new LastTrick(this);
	this->gameinfo_.push_back(this->last_trick_);
      }

      if (this->last_trick_->is_visible()) {
	this->last_trick_->set_trickno(game.trick_current_no() - 1);
	this->last_trick_->present();
	return ;
      } // if (this->last_trick_ != NULL)


      this->last_trick_->show_last_trick(game.trick(game.trick_current_no() - 1));

      this->mouse_cursor_update();
#endif

      Game const& game = this->ui->party().game();
      this->show_trick(game.trick(game.real_trick_current_no() - 1));

      return ;
    } // void Table::show_last_trick()

  /**
   **
   ** show the given trick
   **
   ** @param	trick	trick to show
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::show_trick(::Trick const& trick)
    {
      if (!this->last_trick_) {
	this->last_trick_ = new LastTrick(this);
	this->gameinfo_.push_back(this->last_trick_);
      }

      if (this->last_trick_->is_visible()) {
	this->last_trick_->set_trickno(trick.no());
	this->last_trick_->present();
	return ;
      } // if (this->last_trick_ != NULL)


      this->last_trick_->show_last_trick(trick);

      this->mouse_cursor_update();

      return ;
    } // void Table::show_trick(::Trick const& trick)

  /**
   **
   ** show the card suggestion
   **
   ** @param	show_window	whether to show the window
   **
   ** @return	-
   **
   ** @version	0.6.8
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::show_card_suggestion(bool const show_window)
    {
      DEBUG_ASSERTION((this->ui->game().player_current().type()
		       == Player::HUMAN),
		      "Table::show_card_suggestion():\n"
		      "  current player is not human.");

      if (!this->card_suggestion_)
	this->card_suggestion_ = new CardSuggestion(this);

      this->card_suggestion_->show_information(show_window);

      return ;
    } // void Table::show_card_suggestion(bool show_window)

  /**
   **
   ** show the game finished window
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::game_finished()
    {
      if (!this->game_finished_)
	this->game_finished_ = new GameFinished(this);

      this->draw_all();

      this->party_points_->game_finished();

      if (!FAST_PLAY)
	this->game_finished_->game_finished();

      delete this->poverty_;
      this->poverty_ = NULL;

      this->gametype_known_ = false;

      return ;
    } // void Table::game_finished()

  /**
   **
   ** destroy the htin-objects
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::game_close()
    {
      delete this->trick_;
      this->trick_ = NULL;

      if (this->card_suggestion_)
	this->card_suggestion_->card_played();

      for (vector<Hand*>::iterator h = this->hand_.begin();
	   h != this->hand_.end();
	   h++)
	delete *h;
      this->hand_.clear();

      for (vector<TrickPile*>::iterator t = this->trickpile_.begin();
	   t != this->trickpile_.end();
	   t++)
	delete *t;
      this->trickpile_.clear();

      for (vector<Icongroup*>::iterator t = this->icongroup_.begin();
	   t != this->icongroup_.end();
	   t++)
	delete *t;
      this->icongroup_.clear();

      for (vector<Name*>::iterator n = this->name_.begin();
	   n != this->name_.end();
	   n++)
	delete *n;
      this->name_.clear();

      return ;
    } // void Table::game_close()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	whether the game is reviewed
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  bool
    Table::in_game_review() const
    {
      return ((::game_status == GAMESTATUS::GAME_FINISHED)
	      && (this->game_finished_)
	      && (this->game_finished_->game_review));
    } // bool Table::in_game_review() const

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	current tricknumber in the game review
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  unsigned
    Table::game_review_trickno() const
    {
      DEBUG_ASSERTION(this->in_game_review(),
		      "Table::game_review_trickno():\n"
		      "  not in game review");
      return this->game_finished_->game_review->trickno();
    } // unsigned Table::game_review_trickno() const

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	current trick in the game review
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  ::Trick const&
    Table::game_review_trick() const
    {
      DEBUG_ASSERTION(this->in_game_review(),
		      "Table::game_review_trickno():\n"
		      "  not in game review");
      return this->game_finished_->game_review->trick();
    } // ::Trick const& Table::game_review_trick() const

  /**
   **
   ** 'player' has announced 'announcement'
   **
   ** @param	announcement	the announcement that has been made
   ** @param	player		the player that has announced swines
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::announcement_made(Announcement const announcement,
			     Player const& player)
    {
      if (!this->announcement_info_) {
	this->announcement_info_ = new AnnouncementInfo(this);
	this->gameinfo_.push_back(this->announcement_info_);
      }
      this->announcement_info_->announcement_made(player);

      this->icongroup(player).draw_announcement();
      if ((announcement == ANNOUNCEMENT::REPLY)
	  || (announcement == ANNOUNCEMENT::NO120))
	for (vector<Icongroup*>::iterator i = this->icongroup_.begin();
	     i != this->icongroup_.end();
	     i++)
	  (*i)->draw_team();

      if (player.type() != Player::HUMAN) {

	if (!FAST_PLAY)
	  this->announcement_info_->show_information(player);

      } // if (player.type() != Player::HUMAN)

      return ;
    } // void Table::announcement_made(Announcement const announcement, Player const& player)

  /**
   **
   ** swines have been announced
   **
   ** @param	player	the player that has announced swines
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::swines_announced(Player const& player)
    {
      this->icongroup(player).draw_swines();
      this->hand(player).draw();

      if (player.type() != Player::HUMAN) {

	if (!FAST_PLAY) {
	  if (!this->swines_info_) {
	    this->swines_info_ = new SwinesInfo(this);
	    this->gameinfo_.push_back(this->swines_info_);
	  }
	  this->swines_info_->show_swines_information(player);
	} // if (!FAST_PLAY)

      } // if (player.type() != Player::HUMAN)

      return ;
    } // void Table::swines_announced(Player const& player)

  /**
   **
   ** hyperswines have been announced
   **
   ** @param	player	the player that has announced hyperswines
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::hyperswines_announced(Player const& player)
    {
      this->icongroup(*player.game().swines_owner()).draw_swines();
      this->icongroup(player).draw_swines();
      this->hand(player).draw();

      if (player.type() != Player::HUMAN) {

	if (!FAST_PLAY) {
	  if (!this->swines_info_) {
	    this->swines_info_ = new SwinesInfo(this);
	    this->gameinfo_.push_back(this->swines_info_);
	  }
	  this->swines_info_->show_hyperswines_information(player);
	} // if (!FAST_PLAY)

      } // if (player.type() != Player::HUMAN)

      return ;
    } // void Table::hyperswines_announced(Player const& player)

  /**
   **
   ** marriage: Information of the new team
   **
   ** @param	bridegroom	the player with the marriage
   ** @param	bride		the bride
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.6
   **
   **/
  void
    Table::marriage(Player const& bridegroom,
		    Player const& bride)
    {
      this->icons_update();

      if (!FAST_PLAY) {
	if (!this->marriage_info_) {
	  this->marriage_info_ = new MarriageInfo(this);
	  this->gameinfo_.push_back(this->marriage_info_);
	}
	this->marriage_info_->show_information(bridegroom, bride);
      } // if (!FAST_PLAY)

      return ;
    } // void Table::marriage(Player const& bridegroom, Player const& bride)

  /**
   **
   ** select a partner
   **
   ** @param	genscher	player with the genscher
   **
   ** @return	pointer to the new teammate,
   **		NULL if the genscher is not to be announced
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.6
   **
   **/
  Player const*
    Table::select_genscher_partner(Player const& genscher)
    {
      if (!this->genscher_selection_)
	this->genscher_selection_ = new GenscherSelection(this);

      Player const* const partner
	= this->genscher_selection_->select_partner(genscher);

      return partner;
    } // Player const* Table::select_genscher_partner(Player const& genscher)

  /**
   **
   ** genscher information
   **
   ** @param	genscher	player with the genscher
   ** @param	partner		new partner
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.6
   **
   **/
  void
    Table::genscher(Player const& genscher,
		    Player const& partner)
    {
      if (!this->genscher_info_) {
	this->genscher_info_ = new GenscherInfo(this);
	this->gameinfo_.push_back(this->genscher_info_);
      }

      this->icons_update();

      if (genscher.type() != Player::HUMAN) {

	if (!FAST_PLAY)
	  this->genscher_info_->show_information(genscher, partner);

      } // if (genscher.type() != Player::HUMAN)

      return ;
    } // void Table::genscher(Player const& genscher, Player const& partner)

  /**
   **
   ** event: mouse click
   **
   ** @param	event	the event
   **
   ** @return	whether there was a reaction
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  bool
    Table::on_button_press_event(GdkEventButton* button_event)
    {
      int const x = static_cast<int>(button_event->x);
      int const y = static_cast<int>(button_event->y);

      if (this->in_game_review()) {
	switch (button_event->button) {
	case 3:
	case 4:
	  this->game_finished_->game_review->previous_trick();
	  this->mouse_cursor_update();
	  return true;
	case 2:
	  this->game_finished_->game_review->set_trickno(0);
	  this->mouse_cursor_update();
	  return true;
	case 1:
	case 5:
	  this->game_finished_->game_review->following_trick();
	  this->mouse_cursor_update();
	  return true;
	default:
	  break;
	}
      } // if (this->in_game_review)

      // shifting cards in a poverty
      if (::game_status == GAMESTATUS::GAME_POVERTY_SHIFT)
	if (this->poverty().button_press_event(button_event)) {
	  this->mouse_cursor_update();
	  return true;
	}

      { // test, whether the last trick shall be shown
	if (::in_running_game()) {
	  Game const& game = this->ui->game();
	  for (vector<Player*>::const_iterator p = game.player_begin();
	       p != game.player_end();
	       ++p) {
	    ::Trick const* const trick
	      = this->trickpile(**p).over_trick(x, y);
	    if (trick) {
	      switch (button_event->button) {
	      case 1: // left mouse button
		this->show_trick(*trick);
		this->mouse_cursor_update();
		return true;
	      default:
		break;
	      } // switch (button_event->button)
	    } // if (trick)
	  } // for (p \in game.player)
	} // if (::game_status in game)
      } // test, whether the last trick shall be shown

      { // close all open gameinfo-dialogs
	for (vector<GameinfoDialog*>::iterator
	     gid = this->gameinfo_.begin();
	     gid != this->gameinfo_.end();
	     ++gid)
	  (*gid)->close();
      } // close all open gameinfo-dialogs

      { // test, whether a card shall be played
	if (this->get_card()) {
	  Player& player = this->ui->game().player_current();
	  if (player.type() == Player::HUMAN) {
	    unsigned const cardno = this->hand(player).cardno_at_position(x, y);

	    if (cardno != UINT_MAX) {
	      switch (button_event->button) {
	      case 1: // left mouse button
		player.sorted_hand().request_position_all(cardno);
		// ToDo: only do this, if the card is in the hand
		//       (for a bug report replay)
		this->ui->thrower(player.sorted_hand().requested_card());

		this->mouse_cursor_update();
		return true;
	      case 3: // right mouse button
		if (this->card_suggestion_)
		  this->card_suggestion_->play_card();
		return true;
	      default:
		break;
	      } // switch (button_event->button)
	    } // if (cardno != UINT_MAX)
	  } // if (player is human)
	} // if (::game_status == GAMESTATUS::GAME_PLAY)
      } // test, whether a card shall be played

      return false;
    } // bool Table::on_button_press_event(GdkEventButton* event)

} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
