/**********************************************************************
 *
 *   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 "players_db.h"
#include "ui.h"
#include "translations.h"
#include "cards.h"
#include "main_window.h"

#include "widgets/stock_button.h"

#include "../../party/party.h"
#include "../../player/player.h"
#include "../../player/playersDb.h"
#include "../../player/ai/aiDb.h"

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


#include <gtkmm/treeview.h>
#include <gtkmm/alignment.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/stock.h>
#include <gtkmm/scrolledwindow.h>
#include <gdk/gdkkeysyms.h>
namespace UI_GTKMM_NS {

  /**
   **
   ** -> result
   **
   ** @param	statistic	statistic
   **
   ** @return	name of 'statistic'
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.6
   **
   **/
    string
      PlayersDB::name(Statistic const statistic)
    {
      string name;
      switch(statistic) {
      case TOTAL:
	name = "statistic total";
	break;
      case WON:
	name = "statistic won";
	break;
      case LOST:
	name = "statistic lost";
	break;
      case PERCENT_WON:
	name = "statistic percent won";
	break;
      } // switch(statistic)

      return name;
    } // static string PlayersDB::name(Statistic const statistic)

    /**
     **
     ** Constructor for the model
     **
     ** @param	playerno	number of players in the party
     **
     ** @return	-
     ** 
     ** @author	Diether Knof
     **
     ** @version	0.6.6
     **
     **/
    PlayersDB::PlayersDBModel::PlayersDBModel(unsigned const playerno) :
      Gtk::TreeModel::ColumnRecord(),
    type(),
    statistic(playerno)
    {
      this->add(this->type);
      for (unsigned p = 0; p < playerno; p++)
	this->add(this->statistic[p]);

      return ;
    } // PlayersDB::PlayersDBModel::PlayersDBModel(unsigned const playerno)

    /**
     **
     ** Constructor
     **
     ** @param	-
     **
     ** @return	-
     ** 
     ** @author	Diether Knof
     **
     ** @version	0.6.6
     **
     **/
    PlayersDB::PlayersDB(Base* const parent) :
      Base(parent),
    StickyDialog("Players DB", false, true),
    statistic(TOTAL),
    players_db_list(),
    players_db_treeview(NULL),
    ranking(),
    average_game_points(),
    average_game_trick_points(),
    games(),
    games_marriage(),
    games_poverty(),
    games_solo(),
    games_solo_color(),
    games_solo_picture(),
    games_solo_picture_single(),
    games_solo_picture_double(),
    game(0),
    specialpoints(),
    specialpoints_winning(),
    specialpoints_announcement(),
    specialpoint(0),
    heuristics(),
    heuristics_general(),
    heuristics_solo(),
    heuristics_solo_color(),
    heuristics_solo_picture(),
    heuristics_solo_meatless(),
    heuristic(0)
    {
      this->set_default_size(0, 3 * this->ui->cards->height());
      this->signal_realize().connect(SigC::slot(*this, &PlayersDB::init));

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

    /**
     **
     ** Destruktor
     **
     ** @param	-
     **
     ** @return	-
     ** 
     ** @author	Diether Knof
     **
     ** @version	0.6.6
     **
     **/
    PlayersDB::~PlayersDB()
    {
      delete this->players_db_model;

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

    /**
     **
     ** create all subelements
     **
     ** @param	-
     **
     ** @return	-
     ** 
     ** @author	Diether Knof
     **
     ** @version	0.6.6
     **
     **/
    void
      PlayersDB::init()
      {
	this->set_icon(this->ui->main_window->icon);
      this->ui->translations->add(*this, ::translator("FreeDoko %ttitle%",
						      ::translator("Players database")));


	{ // vbox
	  Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox(false, 1 em));
	  this->get_vbox()->add(*hbox);
	  { // statistic type
	    Gtk::Alignment* alignment
	      = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_TOP,
					       0, 0));
	    alignment->set_border_width(1 ex);
	    hbox->add(*alignment);
	    Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(false, 1 ex / 2));
	    alignment->add(*vbox);

	    Gtk::RadioButton::Group statistic_group;
	    for (int statistic = STATISTIC_FIRST;
		 statistic <= STATISTIC_LAST;
		 ++statistic) {
	      Gtk::RadioButton* const radio_button
		= Gtk::manage(new Gtk::RadioButton(statistic_group, ""));
	      this->ui->translations->add(*radio_button,
					    ::translator(PlayersDB::name(static_cast<Statistic const>(statistic))));
	      vbox->add(*radio_button);
	      radio_button->signal_clicked().connect(SigC::bind<Statistic const>(SigC::slot(*this, &PlayersDB::set_statistic), static_cast<Statistic const>(statistic)));
	      if (statistic == PERCENT_WON)
		radio_button->set_active(true);

	    } // for (statistic)
	  } // statistic type

	  { // the treeview

	    // the points table
	    this->players_db_model = new PlayersDBModel(this->ui->party().playerno());

	    this->players_db_list
	      = Gtk::TreeStore::create(*(this->players_db_model));
	    this->players_db_treeview
	      = Gtk::manage(new Gtk::TreeView(this->players_db_list));

	    this->players_db_treeview->append_column("players database data type",
						     this->players_db_model->type);
	    this->players_db_treeview->get_column_cell_renderer(0)->property_xalign() = 1;
	    for (unsigned p = 0; p < this->ui->party().playerno(); p++) {
	      this->players_db_treeview->append_column(Glib::locale_to_utf8(this->ui->party().player(p).name()),
						       this->players_db_model->statistic[p]);
	      this->players_db_treeview->get_column_cell_renderer(p + 1)->property_xalign() = 0.5;
	    } // for (p < this->ui->party().playerno())

	    { // the scrolled window
	      Gtk::ScrolledWindow* scrolled_window
		= Gtk::manage(new Gtk::ScrolledWindow);
	      scrolled_window->set_policy(Gtk::POLICY_NEVER,
					  Gtk::POLICY_AUTOMATIC);
	      scrolled_window->add(*(this->players_db_treeview));

	      hbox->pack_start(*scrolled_window);
	    } // the scrolled window
	  } // the treeview
	} // vbox

	{ // action area
	  { // clear button
	    Gtk::StockButton* clear_button
	      = Gtk::manage(new Gtk::StockButton(Gtk::Stock::CLEAR, "clear database"));
	    this->ui->translations->add(*clear_button,
					  ::translator("clear database"));
	    this->add_action_widget(*clear_button, Gtk::RESPONSE_NONE);
	    clear_button->signal_clicked().connect(SigC::slot(*this, &PlayersDB::clear_db));
	  } // clear button

	  { // close button
	    Gtk::StockButton* close_button
	      = Gtk::manage(new Gtk::StockButton(Gtk::Stock::CLOSE, "close"));
	    this->ui->translations->add(*close_button,
					  ::translator("close"));
	    this->add_action_widget(*close_button, Gtk::RESPONSE_CLOSE);

	    close_button->grab_default();

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

	  } // close button
	} // action area

	this->show_all_children();

	this->signal_show().connect(SigC::slot(*this,
					       &PlayersDB::recreate_db));
	this->recreate_db();

	this->language_update();

	return ;
      } // void PlayersDB::init()

    /**
     **
     ** the party is open
     **
     ** @param		-
     **
     ** @return		-
     **
     ** @version	0.6.6
     **
     ** @author		Diether Knof
     **
     **/
    void
      PlayersDB::party_open()
      {
	this->recreate_db();

	return ;
      } // void PlayersDB::party_open(Party const& party)

    /**
     **
     ** the party is closed
     **
     ** @param	-
     **
     ** @return	-
     **
     ** @version	0.6.6
     **
     ** @author	Diether Knof
     **
     **/
    void
      PlayersDB::party_close()
      {
	this->hide();

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

    /**
     **
     ** the name of 'player' has changed
     **
     ** @param	player	the player with the changed name
     **
     ** @return	-
     **
     ** @version	0.6.6
     **
     ** @author	Diether Knof
     **
     **/
    void
      PlayersDB::name_changed(Player const& player)
      {
	if (!this->is_realized())
	  return ;

	// ToDo: Here 'player.no()' means the number in the party, not in the game
	this->players_db_treeview->get_column(player.no() + 1
					     )->set_title(" " + Glib::locale_to_utf8(player.name()) + " ");

	return ;
      } // void PlayersDB::name_changed(Player const& player)

    /**
     **
     ** recreates the table
     **
     ** @param	-
     **
     ** @return	-
     **
     ** @version	0.6.6
     **
     ** @author	Diether Knof
     **
     **/
    void
      PlayersDB::recreate_db()
      {
	if (!this->is_realized())
	  return ;

	if (!this->players_db_list)
	  return ;


	this->players_db_list->clear();

	{ // ranking
	  this->ranking = *(this->players_db_list->append());
	} // ranking

	{ // average game points
	  this->average_game_points = *(this->players_db_list->append());
	} // average game points

	{ // average game trick points
	  this->average_game_trick_points = *(this->players_db_list->append());
	} // average game trick points

	{ // game type
	  this->games = *(this->players_db_list->append());
	  this->games_marriage
	    = *(this->players_db_list->append(this->games.children()));
	  this->games_poverty
	    = *(this->players_db_list->append(this->games.children()));
	  this->games_solo
	    = *(this->players_db_list->append(this->games.children()));
	  this->games_solo_color
	    = *(this->players_db_list->append(this->games_solo.children()));
	  this->games_solo_picture
	    = *(this->players_db_list->append(this->games_solo.children()));
	  this->games_solo_picture_single
	    = *(this->players_db_list->append(this->games_solo_picture.children()));
	  this->games_solo_picture_double
	    = *(this->players_db_list->append(this->games_solo_picture.children()));

	  this->game.clear();
	  for (int gt = GAMETYPE::FIRST; gt <= GAMETYPE::LAST; ++gt) {
	    switch (static_cast<GameType>(gt)) {
	    case GAMETYPE::NORMAL:
	      this->game.push_back(*(this->players_db_list->prepend(this->games.children())));
	      break;
	    case GAMETYPE::FIVE_NINES:
	    case GAMETYPE::SIX_KINGS:
	    case GAMETYPE::FOX_HIGHEST_TRUMP:
	    case GAMETYPE::POVERTY:
	      this->game.push_back(*(this->players_db_list->append(this->games_poverty.children())));
	      break;
	    case GAMETYPE::GENSCHER:
	    case GAMETYPE::MARRIAGE:
	    case GAMETYPE::MARRIAGE_SOLO:
	    case GAMETYPE::MARRIAGE_SILENT:
	      this->game.push_back(*(this->players_db_list->append(this->games_marriage.children())));
	      break;
	    case GAMETYPE::SOLO_JACK:
	    case GAMETYPE::SOLO_QUEEN:
	      this->game.push_back(*(this->players_db_list->append(this->games_solo_picture_single.children())));
	      break;
	    case GAMETYPE::SOLO_KING:
	    case GAMETYPE::SOLO_QUEEN_JACK:
	    case GAMETYPE::SOLO_KING_JACK:
	    case GAMETYPE::SOLO_KING_QUEEN:
	      this->game.push_back(*(this->players_db_list->append(this->games_solo_picture_double.children())));
	      break;
	    case GAMETYPE::SOLO_KOEHLER:
	      this->game.push_back(*(this->players_db_list->append(this->games_solo_picture.children())));
	      break;
	    case GAMETYPE::SOLO_CLUB:
	    case GAMETYPE::SOLO_HEART:
	    case GAMETYPE::SOLO_SPADE:
	    case GAMETYPE::SOLO_DIAMOND:
	      this->game.push_back(*(this->players_db_list->append(this->games_solo_color.children())));
	      break;
	    case GAMETYPE::SOLO_MEATLESS:
	      this->game.push_back(*(this->players_db_list->append(this->games_solo.children())));
	      break;
	    } // switch(gt)
	  } // for (gt \in GameType)
	} // game type

	{ // special points
	  this->specialpoints = *(this->players_db_list->append());
	  this->specialpoints_winning
	    = *(this->players_db_list->append(this->specialpoints.children()));
	  this->specialpoints_announcement
	    = *(this->players_db_list->append(this->specialpoints.children()));

	  this->specialpoint.clear();
	  for (int sp = SPECIALPOINT::FIRST; sp <= SPECIALPOINT::LAST; ++sp) {
	    switch (static_cast<Specialpoint>(sp)) {
	    case SPECIALPOINT::NOSPECIALPOINT:
	    case SPECIALPOINT::CAUGHT_FOX:
	    case SPECIALPOINT::FOX_LAST_TRICK:
	    case SPECIALPOINT::CHARLIE:
	    case SPECIALPOINT::CAUGHT_CHARLIE:
	    case SPECIALPOINT::DOLLE_CAUGHT_DOLLE:
	    case SPECIALPOINT::HEART_TRICK:
	    case SPECIALPOINT::DOPPELKOPF:
	      this->specialpoint.push_back(*(this->players_db_list->prepend(this->specialpoints.children())));
	      break;
	    case SPECIALPOINT::NO120:
	    case SPECIALPOINT::NO90:
	    case SPECIALPOINT::NO60:
	    case SPECIALPOINT::NO30:
	    case SPECIALPOINT::NO0:
	    case SPECIALPOINT::NO90_WON:
	    case SPECIALPOINT::NO60_WON:
	    case SPECIALPOINT::NO30_WON:
	    case SPECIALPOINT::NO0_WON:
	    case SPECIALPOINT::CONTRA_WON:
	    case SPECIALPOINT::SOLO:
	      this->specialpoint.push_back(*(this->players_db_list->append(this->specialpoints_winning.children())));
	      break;
	    case SPECIALPOINT::NO120_SAID:
	    case SPECIALPOINT::NO90_SAID:
	    case SPECIALPOINT::NO60_SAID:
	    case SPECIALPOINT::NO30_SAID:
	    case SPECIALPOINT::NO0_SAID:
	    case SPECIALPOINT::NO120_REPLY:
	    case SPECIALPOINT::NO90_REPLY:
	    case SPECIALPOINT::NO60_REPLY:
	    case SPECIALPOINT::NO30_REPLY:
	    case SPECIALPOINT::NO0_REPLY:
	      this->specialpoint.push_back(*(this->players_db_list->append(this->specialpoints_announcement.children())));
	      break;
	    } // switch(sp)
	  } // for (sp \in Specialpoint)
	} // special points

	{ // heuristics
	  this->heuristics = *(this->players_db_list->append());
	  this->heuristics_general
	    = *(this->players_db_list->append(this->heuristics.children()));
	  this->heuristics_solo
	    = *(this->players_db_list->append(this->heuristics.children()));
	  this->heuristics_solo_color
	    = *(this->players_db_list->append(this->heuristics_solo.children()));
	  this->heuristics_solo_picture
	    = *(this->players_db_list->append(this->heuristics_solo.children()));
	  this->heuristics_solo_meatless
	    = *(this->players_db_list->append(this->heuristics_solo.children()));

	  this->heuristic.clear();
	  for (int h = Aiconfig::HEURISTIC_FIRST;
	       h <= Aiconfig::HEURISTIC_LAST;
	       ++h) {
	    switch (static_cast<Aiconfig::Heuristic>(h)) {
	    case Aiconfig::NO_HEURISTIC:
	    case Aiconfig::BUG_REPORT:
	      this->heuristic.push_back(this->heuristics);
	      break;
	    case Aiconfig::ONLY_VALID_CARD:
	      this->heuristic.push_back(*(this->players_db_list->prepend(this->heuristics.children())));
	      break;
	    case Aiconfig::CHOOSE_BEST_CARD:
	      this->heuristic.push_back(*(this->players_db_list->prepend(this->heuristics.children())));
	      std::swap(this->heuristic[Aiconfig::ONLY_VALID_CARD - Aiconfig::HEURISTIC_FIRST],
			this->heuristic.back());
	      break;
	    case Aiconfig::CHOOSE_ACE:
	    case Aiconfig::CHOOSE_FOR_COLOR_TRICK:
	    case Aiconfig::SERVE_COLOR_TRICK:
	    case Aiconfig::SERVE_TRUMP_TRICK:
	    case Aiconfig::CHOOSE_PFUND:
	    case Aiconfig::CHOOSE_PFUND_POVERTY:
	    case Aiconfig::JAB_FOR_ACE:
	    case Aiconfig::CREATE_FEHL:
	    case Aiconfig::BEST_WINNING:
	    case Aiconfig::LOW_HIGH:
	    case Aiconfig::PLAY_FOR_TEAM:
	    case Aiconfig::JAB_FOX:
	    case Aiconfig::DRAW_TRUMP:
	      this->heuristic.push_back(*(this->players_db_list->append(this->heuristics_general.children())));
	      break;
	    case Aiconfig::COLOR_CHOOSE_ACE:
	    case Aiconfig::COLOR_BEST_WINNING:
	    case Aiconfig::COLOR_JAB_FOR_ACE:
	    case Aiconfig::COLOR_LOW_HIGH:
	      this->heuristic.push_back(*(this->players_db_list->append(this->heuristics_solo_color.children())));
	      break;
	    case Aiconfig::PICTURE_SECONDBESTTRUMP:
	      this->heuristic.push_back(*(this->players_db_list->append(this->heuristics_solo_picture.children())));
	      break;
	    case Aiconfig::MEATLESS_PLAYHIGHESTCOLOR:
	      this->heuristic.push_back(*(this->players_db_list->append(this->heuristics_solo_meatless.children())));
	      break;
	    } // switch(h)
	  } // for (h \in Aiconfig::Heuristic)
	} // heuristics

	this->update_db();
	this->language_update();

	this->players_db_treeview->show_all();

	return ;
      } // void PlayersDB::recreate_db()

    /**
     **
     ** updates the statistics
     **
     ** @param	-
     **
     ** @return	-
     **
     ** @version	0.6.6
     **
     ** @author	Diether Knof
     **
     **/
    void
      PlayersDB::update_db()
      {
	if (!this->is_realized())
	  return ;

	if (!this->players_db_list)
	  return ;

	Party const& party = this->ui->party();

	{ // ranking
	  for (unsigned p = 0; p < party.playerno(); ++p)
	    this->ranking[this->players_db_model->statistic[p]]
	      = DK::Utils::String::to_string(static_cast<int>(party.player(p).db().rank.value() * 10) / 10.0);
	} // ranking

	{ // average game points
	  for (unsigned p = 0; p < party.playerno(); ++p)
	    this->average_game_points[this->players_db_model->statistic[p]]
	      = DK::Utils::String::to_string(static_cast<int>(party.player(p).db().averageGamePoints() * 10) / 10.0);
	} // average game points

	{ // average game trick points
	  for (unsigned p = 0; p < party.playerno(); ++p)
	    this->average_game_trick_points[this->players_db_model->statistic[p]]
	      = DK::Utils::String::to_string(static_cast<int>(party.player(p).db().averageGameTrickPoints() * 10) / 10.0);
	} // average game trick points

	{ // game type
	  for (unsigned p = 0; p < party.playerno(); ++p) {
	    this->games[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_all());
	    this->games_marriage[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_group_marriage());
	    this->games_poverty[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_group_poverty());
	    this->games_solo[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_group_solo());
	    this->games_solo_color[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_group_solo_color());
	    this->games_solo_picture[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_group_solo_picture());
	    this->games_solo_picture_single[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_group_solo_picture_single());
	    this->games_solo_picture_double[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().games_group_solo_picture_double());
	  } // for (p < party.playerno())
	  for (int gt = GAMETYPE::FIRST; gt <= GAMETYPE::LAST; ++gt) {
	    for (unsigned p = 0; p < party.playerno(); ++p) {
	      this->game[gt][this->players_db_model->statistic[p]]
		= this->statistic_data(party.player(p).db().games(static_cast<GameType const>(gt)));
	    } // for (p < party.playerno())
	  } // for (gt \in GAMETYPE)
	} // game type

	{ // special points
	  for (unsigned p = 0; p < party.playerno(); ++p) {
	    this->specialpoints[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().specialpoints_all());
	    this->specialpoints_winning[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().specialpoints_group_winning());
	    this->specialpoints_announcement[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().specialpoints_group_announcement());
	  } // for (p < party.playerno())
	  for (int sp = SPECIALPOINT::FIRST; sp <= SPECIALPOINT::LAST; ++sp) {
	    for (unsigned p = 0; p < party.playerno(); ++p) {
	      this->specialpoint[sp][this->players_db_model->statistic[p]]
		= this->statistic_data(party.player(p).db().specialpoints(static_cast<Specialpoint const>(sp)));
	    } // for (p < party.playerno())
	  } // for (sp \in Specialpoint)
	} // special points
	{ // heuristics
	  for (unsigned p = 0; p < party.playerno(); ++p) {
	    this->heuristics_solo[this->players_db_model->statistic[p]]
	      = this->statistic_data(party.player(p).db().specialpoints_all());
	      int const total
		= static_cast<AiDb const&>(party.player(p).db()).heuristic(Aiconfig::NO_HEURISTIC);

	      this->heuristics_general[this->players_db_model->statistic[p]]
		= this->heuristic_statistic_data(total, static_cast<AiDb const&>(party.player(p).db()).heuristics_group_general());
	      this->heuristics_solo[this->players_db_model->statistic[p]]
		= this->heuristic_statistic_data(total, static_cast<AiDb const&>(party.player(p).db()).heuristics_group_solo());
	      this->heuristics_solo_color[this->players_db_model->statistic[p]]
		= this->heuristic_statistic_data(total, static_cast<AiDb const&>(party.player(p).db()).heuristics_group_solo_color());
	      this->heuristics_solo_picture[this->players_db_model->statistic[p]]
		= this->heuristic_statistic_data(total, static_cast<AiDb const&>(party.player(p).db()).heuristics_group_solo_picture());
	      this->heuristics_solo_meatless[this->players_db_model->statistic[p]]
		= this->heuristic_statistic_data(total, static_cast<AiDb const&>(party.player(p).db()).heuristics_group_solo_meatless());
	  } // for (p < party.playerno())
	  for (int h = Aiconfig::HEURISTIC_FIRST;
	       h <= Aiconfig::HEURISTIC_LAST;
	       ++h) {
	    for (unsigned p = 0; p < party.playerno(); ++p) {
	      int const total
		= static_cast<AiDb const&>(party.player(p).db()).heuristic(Aiconfig::NO_HEURISTIC);
	      int const num
		= static_cast<AiDb const&>(party.player(p).db()).heuristic(static_cast<Aiconfig::Heuristic const>(h));

	      this->heuristic[h][this->players_db_model->statistic[p]]
		= this->heuristic_statistic_data(total, num);
	    } // for (p < party.playerno())
	  } // for (h \in Heuristic)
	} // heuristics

	return ;
      } // void PlayersDB::update_db()

    /**
     **
     ** updates all language elements
     **
     ** @param	-
     **
     ** @return	-
     **
     ** @version	0.6.6
     **
     ** @author	Diether Knof
     **
     **/
    void
      PlayersDB::language_update()
      {
	if (!this->is_realized())
	  return ;

	this->players_db_treeview->get_column(0)->set_title(Glib::locale_to_utf8(::translator("players database data type").translation()));

	{ // ranking
	  this->ranking[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("ranking"
					       ).translation());
	} // ranking

	{ // average game points
	  this->average_game_points[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("average game points"
					       ).translation());
	} // average game points

	{ // average game trick points
	  this->average_game_trick_points[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("average game trick points"
					       ).translation());
	} // average game trick points

	{ // game type
	  this->games[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games").translation());
	  this->games_marriage[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games marriage"
					       ).translation());
	  this->games_poverty[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games poverty"
					       ).translation());
	  this->games_solo[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games solo"
					       ).translation());
	  this->games_solo_color[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games solo color"
					       ).translation());
	  this->games_solo_picture[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games solo picture"
					       ).translation());
	  this->games_solo_picture_single[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games solo picture single"
					       ).translation());
	  this->games_solo_picture_double[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("games solo picture double"
					       ).translation());
	  for (int gt = GAMETYPE::FIRST; gt <= GAMETYPE::LAST; ++gt) {
	    this->game[gt][this->players_db_model->type]
	      = Glib::locale_to_utf8(::translator(::name(static_cast<GameType const>(gt))).translation());
	  } // for (gt \in GAMETYPE)
	} // game type
	{ // special points
	  this->specialpoints[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("specialpoints").translation());
	  this->specialpoints_winning[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("specialpoints winning"
					       ).translation());
	  this->specialpoints_announcement[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("specialpoints announcements"
					       ).translation());
	  for (int sp = SPECIALPOINT::FIRST; sp <= SPECIALPOINT::LAST; ++sp) {
	    this->specialpoint[sp][this->players_db_model->type]
	      = Glib::locale_to_utf8(::translator(::name(static_cast<Specialpoint const>(sp))).translation());
	  } // for (sp \in Specialpoint)
	} // special points
	{ // heuristics
	  this->heuristics[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("heuristics").translation());
	  this->heuristics_general[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("heuristics general"
					       ).translation());
	  this->heuristics_solo[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("heuristics solo"
					       ).translation());
	  this->heuristics_solo_color[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("heuristics solo color"
					       ).translation());
	  this->heuristics_solo_picture[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("heuristics solo picture"
					       ).translation());
	  this->heuristics_solo_meatless[this->players_db_model->type]
	    = Glib::locale_to_utf8(::translator("heuristics solo meatless"
					       ).translation());
	  for (int h = Aiconfig::HEURISTIC_FIRST;
	       h <= Aiconfig::HEURISTIC_LAST;
	       ++h) {
	    if ((h != Aiconfig::NO_HEURISTIC)
	    && (h != Aiconfig::BUG_REPORT))
	      this->heuristic[h][this->players_db_model->type]
		= Glib::locale_to_utf8(::translator(::name(static_cast<Aiconfig::Heuristic const>(h))).translation());
	  } // for (h \in Heuristic)
	} // heuristics

	return ;
      } // void PlayersDB::language_update()

    /**
     **
     ** sets the statistic
     **
     ** @param	statistic	new statistic
     **
     ** @return	-
     **
     ** @version	0.6.6
     **
     ** @author	Diether Knof
     **
     **/
    void
      PlayersDB::set_statistic(Statistic const statistic)
      {
	this->statistic = statistic;
	this->update_db();

	return ;
      } // void PlayersDB::set_statistic(Statistic const statistic)

    /**
     **
     ** clears the database
     **
     ** @param		-
     **
     ** @return		-
     **
     ** @version	0.6.7
     **
     ** @author		Diether Knof
     **
     ** @todo		remove 'const cast'
     **
     **/
    void
      PlayersDB::clear_db()
      {
	for (unsigned p = 0; p < this->ui->party().playerno(); p++)
	  const_cast<Player&>(this->ui->party().player(p)).db().clear();

	this->update_db();

	return ;
      } // void PlayersDB::clear_db()

    /**
     **
     ** -> result
     **
     ** @param		tlwcount	counter
     **
     ** @return		string of the data analysed by 'this->statistic'
     **
     ** @version	0.6.6
     **
     ** @author		Diether Knof
     **
     **/
    Glib::ustring
      PlayersDB::statistic_data(TLWcount const& tlwcount) const
      {
	Glib::ustring value;

	switch(this->statistic) {
	case TOTAL:
	  value = DK::Utils::String::to_string(tlwcount.total());
	  break;
	case WON:
	  if (tlwcount.total() > 0)
	    value = DK::Utils::String::to_string(tlwcount.won());
	  else
	    value = "-";
	  break;
	case LOST:
	  if (tlwcount.total() > 0)
	    value = DK::Utils::String::to_string(tlwcount.lost());
	  else
	    value = "-";
	  break;
	case PERCENT_WON:
	  if (tlwcount.total() > 0)
	    value = (DK::Utils::String::to_string(tlwcount.won() * 100
						  / tlwcount.total())
		     + "%");
	  else
	    value = "-";
	  break;
	} // switch(this->statistic)

	return value;
      } // void PlayersDB::statistic_data(TLWcout const& tlwcount) const

    /**
     **
     ** -> result
     **
     ** @param		total	total number of heuristics
     ** @param		num	number of the active heuristics
     **
     ** @return		string of the data analysed by 'this->statistic'
     **
     ** @version	0.6.6
     **
     ** @author		Diether Knof
     **
     **/
    Glib::ustring
      PlayersDB::heuristic_statistic_data(unsigned const total,
					  unsigned const num) const
      {
	Glib::ustring value;

	switch(this->statistic) {
	case TOTAL:
	  if (num > 0)
	    value = DK::Utils::String::to_string(num);
	  else
	    value = "-";
	  break;
	case WON:
	  if (num > 0)
	    value = DK::Utils::String::to_string(num);
	  else
	    value = "-";
	  break;
	case LOST:
	  if (num > 0)
	    value = DK::Utils::String::to_string(num);
	  else
	    value = "-";
	  break;
	case PERCENT_WON:
	  if (num > 0)
	    value = (DK::Utils::String::to_string((num * 100 / total))
		     + "."
		     + DK::Utils::String::to_string((num * 1000 / total) % 10)
		     + "%");
	  else
	    value = "-";
	  break;
	} // switch(this->statistic)

	return value;
      } // void PlayersDB::heuristic_statistic_data(unsigned const total, unsigned const num) const

    /**
     **
     ** a key has been pressed
     ** C-o: output of the statistics on 'stdout'
     **
     ** @param	key	the key
     **
     ** @return	whether the key was managed
     **
     ** @version	0.6.1
     **
     ** @author	Diether Knof
     **
     **/
    bool
      PlayersDB::on_key_press_event(GdkEventKey* key)
      {
	bool managed = false;

	if ((key->state & ~GDK_SHIFT_MASK) == GDK_CONTROL_MASK) {
	  switch (key->keyval) {
	  case GDK_o: // ouput of the preferences
	    for (unsigned p = 0; p < this->ui->party().playerno(); ++p)
	      cout << this->ui->party().player(p).name() << '\n'
		<< "{\n"
		<< this->ui->party().player(p).db()
		<< "}\n";
	    managed = true;
	    break;
	  } // switch (key->keyval)
	} // if (key->state == GDK_CONTROL_MASK)

      return (managed
	      || this->StickyDialog::on_key_press_event(key)
	      || this->ui->key_press(key));
      } // bool PlayersDB::on_key_press_event(GdkEventKey* key)

} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
