#include "pile.h"
#include "dealer.h"
#include <kdebug.h>
#include <tqpainter.h>
#include "cardmaps.h"
#include <assert.h>
#include "speeds.h"

const int Pile::RTTI          = 1002;

const int Pile::Default       = 0x0000;
const int Pile::disallow      = 0x0001;
const int Pile::several       = 0x0002; // default: move one card

// Add-flags
const int Pile::addSpread     = 0x0100;

// Remove-flags
const int Pile::autoTurnTop   = 0x0200;
const int Pile::wholeColumn   = 0x0400;



Pile::Pile( int _index, Dealer* _dealer)
    : TQCanvasRectangle( _dealer->canvas() ),
      m_dealer(_dealer),
      m_atype(Custom),
      m_rtype(Custom),
      myIndex(_index),
      _target(false)
{
    // Make the patience aware of this pile.
    dealer()->addPile(this);

    TQCanvasRectangle::setVisible(true); // default
    _checkIndex = -1;
    m_addFlags    = 0;
    m_removeFlags = 0;

    setBrush(TQt::black);
    setPen(TQPen(TQt::black));

    setZ(0);
    initSizes();
}


void Pile::initSizes()
{
    setSpread( cardMap::CARDY() / 5 + 1 );
    setHSpread( cardMap::CARDX() / 9 + 1 );
    setDSpread( cardMap::CARDY() / 8 );

    setSize( cardMap::CARDX(), cardMap::CARDY() );
}

void Pile::setType(PileType type)
{
    setAddType(type);
    setRemoveType(type);
}

void Pile::setAddType(PileType _type)
{
    m_atype = _type;
    switch (_type) {
        case Custom:
        case FreeCell:
            break;
        case KlondikeTarget:
            setTarget(true);
            break;
        case KlondikeStore:
        case GypsyStore:
        case FreecellStore:
            setAddFlags(Pile::addSpread | Pile::several);
            break;
    }
}

void Pile::setRemoveType(PileType _type)
{
    m_rtype = _type;
    switch (_type) {
        case Custom:
            break;
        case KlondikeTarget:
            setRemoveFlags(Pile::disallow);
            break;
        case KlondikeStore:
        case GypsyStore:
        case FreeCell:
            break;
        case FreecellStore:
            setRemoveFlags(Pile::several | Pile::autoTurnTop);
            break;
    }
}

Pile::~Pile()
{
    dealer()->removePile(this);

    for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
    {
        if ((*it)->source() != this) {
            int i = -13;
            if ((*it)->source())
                i = (*it)->source()->index();
            kdDebug(11111) << "pile doesn't match " << index() << " - " << i << endl;
        }
        (*it)->setSource(0);
    }
}

void Pile::resetCache()
{
    cache.resize(0, 0);
    cache_selected.resize(0, 0);
}

void Pile::drawShape ( TQPainter & painter )
{
    if (isSelected()) {
        if (cache.isNull())
            dealer()->drawPile(cache, this, false);
        painter.drawPixmap(int(x()), int(y()), cache);
    } else {
        if (cache_selected.isNull())
            dealer()->drawPile(cache_selected, this, true);
        painter.drawPixmap(int(x()), int(y()), cache_selected);
    }
}

bool Pile::legalAdd( const CardList& _cards ) const
{
    if ( m_addFlags & disallow )
        return false;

    if ( !( m_addFlags & several ) && _cards.count() > 1 )
        return false;

    // getHint removes cards without turning, so it could be it
    // checks later if cards can be added to a face down card
    if (top() && !top()->realFace())
	return false;

    switch (addType()) {
        case Custom:
            return dealer()->checkAdd( checkIndex(), this, _cards );
            break;
        case KlondikeTarget:
            return add_klondikeTarget(_cards);
            break;
        case FreecellStore:
        case KlondikeStore:
            return add_klondikeStore(_cards);
            break;
        case GypsyStore:
            return add_gypsyStore(_cards);
            break;
        case FreeCell:
            return add_freeCell(_cards);
    }
    return false;
}

bool Pile::legalRemove(const Card *c) const
{
    if ( m_removeFlags & disallow ) {
        return false;
    }
    if ( !( m_removeFlags & several ) && top() != c)
	return false;

    switch (removeType()) {
        case Custom:
            return dealer()->checkRemove( checkIndex(), this, c);
            break;
        case KlondikeTarget:
        case GypsyStore:
        case KlondikeStore:
            break;
        case FreecellStore:
            return remove_freecellStore(c);
            break;
        case FreeCell:
            return (top() == c);
            break;
    }
    return true;
}

void Pile::setVisible(bool vis)
{
    TQCanvasRectangle::setVisible(vis);
    dealer()->enlargeCanvas(this);

    for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
    {
        (*it)->setVisible(vis);
        dealer()->enlargeCanvas(*it);
    }
}

void Pile::moveBy(double dx, double dy)
{
    TQCanvasRectangle::moveBy(dx, dy);
    dealer()->enlargeCanvas(this);

    for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
    {
        (*it)->moveBy(dx, dy);
        dealer()->enlargeCanvas(*it);
    }
}

int Pile::indexOf(const Card *c) const
{
    assert(c->source() == this);
    return m_cards.findIndex(const_cast<Card*>(c)); // the list is of non-const cards
}

Card *Pile::at(int index) const
{
    if (index < 0 || index >= int(m_cards.count()))
        return 0;
    return *m_cards.at(index);
}

// Return the top card of this pile.
//

Card *Pile::top() const
{
    if (m_cards.isEmpty())
        return 0;

    return m_cards.last();
}

void Pile::clear()
{
    for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
    {
        (*it)->setSource(0);
    }
    m_cards.clear();
}

void Pile::add( Card *_card, int index)
{
    if (_card->source() == this)
        return;

    Pile *source = _card->source();
    if (source) {
        _card->setTakenDown(source->target() && !target());
        source->remove(_card);
    }

    _card->setSource(this);

    if (index == -1)
        m_cards.append(_card);
    else {
        while (m_cards.count() <= uint(index))
            m_cards.append(0);
        assert(m_cards[index] == 0);
        m_cards[index] = _card;
    }
}


// Return the number of pixels in x and y that the card should be
// offset from the start position of the pile. 
//
// Note: Default is to only have vertical spread (Y direction). 

TQSize Pile::cardOffset( bool _spread, bool _facedown, const Card *before) const
{
    if (_spread) {
        if (_facedown)
            return TQSize(0, dspread());
        else {
            if (before && !before->isFaceUp())
                return TQSize(0, dspread());
            else
                return TQSize(0, spread());
        }
    }

    return TQSize(0, 0);
}

/* override cardtype (for initial deal ) */
void Pile::add( Card* _card, bool _facedown, bool _spread )
{
    if (!_card)
        return;

    // The top card
    Card *t = top();

    // If this pile is visible, then also show the card.
    if (isVisible())
        _card->show();
    else
        _card->hide();

    _card->turn( !_facedown );

    TQSize offset = cardOffset(_spread, _facedown, t);

    int x2, y2, z2;

    if (t) {
        x2 = int(t->realX() + offset.width());
        y2 = int(t->realY() + offset.height());
        z2 = int(t->realZ() + 1);
    } else {
        x2 = int(x());
        y2 = int(y());
        z2 = int(z() + 1);
    }

    add(_card);

    if (_facedown || !isVisible()) {
        _card->move( x2, y2 );
        _card->setZ( z2 );
    } else {
        _card->moveTo(x2, y2, z2, STEPS_INITIALDEAL);
    }

    dealer()->enlargeCanvas(_card);
}

void Pile::remove(Card *c)
{
    assert(m_cards.contains(c));
    m_cards.remove(c);
}

void Pile::hideCards( const CardList & cards )
{
    for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it)
        m_cards.remove(*it);
}

void Pile::unhideCards( const CardList & cards )
{
    for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it)
        m_cards.append(*it);
}

CardList Pile::cardPressed(Card *c)
{
    CardList result;

    if (!legalRemove(c))
        return result;

    int below = -1;

    if (!c->isFaceUp())
        return result;

    for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
    {
        if (c == *it) {
            below = 0;
        }
        if (below >= 0) {
            (*it)->setAnimated(false);
            (*it)->setZ(128 + below);
            below++;
            result.append(*it);
        }
    }
    return result;
}

void Pile::moveCards(CardList &cl, Pile *to)
{
    if (!cl.count())
        return;

    for (CardList::Iterator it = cl.begin(); it != cl.end(); ++it)
        to->add(*it);

    if (m_removeFlags & autoTurnTop && top()) {
        Card *t = top();
        if (!t->isFaceUp()) {
            t->flipTo(int(t->x()), int(t->y()), 8);
            canvas()->update();
        }
    }

    to->moveCardsBack(cl, false);
}

void Pile::moveCardsBack(CardList &cl, bool anim)
{
    if (!cl.count())
        return;

    Card *c = cl.first();

    Card *before = 0;
    TQSize off;

    int steps = STEPS_MOVEBACK;
    if (!anim)
        steps = 0;

    for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
    {
        if (c == *it) {
            if (before) {
                off = cardOffset(m_addFlags & Pile::addSpread, false, before);
                c->moveTo( before->realX() + off.width(),
			   before->realY() + off.height(),
			   before->realZ() + 1, steps);
                dealer()->enlargeCanvas(c);
            }
            else {
                c->moveTo( int(x()), int(y()), int(z()) + 1, steps);
            }
            break;
        } else
            before = *it;
    }

    before = c;
    CardList::Iterator it = cl.begin(); // == c
    ++it;

    off = cardOffset(m_addFlags & Pile::addSpread, false, 0);

    for (; it != cl.end(); ++it)
    {
        (*it)->moveTo( before->realX() + off.width(),
                             before->realY() + off.height(),
                             before->realZ() + 1, steps);
        dealer()->enlargeCanvas(*it);
        before = *it;
    }
}

bool Pile::cardClicked(Card *c)
{
    emit clicked(c);
    return false;
}

bool Pile::cardDblClicked(Card *c)
{
    emit dblClicked(c);
    return false;
}

#include "pile.moc"
