/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2006  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 "card_counter.h"


/**
 ** standard constructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardCounter::CardCounter() :
  map<Card, unsigned>(),
cards_no_(0),
  cards_()
{ }

/**
 ** copy constructor
 **
 ** @param	card_counter	object to copy from
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardCounter::CardCounter(CardCounter const& card_counter) :
map<Card, unsigned>(card_counter),
  cards_no_(card_counter.cards_no_),
  cards_(card_counter.cards_)
{ }

/**
 ** copy operator
 **
 ** @param	card_counter	object to copy from
 **
 ** @return	object
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardCounter&
CardCounter::operator=(CardCounter const& card_counter)
{
  static_cast<map<Card, unsigned>& >(*this) = card_counter;
  this->cards_no_ = card_counter.cards_no_;
  this->cards_ = card_counter.cards_;

  return *this;
} // CardCounter& CardCounter::operator=(CardCounter card_counter)

/**
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardCounter::~CardCounter()
{ }

/**
 ** clears all data
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardCounter::clear()
{
  this->cards_.clear();
  this->cards_no_ = 0;
  this->map<Card, unsigned>::clear();

  return ;
} // void CardCounter::clear()

/**
 ** writes the data ('card = number') in 'ostr'
 **
 ** @param	ostr	output stream to write the data to
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardCounter::write(ostream& ostr) const
{
  ostr << "cardno = " << this->cards_no() << '\n';
  for (CardCounter::const_iterator x = this->begin();
       x != this->end();
       ++x) {
    if (x->second)
      ostr << setw(14) << x->first << " = " << x->second << endl;
  }

  return ;
} // void CardCounter::write(ostream& ostr) const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the total number of cards
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardCounter::cards_no() const
{
  DEBUG_ASSERTION((this->cards_no_ == this->cards_.size()),
		  "CardCounter::cards_no():\n"
		  "  'this->cards_no_ = " << this->cards_no_
		  << " != " << this->cards_.size()
		  << " = this->cards_.size()");

  unsigned n = 0;
  for (CardCounter::const_iterator x = this->begin();
       x != this->end();
       ++x)
    n += x->second;

  DEBUG_ASSERTION((this->cards_no_ == n),
		  "CardCounter::cards_no():\n"
		  "  'this->cards_no_ = " << this->cards_no_
		  << " != " << n
		  << " = Sum");

  return this->cards_no_;
} // unsigned CardCounter::cards_no() const

/**
 ** -> result
 **
 ** @param	card	card to return the number of
 **
 ** @return	the number for 'card'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardCounter::operator[](Card const& card) const
{
  CardCounter::const_iterator const x = this->find(card);
  if (x == this->end())
    return 0;

  return x->second;
} // unsigned CardCounter::operator[](Card card) const

/**
 ** set the counter of 'card' to exactly 'n'
 **
 ** @param	card	card to set the number of
 ** @param	n	new number for 'card'
 **
 ** @return	whether something has changed
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
CardCounter::set(Card const& card, unsigned const n)
{
  unsigned const& m = (*this)[card];
  if (n > m)
    return this->min_set(card, n);
  else if (m < n)
    return this->max_set(card, n);

  return false;
} // bool CardCounter::set(Card const& card, unsigned const n)

/**
 ** set the counter of 'card' to minmal 'n'
 **
 ** @param	card	card to set the number of
 ** @param	n	minimal number for 'card'
 **
 ** @return	whether something has changed
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
CardCounter::min_set(Card const& card, unsigned const n)
{
  if (n == 0)
    return false;

  unsigned& t = this->map<Card, unsigned>::operator[](card);
  if (t >= n)
    return false;

  // add the card to the vector
  for (unsigned i = 0; i < n - t; ++i)
    this->cards_.push_back(card);

  // adjust the total cards number
  this->cards_no_ += n - t;

  // set the number (t is reference!)
  t = n;
 
  return true;
} // bool CardCounter::min_set(Card const& card, unsigned const n)

/**
 ** set the counter of 'card' to maximal 'n'
 **
 ** @param	card	card to set the number of
 ** @param	n	maximal number for 'card'
 **
 ** @return	whether something has changed
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
CardCounter::max_set(Card const& card, unsigned const n)
{
  CardCounter::iterator x = this->find(card);
  if (x == this->end())
    return false;

  unsigned& t = x->second;
  if (t <= n)
    return false;

  { // remove the cards from the vector
    unsigned a = t - n;
    vector<Card>::iterator i = this->cards_.begin();
    while (true) {
      if (*i == card) {
	i = this->cards_.erase(i);
	a -= 1;
	if (a == 0)
	  break;
      } else {
	++i;
      }
    } // while (true)
  } // remove the cards from the vector

  if (n == 0) {
    this->cards_no_ -= t;
    this->map<Card, unsigned>::erase(x);
  } else { // if !(n == 0)
    this->cards_no_ -= t - n;
    t = n;
  } // if !(n == 0)

  return true;
} // bool CardCounter::max_set(Card const& card, unsigned const n)

/**
 ** erases the counter of 'card'
 **
 ** @param	card	card to erase
 **
 ** @return	whether something has changed
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
CardCounter::erase(Card const& card)
{
  return this->max_set(card, 0);
} // bool CardCounter::erase(Card const& card)
/**
 ** increments the counter of 'card'
 **
 ** @param	card	card to increment
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardCounter::inc(Card const& card)
{
  this->map<Card, unsigned>::operator[](card) += 1;
  this->cards_.push_back(card);
  this->cards_no_ += 1;

  return ;
} // void CardCounter::inc(Card const& card)

/**
 ** decrements the counter of 'card'
 **
 ** @param	card	card to decrements
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardCounter::dec(Card const& card)
{
  map<Card, unsigned>::iterator x = this->find(card);
  DEBUG_ASSERTION((x != this->end())
		 && x->second,
		 "CardCounter::dec(" << card << "):\n"
		 "  the counter is '0'");
  x->second -= 1;
  if (x->second == 0)
    this->map<Card, unsigned>::erase(x);
  {
    vector<Card>::iterator i = this->cards_.begin();
    while (*i != card)
      ++i;
    this->cards_.erase(i);
  }
  this->cards_no_ -= 1;

  return ;
} // void CardCounter::dec(Card const& card)

/**
 ** writes the card counter in 'ostr'
 **
 ** @param	ostr		output stream to write into
 ** @param	card_counter	object to write
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
ostream&
operator<<(ostream& ostr, CardCounter const& card_counter)
{
  card_counter.write(ostr);
  return ostr;
} // ostream& operator<<(ostream& ostr, CardCounter const& card_counter)

/**
 ** -> result
 **
 ** @param	card_counter_a	first object
 ** @param	card_counter_b	second object
 **
 ** @return	whether the two objects are equal
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
operator==(CardCounter const& card_counter_a,
	   CardCounter const& card_counter_b)
{
  return (static_cast<map<Card, unsigned> const&>(card_counter_a)
	  == static_cast<map<Card, unsigned> const&>(card_counter_b));
} // bool operator==(CardCounter card_counter_a, CardCounter card_counter_b)

/**
 ** -> result
 **
 ** @param	card_counter_a	first object
 ** @param	card_counter_b	second object
 **
 ** @return	whether the two objects differ
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
operator!=(CardCounter const& card_counter_a,
	   CardCounter const& card_counter_b)
{
  return (static_cast<map<Card, unsigned> const&>(card_counter_a)
	  != static_cast<map<Card, unsigned> const&>(card_counter_b));
} // bool operator!=(CardCounter card_counter_a, CardCounter card_counter_b)
