/**********************************************************************
 *
 *   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 "cards.h"

#include "ui.h"
#include "utils.h"

#include "../../misc/setting.h"
#include "../../misc/translator.h"
#include "../../utils/file.h"

#include <gtkmm/image.h>
namespace UI_GTKMM_NS {

  /**
   **
   ** Constructor
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  Cards::Cards(Base* const parent) :
    Base(parent),
  SigC::Object(),
  directory_(),
  scaling_(1),
  back_(),
  card_(),
  managed_image_()
  {
    this->load_all();

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

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

  /**
   **
   ** update the scaling
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   **/
  void
    Cards::update_scaling()
    {
      if (::setting(Setting::ORIGINAL_CARDS_SIZE))
	this->scaling_ = 1;
      else
	this->scaling_ = (static_cast<double>(::setting(Setting::CARDS_HEIGHT))
			  / this->height_original());

      // make that the width cannot get '0'
      if (static_cast<unsigned>(this->scaling_ * this->width_original())
	  < ::setting.min(Setting::CARDS_HEIGHT))
	this->scaling_
	  = (static_cast<double>(::setting.min(Setting::CARDS_HEIGHT))
	     / this->width_original());


      // set the scaling of the 'ScaledPixbufRotations'
      this->back_.set_scaling(this->scaling());
      for (vector<vector<Gdk::ScaledPixbufRotations> >::iterator c
	   = this->card_.begin();
	   c != this->card_.end();
	   ++c)
	for (vector<Gdk::ScaledPixbufRotations>::iterator v
	     = c->begin();
	     v != c->end();
	     v++)
	  v->set_scaling(this->scaling_);

      return ;
    } // void Cards::update_scaling()

  /**
   **
   ** -> result
   **
   ** @param	rotation	the rotation of the card
   **
   ** @return	the original width of the cards
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  unsigned
    Cards::width_original(Rotation const rotation) const
    {
      DEBUG_ASSERTION(this->back_,
		      "Cards::width_original(rotation):\n"
		      "  'this->back_' is empty");

      return this->back_.get_orig_width(rotation);
    } // unsigned Cards::width_original(Rotation const rotation = UP) const

  /**
   **
   ** -> result
   **
   ** @param	rotation	the rotation of the card
   **
   ** @return	the original height of the cards
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  unsigned
    Cards::height_original(Rotation const rotation) const
    {
      DEBUG_ASSERTION(this->back_,
		      "Cards::height_original(rotation):\n"
		      "  'this->back_' is empty");

      return this->back_.get_orig_height(rotation);
    } // unsigned Cards::height_original(Rotation const rotation = UP) const


  /**
   **
   ** -> result
   **
   ** @param	rotation	the rotation of the card
   **
   ** @return	the width of the cards
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  unsigned
    Cards::width(Rotation const rotation) const
    {
      return this->back_.get_width(rotation);
    } // unsigned Cards::width(Rotation const rotation = UP) const

  /**
   **
   ** -> result
   **
   ** @param	rotation	the rotation of the card
   **
   ** @return	the height of the cards
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  unsigned
    Cards::height(Rotation const rotation) const
    {
      return this->back_.get_height(rotation);
    } // unsigned Cards::height(Rotation const rotation = UP) const

  /**
   **
   ** -> result
   **
   ** @param	rotation	the rotation
   **
   ** @return	the back of the cards (with rotation 'rotation')
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   **/
  Gdk::ScaledPixbuf&
    Cards::back(Rotation const rotation)
    {
      return this->back_[rotation];
    } // Gdk::ScaledPixbuf& Cards::back(Rotation const rotation = UP)

  /**
   **
   ** -> result
   **
   ** @param	card		the card
   ** @param	rotation	the rotation
   **
   ** @return	the pixbuf of 'card' (with rotation 'rotation')
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   **/
  Gdk::ScaledPixbuf&
    Cards::card(Card const card, Rotation const rotation)
    {
      if (card == Card())
	return this->back(rotation);

      // a pointer to the vector with all cards with 'card.color'
      vector<Gdk::ScaledPixbufRotations>* card_color;
      switch (card.color()) {
      case Card::CLUB:
	card_color = &(this->card_[0]);
	break;
      case Card::SPADE:
	card_color = &(this->card_[1]);
	break;
      case Card::HEART:
	card_color = &(this->card_[2]);
	break;
      case Card::DIAMOND:
	card_color = &(this->card_[3]);
	break;
      default:
	DEBUG_ASSERTION(false,
			"Cards::card(card, rotation):\n"
			"  card.color '" << card.color() << "' not valid.");
	break;
      } // switch(card.color())

      // a pointer to the vector with all cards with 'card'
      Gdk::ScaledPixbufRotations* card_color_value;
      switch (card.value()) {
      case Card::ACE:
	card_color_value = &((*card_color)[0]);
	break;
      case Card::TEN:
	card_color_value = &((*card_color)[1]);
	break;
      case Card::KING:
	card_color_value = &((*card_color)[2]);
	break;
      case Card::QUEEN:
	card_color_value = &((*card_color)[3]);
	break;
      case Card::JACK:
	card_color_value = &((*card_color)[4]);
	break;
      case Card::NINE:
	card_color_value = &((*card_color)[5]);
	break;
      default:
	DEBUG_ASSERTION(false,
			"Cards::card(card, rotation):\n"
			"  card.value '" << card.value() << "' not valid.");
	break;
      } // switch(card.color())

      return (*card_color_value)[rotation];
    } // Gdk::ScaledPixbuf& Cards::card(Card const card, Rotation const rotation = UP)

  /**
   **
   ** -> result
   **
   ** @param	rotation	the rotation
   **
   ** @return	the back of the cards (with rotation 'rotation') (scaled)
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   **/
  Glib::RefPtr<Gdk::Pixbuf> const&
    Cards::back_original(Rotation const rotation)
    {
      return this->back().orig_pixbuf();
    } // Glib::RefPtr<Gdk::Pixbuf> const& Cards::back_original(Rotation const rotation = UP)

  /**
   **
   ** -> result
   **
   ** @param	card		the card
   ** @param	rotation	the rotation
   **
   ** @return	the pixbuf of 'card' (with rotation 'rotation') (original)
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   **/
  Glib::RefPtr<Gdk::Pixbuf> const&
    Cards::card_original(Card const card, Rotation const rotation)
    {
      DEBUG_ASSERTION(this->card(card, rotation),
		      "Cards::card_original(card, rotation):\n"
		      "  'card' is null");
      DEBUG_ASSERTION(this->card(card, rotation).orig_pixbuf(),
		      "Cards::card_original(card, rotation):\n"
		      "  'card.orig_pixbuf()' is null");

      return this->card(card, rotation).orig_pixbuf();
    } // Glib::RefPtr<Gdk::Pixbuf> const& Cards::card_original(Card const card, Rotation const rotation = UP)

  /**
   **
   ** -> result
   **
   ** @param	card	card
   **			(default: Card())
   **
   ** @return	new image with the card 'card' that is managed
   **		(that is size and theme)
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  Gtk::Image*
    Cards::new_managed_image(Card const card)
    {
      Gtk::Image* image = Gtk::manage(new Gtk::Image(this->card(card)));
      image->set_data("card", new Card(card));

      image->signal_unrealize().connect(SigC::bind<Gtk::Image const* const>(SigC::slot(*this, &Cards::remove_managed), image));

      this->managed_image_.push_back(image);

      return image;
    } // Gtk::Image* Cards::new_managed_image(Card const card = Card())

  /**
   **
   ** changes the type of 'image'
   **
   ** @param	image	image to change the type of
   ** @param	card	new card
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Cards::change_managed(Gtk::Image* const image, Card const card)
    {
      DEBUG_ASSERTION(image->get_data("card"),
		      "Cards::change_managed:\n"
		      "  image has no data 'type'");
      // ToDo: Test whether the image is in the vector

      delete static_cast<Card*>(image->steal_data("card"));
      image->set_data("card", new Card(card));

      image->set(this->card(card));

      return ;
    } // void Cards::change_managed(Gtk::Image* const image, Card const card)

  /**
   **
   ** remove 'image' from the managed image list
   **
   ** @param	image	image to remove
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Cards::remove_managed(Gtk::Image const* const image)
    {
      for (vector<Gtk::Image*>::iterator i = this->managed_image_.begin();
	   i != this->managed_image_.end();
	  )
	if (*i == image) {
	  delete static_cast<Card*>((*i)->steal_data("card"));
	  i = this->managed_image_.erase(i);
	  break;
	} else {
	  ++i;
	}

	return ;
    } // void Cards::remove_managed(Gtk::Image const* const image)

  /**
   **
   ** update the managed images
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Cards::update_managed()
    {
      for (vector<Gtk::Image*>::iterator i = this->managed_image_.begin();
	   i != this->managed_image_.end();
	   ++i) {
	Card const card = *(static_cast<Card*>((*i)->get_data("card")));
	(*i)->set(this->card(card));
      }

      return ;
    } // void Cards::update_managed()

  /**
   **
   ** load the cards and the back
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    Cards::load_all()
    {
      if (this->search_directory() == false) {
	if (this->directory().empty())
	  // ToDo: translate
	  this->ui->error("Error at loading the cardset '"
			  + ::setting(Setting::CARDSET)
			  + "' of the cards.\n"
			  + "Perhaps you should validate your 'FreeDokorc'-File "
			  + "(in the directory '"
			  + ::setting(Setting::PRIVATE_DATA_DIRECTORY)
			  + "').");
	else
	  // ToDo: translate
	  this->ui->error("Error at loading the cardset '"
			  + ::setting(Setting::CARDSET)
			  + "' of the cards.\n"
			  "Keeping the old.");

	return ;
      } // if (this->search_directory() == false)

      this->load_back();
      this->load_cards();

      return ;
    } // void Cards::load_all()

  /**
   **
   ** load the cards back
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    Cards::load_back()
    {
      vector<string> file;

      file.push_back(this->directory() + "/"
		     + ::setting.value(Setting::CARDS_BACK_DIRECTORY) + "/"
		     + ::setting.value(Setting::CARDS_BACK));
      file.push_back(this->directory() + "/"
		     + ::setting.value(Setting::CARDS_BACK_DIRECTORY) + "/"
		     + ::setting_default.value(Setting::CARDS_BACK));

      Gdk::ScaledPixbufRotations back_new;
      for (vector<string>::const_iterator f = file.begin();
	   f != file.end();
	   f++) {
	if (Glib::file_test(*f + "." + ::setting(Setting::GRAPHIC_EXTENSION),
			    Glib::FILE_TEST_IS_REGULAR)) {
	  back_new = Gdk::ScaledPixbufRotations(*f + "." + ::setting(Setting::GRAPHIC_EXTENSION));
	  if (back_new)
	    break;
	} // if (Glib::file_test(*file, Glib::FILE_TEST_IS_REGULAR))
      } // for (f \in file)

      if (back_new) {
	this->back_ = back_new;
	this->back_.set_scaling(this->scaling());
      }

      if (!this->back_) {
	{ // search for any back
	  string const path = (this->directory() + "/"
			       + ::setting.value(Setting::CARDS_BACK_DIRECTORY));

	  Glib::Dir dir(path);
	  string filename;

	  while ((filename = dir.read_name()) != "") {
	    if ( (DK::Utils::File::extension(filename)
		  == ::setting(Setting::GRAPHIC_EXTENSION))
		 && Glib::file_test(path + "/" + filename,
				    Glib::FILE_TEST_IS_REGULAR)) {
	      back_new = Gdk::ScaledPixbufRotations(path + "/" + filename);
	      if (back_new) {
		break;
	      }
	    } // if (filename valid)
	  } // while (filename \in dir)
	  if (back_new) {
	    this->back_ = back_new;
	    this->back_.set_scaling(this->scaling());
	  }

	} // search for any back

	if (!this->back_) {
	  // ToDo: translate
	  this->ui->error("Error at loading the back '"
			  + ::setting(Setting::CARDS_BACK)
			  + "' of the cards.\n"
			  + "Perhaps you should validate your 'FreeDokorc'-File "
			  + "(in the directory '"
			  + ::setting(Setting::PRIVATE_DATA_DIRECTORY)
			  + "').");
	}
      } // if (!this->back_)

      if (::setting(Setting::ORIGINAL_CARDS_SIZE) == true)
	::setting.set(Setting::CARDS_HEIGHT, this->height_original());

      this->update_scaling();

      return ;
    } // void Cards::load_back()

  /**
   **
   ** load the cards
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    Cards::load_cards()
    {
      if (::game_status != GAMESTATUS::PROGRAMSTART) {
	this->ui->set_max_progress(1);
	this->ui->add_status_message(::translator("loading the cards"));
      } // if (::game_status != GAMESTATUS::PROGRAMSTART)

      // load the cards
      vector<Card::Color> color;
      color.push_back(Card::CLUB);
      color.push_back(Card::SPADE);
      color.push_back(Card::HEART);
      color.push_back(Card::DIAMOND);
      vector<Card::Value> value;
      value.push_back(Card::ACE);
      value.push_back(Card::TEN);
      value.push_back(Card::KING);
      value.push_back(Card::QUEEN);
      value.push_back(Card::JACK);
      value.push_back(Card::NINE);

      vector<vector<Gdk::ScaledPixbufRotations > > card_new;
      for (vector<Card::Color>::const_iterator c = color.begin();
	   c != color.end();
	   c++) {
	card_new.push_back(vector<Gdk::ScaledPixbufRotations>());
	for (vector<Card::Value>::const_iterator v = value.begin();
	     v != value.end();
	     v++) {
	  card_new.back().push_back(Gdk::ScaledPixbufRotations(this->directory() + "/"
							       + ::setting.value(Setting::CARDS_DIRECTORY) + "/"
							       + ::name(*c)
							       + "/"
							       + ::name(*v)
							       + "."
							       + ::setting(Setting::GRAPHIC_EXTENSION)));
	  card_new.back().back().set_scaling(this->scaling());
	  if (!card_new.back().back())
	    // ToDo: translate
	    ::ui->error("Error loading the card '" + ::name(Card(*c, *v)) + "'");
	  this->ui->add_progress(1.0 / (color.size() * value.size()));
	} // for (v \in value)
      } // for (c \in color)
      if (::game_status != GAMESTATUS::PROGRAMSTART) {
	this->ui->finish_progress();
	this->ui->remove_status_message(::translator("loading the cards"));
      } // if (::game_status != GAMESTATUS::PROGRAMSTART)

      this->card_ = card_new;

      return ;
    } // void Cards::load_cards()

  /**
   **
   ** searchs a directory for the cards
   **
   ** @param	-
   **
   ** @return	whether a directory was found
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   ** @todo	search all subdirectories till a cardset is found
   **
   **/
  bool
    Cards::search_directory()
    {
      // search the directory
      vector<string> subdir;
      subdir.push_back(::setting(Setting::CARDSET));
      subdir.push_back(::setting_default(Setting::CARDSET));
      subdir.push_back(::setting(Setting::CARDSETS_DIRECTORY)
		       + "/Altenburg/french");
      subdir.push_back(::setting(Setting::CARDSETS_DIRECTORY)
		       + "/xskat/french");
      subdir.push_back(::setting(Setting::CARDSETS_DIRECTORY)
		       + "/kdecarddecks/spaced");
      subdir.push_back(::setting(Setting::CARDSETS_DIRECTORY)
		       + "/pysol/gpl");
      subdir.push_back(::setting(Setting::CARDSETS_DIRECTORY)
		       + "/pysol/a-m/gpl");

      vector<string> const datadir = ::setting.data_directories();

      for (vector<string>::const_iterator sd = subdir.begin();
	   sd != subdir.end();
	   sd++) {
	for (vector<string>::const_iterator dd = datadir.begin();
	     dd != datadir.end();
	     dd++) {
	  if (Glib::file_test(*dd + "/" + *sd + "/"
			      + ::setting.value(Setting::CARDS_DIRECTORY) + "/"
			      + ::name(Card::CLUB) + "/"
			      + ::name(Card::ACE)
			      + "." + ::setting(Setting::GRAPHIC_EXTENSION),
			      Glib::FILE_TEST_IS_REGULAR)) {
	    this->directory_ = *dd + "/" + *sd;
	    return true;
	  } // if (*d is valid)
	} // for (dd \in datadir)
      } // for (sd \in subdir)

      return false;
    } // bool Cards::search_directory()


} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
