/**********************************************************************
 *
 *   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 "ui.h"
#include "translations.h"
#include "preferences.h"
#include "table.h"
#include "party_settings.h"
#include "players.h"
#include "gameinfo_dialog.h"
#include "game_finished.h"
#include "game_review.h"
#include "main_window.h"
#include "chatter.h"
#include "bug_report.h"
#include "error.h"
#include "splash_screen.h"

#include "../help.h"
#include "../../basistypes.h"
#include "../../party/party.h"
#include "../../game/game.h"
#include "../../misc/setting.h"

#include "widgets/stock_button.h"

#include <gtkmm/main.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/stock.h>
#include <gtkmm/label.h>
#include <gtkmm/button.h>
#include <gtkmm/textview.h>
#include <gdk/gdkkeysyms.h>

namespace UI_GTKMM_NS {

  /**
   **
   ** updates the UI
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    UI_GTKMM::update()
    {
      while(this->kit->events_pending())
	this->kit->iteration();

      this->thrower.throw_it();

      return ;
    } // void UI_GTKMM::update()

  /**
   **
   ** sleeps the given time
   ** (in miliseconds, UINT_MAX for infinity)
   **
   ** @param	slee_usec	time to sleep
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   ** @todo	remove variable 'sleeping'
   **
   **/
  void
    UI_GTKMM::sleep(unsigned const sleep_usec)
    {
      SigC::Connection timeout_connection
	= Glib::signal_timeout().connect(&UI_GTKMM::stop_sleeping,
					 //SigC::slot(*(this->kit),
					 //&Gtk::Main::quit),
					 sleep_usec);

      UI_GTKMM::sleeping = true;

      while (!this->thrower
	     && UI_GTKMM::sleeping)
	Gtk::Main::iteration();

      timeout_connection.disconnect();

      this->thrower.throw_it();

      return ;
    } // void UI_GTKMM::sleep(unsigned const sleep_usec)

  /**
   **
   ** quits the innermost main loop of gtk
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    UI_GTKMM::main_quit()
    {
      if (this->kit->level() > 0)
	this->kit->quit();

      return ;
    } // void UI_GTKMM::main_quit()

  /**
   **
   ** quits the innermost main loop of gtk
   **
   ** @param	-
   **
   ** @return	true
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  bool
    UI_GTKMM::stop_sleeping()
    {
      if (Gtk::Main::level() > 0)
	Gtk::Main::quit();

      UI_GTKMM::sleeping = false;

      return true;
    } // static bool UI_GTKMM::stop_sleeping()

  /**
   **
   ** quits the program
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    UI_GTKMM::quit_program()
    {
      this->thrower(GAMESTATUS::QUIT);
      this->main_quit();

      return ;
    } // void UI_GTKMM::quit_program()

  /**
   **
   ** a key has been pressed
   **
   ** @param	key	the key
   **
   ** @return	true (stop other handlers from being invoked for the event)
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   ** @todo	some shortcuts (search 'ToDo')
   **
   **/
  bool
    UI_GTKMM::key_press(GdkEventKey const* const key)
    {
#if 0
      struct GdkEventKey {
	GdkEventType type;
	GdkWindow *window;
	gint8 send_event;
	guint32 time;
	guint state;
	guint keyval;
	gint length;
	gchar *string;
	guint16 hardware_keycode;
	guint8 group;
      };
#endif

      if (this->table
	  && this->table->in_game_review())
	if (!this->table->game_finished_->game_review->key_press(key))
	  return true;

      if (key->state == GDK_SHIFT_MASK) {
	switch (key->keyval) {
	case GDK_Escape: // quit the program
	  this->quit_program();
	  break;
	} // switch (key->keyval)
      } // if (key->state == GDK_SHIFT_MASK)

      if (::game_status == GAMESTATUS::GAME_PLAY) {
	// play a card?
	Player& player = const_cast<Player&>(this->game().player_current());
	if (player.type() == Player::HUMAN) {
	SortedHand& hand = player.sorted_hand();
	  if (!(key->state & ~GDK_SHIFT_MASK)) {
	    switch (key->keyval) {
	    case GDK_1:
	      hand.request_position_all(0);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_2:
	      hand.request_position_all(1);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_3:
	      hand.request_position_all(2);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_4:
	      hand.request_position_all(3);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_5:
	      hand.request_position_all(4);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_6:
	      hand.request_position_all(5);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_7:
	      hand.request_position_all(6);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_8:
	      hand.request_position_all(7);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_9:
	      hand.request_position_all(8);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_0:
	    case GDK_a:
	      hand.request_position_all(9);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_b:
	      hand.request_position_all(10);
	      this->thrower(hand.requested_card());
	      break;
	    case GDK_c:
	      hand.request_position_all(11);
	      this->thrower(hand.requested_card());
	      break;
	    } // switch (key->keyval)
	  } // if ((key->state & ~GDK_SHIFT_MASK) == 0)

	} // if (player.type() == Player::HUMAN)
      } // if (::game_status == GAMESTATUS::GAME_PLAY)

      if (!(key->state & ~GDK_SHIFT_MASK)) {
	switch (key->keyval) {
	case GDK_Clear: // close windows
#ifdef DKNOF
	  COUT << "Space" << endl;
#endif
	  { // close all open gameinfo-dialogs
	    for (vector<GameinfoDialog*>::iterator
		 gid = this->table->gameinfo_.begin();
		 gid != this->table->gameinfo_.end();
		 ++gid)
	      (*gid)->close();
	  } // close all open gameinfo-dialogs
	  break;
	case GDK_C: // show chat window
	  this->chatter->show();
	  break;

	case GDK_l: // show last trick
	  // -> menu.cpp
	  break;
	case GDK_n: // next announcement
	  if ((::game_status >= GAMESTATUS::GAME_PLAY)
	      && (::game_status < GAMESTATUS::GAME_FINISHED))
	    if (this->game().humanno() == 1) {
	      Announcement const& last_announcement
		= this->game().announcement_of_team(this->game().human_player()->team()).announcement;

	      if (last_announcement < ANNOUNCEMENT::NO0)
		if (last_announcement == ANNOUNCEMENT::NOANNOUNCEMENT)
		  this->game().announcement_make(ANNOUNCEMENT::NO120,
						   *(this->game().human_player())
						  );
		else
		  this->game().announcement_make(Announcement(last_announcement
								+ 1),
						   *(this->game().human_player())
						  );
	    } // if (only one human
	  break;
	case GDK_m: // announcement reply
	  if ((::game_status >= GAMESTATUS::GAME_PLAY)
	      && (::game_status < GAMESTATUS::GAME_FINISHED))
	    if (this->game().humanno() == 1)
	      this->game().announcement_make(ANNOUNCEMENT::REPLY,
					       *(this->game().human_player())
					      );
	  break;
	case GDK_h: // card suggestion
	  // -> menu.cpp
	  break;
	case GDK_r: // show the rules
	  // -> menu.cpp
	  break;
	case GDK_p: // show the party points
	  // -> menu.cpp
	  break;
	case GDK_P: // show the players
#ifdef WORKAROUND
	  if (this->party_settings
	      && this->party_settings->players)
	    this->party_settings->players->present();
#else
	  // -> menu.cpp
#endif
	  break;
	case GDK_F1: // show manual
	  // -> menu.cpp
	  break;
	case GDK_F2: // show preferences
	  // -> menu.cpp
	  break;
	case GDK_Pause: // create a bug report
	case GDK_Break: // create a bug report
	  this->bug_report->create_report();
	  break;
	case GDK_period: // toggle show valid cards
	  // -> menu.cpp
	  break;
	case GDK_Escape:
	  break;
	} // switch (key->keyval)
      } // if (key->state == 0)

      if ((key->state & ~GDK_SHIFT_MASK) == GDK_CONTROL_MASK) {
	switch (key->keyval) {
	case GDK_a: // toggle 'show all hands'
	  // -> menu.cpp
	  break;
	case GDK_n: // start new party
	  // -> menu.cpp
	  break;
	case GDK_l: // load party
	  // -> menu.cpp
	  break;
	case GDK_s: // save party
	  // -> menu.cpp
	  break;
	case GDK_q: // quit the program
	  // -> menu.cpp
	  break;
	case GDK_r: // redraw all
	  this->redraw_all();
	  break;
	case GDK_0: // default card size
	  ::setting.set(Setting::ORIGINAL_CARDS_SIZE, true);
	  break;
	case GDK_plus: // increase card size
	  ::setting.set(Setting::CARDS_HEIGHT,
			::setting(Setting::CARDS_HEIGHT) + 1);
	  ::setting.set(Setting::ORIGINAL_CARDS_SIZE, false);
	  break;
	case GDK_minus: // decrease card size
	  ::setting.set(Setting::CARDS_HEIGHT,
			::setting(Setting::CARDS_HEIGHT) - 1);
	  ::setting.set(Setting::ORIGINAL_CARDS_SIZE, false);
	  break;
	} // switch (key->keyval)
      } // if (key->state == GDK_CONTROL_MASK)

      return false;
    } // bool UI_GTKMM::key_press(GdkEventKey const* const key)

  /**
   ** load the page with the help
   **
   ** @param	page	the page to be loaded
   **
   ** @return	-
   **
   ** @version	0.5.2
   **
   ** @author	Diether Knof
   **
   ** @todo	all
   **
   **/
  void
    UI_GTKMM::help_load(string const& page)
    {
      return ;
    } // void UI_GTKMM::help_load(string const& page)

  /**
   ** 'player' has chatted 'message'
   **
   ** @param	player	name of the player who has chatted
   ** @param	message	message
   **
   ** @return	-
   **
   ** @version	0.6.8
   **
   ** @author	Diether Knof
   **
   **/
  void
  UI_GTKMM::chat(string const& player, string const& message)
    {
      this->chatter->chat(player, message);

      this->thrower.throw_it();

      return ;
    } // void UI_GTKMM::chat(string player, string message)

  /**
   ** shown the information
   **
   ** @param	message		message to display
   **
   ** @return	-
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   ** @todo	remove memory leak
   ** @todo	add a parameter for a title
   **
   **/
  void
    UI_GTKMM::information(Translator::Translation const& message)
    {
      // memory leak: the dialog is never deleted
#if GTKMM_MINOR_VERSION < 4
      Gtk::MessageDialog* information
	= new Gtk::MessageDialog(Glib::locale_to_utf8(message.translation()),
				 Gtk::MESSAGE_INFO,
				 Gtk::BUTTONS_NONE,
				 false, true);
#else
      Gtk::MessageDialog* information
	= new Gtk::MessageDialog(Glib::locale_to_utf8(message.translation()),
				 false,
				 Gtk::MESSAGE_INFO,
				 Gtk::BUTTONS_NONE,
				 false);
#endif
      {
#include "../icon.xpm"
	information->set_icon(Gdk::Pixbuf::create_from_xpm_data(icon_xpm));
      }

      // memory leak: no call of 'ui->remove_from_translation(*close_button)'
      Gtk::Button* close_button
	= Gtk::manage(new Gtk::StockButton(Gtk::Stock::CLOSE, "close"));
      this->translations->add(*close_button, ::translator("close"));
      information->add_action_widget(*close_button, Gtk::RESPONSE_CLOSE);
      close_button->show_all();

      close_button->signal_clicked().connect(SigC::slot(*information,
							&Gtk::Widget::hide)
					    );

      information->present();

      return ;
    } // void UI_GTKMM::information(Translator::Translation const& message)

  /**
   **
   ** shown an error window
   ** and quit the program
   **
   ** @param	message		message to display
   **
   ** @return	-
   **
   ** @version	0.6.3
   **
   ** @author	Diether Knof
   **
   **/
  void
    UI_GTKMM::error(string const& message)
    {
      static bool already_running = false;

      if (already_running) {
	cerr << '\n'
	  << ::translator("Press <Return> to exit.").translation() << endl;
	cin.get();
      } else { // if !(already_running)

	already_running = true;

	if (this->splash_screen)
	  this->splash_screen->hide();

	Error error(message, this);
#ifdef RELEASE
	error.run();
#else
	error.present();
	while (error.is_visible())
	  this->update();
#endif
      } // if !(already_running)

#ifdef ASSERTION_GENERATES_SEGFAULT
      {
	cerr << ::translator("Creating segmentation fault.").translation() << endl;
	int const n = *(static_cast<int*>(NULL));
	(void)(n);
	cout << n << endl;
      }
#endif // #ifdef ASSERTION_GENERATES_SEGFAULT

      ::exit(EXIT_FAILURE);

      return ;
    } // void UI_GTKMM::error(string const& message)

  /**
   **
   ** generate an error (for testings)
   **
   ** @param	error_message	error message
   **
   ** @return	-
   **
   ** @version	0.6.3
   **
   ** @author	Diether Knof
   **
   **/
  void
    UI_GTKMM::generate_error(string const error_message)
    {
      ::generate_error(error_message);

      this->thrower.throw_it();

      return ;
    } // void UI_GTKMM::generate_error(string const error_message)

} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
