/*
 * Spider-Arachnid: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: heap.c 2 2005-05-14 22:25:56Z tom $
 */

#include "heap.h"
#include "deck.h"


/** --- base class Heap ---------------------------------------------------- **/

/** Constructor */
Heap::Heap(unsigned int maxCards)
{
  maxCount = maxCards;
  emptyChanged = true;
}

/** Destructor */
Heap::~Heap()
{
}

/** Current count of cards */
int Heap::count() const
{
  return allCards.size();
}

/** Card in heap */
const Card& Heap::card(int position) const
{
  return allCards[position];
}

/** Top card of the heap */
const Card& Heap::top() const
{
  return allCards.back();
}

/** Add a new card */
void Heap::add(const Card& card)
{
  if (allCards.size() < maxCount)
  {
    if (allCards.empty())
      emptyChanged = true;
    allCards.push_back(card);
  }
}

/** Remove the top card */
void Heap::remove()
{
  if (!allCards.empty())
  {
    allCards.pop_back();
    if (allCards.empty())
      emptyChanged = true;
  }
}

/** Move some matching cards to an other heap */
void Heap::moveTo(Heap* other, int countToMove)
{
  for (int i = count() - countToMove; i < count(); ++i)
    other->add(card(i));
  for (int i = 0; i < countToMove; ++i)
    remove();
}

/** Is the heap empty? */
bool Heap::empty() const
{
  return allCards.empty();
}

/** Is the heap changed? */
bool Heap::changed() const
{
  return emptyChanged;
}

/** Reset changed property */
void Heap::resetChanged()
{
  emptyChanged = false;
}


/** --- class Pack --------------------------------------------------------- **/

/** Constructor */
Pack::Pack(const Deck& deck) :
  Heap(deck.count())
{
  for (int pos = 0; pos < deck.count(); ++pos)
    add(deck.card(pos));
}

/** First initial deal of a game */
void Pack::initialDeal(Piles& piles, int rows, Piles& extra)
{
  for (int r = 0; r < rows; ++r)
    deal(piles);
  deal(extra);

  // turn all open cards
  for (unsigned int p = 0; p < piles.size(); ++p)
    piles[p]->turn();
}

/** Deal one row to the piles */
void Pack::deal(Piles& piles)
{
  for (unsigned int p = 0; p < piles.size(); ++p)
  {
    piles[p]->add(top());
    remove();
  }
}

/** Cancel the deal */
void Pack::takeBackDeal(Piles& piles)
{
  for (int p = piles.size(); --p >= 0; )
  {
    add(piles[p]->top());
    piles[p]->remove();
  }
}


/** --- class Pile --------------------------------------------------------- **/

/** Constructor */
Pile::Pile(const Deck& deck) :
  Heap(deck.count())
{
  currentOpen = 0;
  currentMatching = 0;
  currentSelected = 0;
  currentChanged = 0;
}

/** Add a new card */
void Pile::add(const Card& card)
{
  Heap::add(card);
  ++currentOpen;
  if (topCardsMatches())
    ++currentMatching;
  currentSelected = 0;
  ++currentChanged;
}

/** Remove top card from pile */
void Pile::remove()
{
  if (currentSelected > 0)
    --currentSelected;
  if (topCardsMatches())
    --currentMatching;
  if (currentOpen > 0)
    --currentOpen;
  Heap::remove();
  if (currentChanged > 0)
    --currentChanged;
  if (currentChanged < 1 && !empty())
    currentChanged = 1;
}

/** Turn all open top cards or rather open the top card */
void Pile::turn()
{
  if (currentOpen < 1 && !empty())
    currentOpen = 1;
  else
    currentOpen = 0;
  currentMatching = 0;
  if (currentChanged < 1 && !empty())
    currentChanged = 1;
}

/** Current count of open cards */
int Pile::open() const
{
  return currentOpen;
}

/** Current count of matching cards */
int Pile::getMatching() const
{
  return currentMatching;
}

/** The two open top cards are matching */
bool Pile::topCardsMatches() const
{
  return (open() >= 2 && card(count() - 1).matchesTo(card(count() - 2)));
}

/** Current count of selected cards */
int Pile::selected() const
{
  return currentSelected;
}

/** Select up to max matching cards on the end of this pile */
void Pile::select(int max)
{
  currentSelected = 0;
  if (open() > 0)
  {
    currentSelected = 1;
    for (int i = count(); --i > count() - open(); )
      if (card(i).matchesTo(card(i - 1)))
        currentSelected++;
      else
        break;
  }
  if (currentSelected > max && max > 0)
    currentSelected = max;
  if (currentChanged < currentSelected)
    currentChanged = currentSelected;
}

/** Unselect this pile */
void Pile::unselect()
{
  if (currentChanged < currentSelected)
    currentChanged = currentSelected;
  currentSelected = 0;
}

/** Adapt the selection to match an other pile */
void Pile::adaptSelectionTo(const Pile* other)
{
  if (!other->empty())
  {
    if (currentChanged < currentSelected)
      currentChanged = currentSelected;
    int diff = other->top().rank - top().rank;
    if (diff > 0 && diff <= currentSelected)
      currentSelected = diff;
    else
      currentSelected = 0;
  }
}

/** Matches the selection to an other pile? */
bool Pile::selectionMatchesTo(const Pile* other, bool matchSuit) const
{
  return (!other->empty() &&
          (other->top().rank == top().rank + currentSelected) &&
          (other->top().suit == top().suit || !matchSuit));
}

/** Is the heap changed? */
bool Pile::changed() const
{
  return (Heap::changed() || currentChanged > 0);
}

/** Reset changed property */
void Pile::resetChanged()
{
  Heap::resetChanged();
  currentChanged = 0;
}

/** How many cards are changed? */
int Pile::cardsChanged() const
{
  return currentChanged;
}


/** --- class FinalHeap ---------------------------------------------------- **/

/** Constructor */
FinalHeap::FinalHeap(const Deck& deck) :
  Heap(deck.cardsInSuit)
{
  bonus = false;
}

/** Set bonus of the final heap */
void FinalHeap::setBonus(bool newBonus)
{
  bonus = newBonus;
}

/** Has this final heap bonus? */
bool FinalHeap::getBonus() const
{
  return bonus;
}