/*
    QSoloCards is a collection of Solitaire card games written using Qt
    Copyright (C) 2009  Steve Moore

    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 3 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 should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __DEALANIMATION_H__
#define __DEALANIMATION_H__

#include <QtCore/QObject>

#include "CardStack.h"
#include "CardMoveRecord.h"
#include <list>
#include <vector>

#include <QtCore/QTimer>
#include <QtGui/QGraphicsPixmapItem>
#include <QtGui/QGraphicsItemAnimation>

// DealItems can be used to define the cards that will be dealt to a stack.
// The DealItem can then be added to the DealItemVector which is the overall
// definition of the deal.   
class DealItem
{
public:
    DealItem(CardStack * pDst,CardStack * pSrc);
    DealItem(const DealItem & rh);
    ~DealItem();

    inline bool isEmpty()const{return (m_flipList.empty() || m_pSrc->isEmpty());}
    inline void addCard(bool flip){m_flipList.push_back(flip);}

    // returns the flip value of the next card and pops it off the list.
    bool getNextCard();

    inline CardStack * dst(){return m_pDst;}
    inline CardStack * src(){return m_pSrc;}

    DealItem & operator=(const DealItem & rh);

private:
    CardStack *       m_pDst;
    CardStack *       m_pSrc;
    std::list<bool>   m_flipList;  // the list allows a record of how many cards to deal from the 
                                   // src to the dst and which ones need to be flipped.
                                   // The cards will only be flipped once added to the dst stack.
};

typedef std::vector<DealItem>  DealItemVector;

class DealGraphicsItem:public QObject,public QGraphicsPixmapItem
{
    Q_OBJECT
public:
    DealGraphicsItem(DealItem & dealItem,CardMoveRecord & moveRecord);
    virtual ~DealGraphicsItem();

    inline DealItem & getItem(){return m_dealItem;}

    void setupAnimation(unsigned int duration,unsigned int delay,unsigned int zValue);

    inline bool flipCard()const{return m_flipCard;}  
    
    inline const PlayingCard & card()const{return m_card;}

    void stopAni();

signals:
    void aniFinished(DealGraphicsItem *);
public slots:
    void slotAniFinished();
    void slotTimeToStart();

private:
    DealItem &                m_dealItem;
    bool                      m_flipCard;
    PlayingCard               m_card;
    QGraphicsItemAnimation *  m_pGItemAni;
    CardMoveRecord &          m_moveRecord;
    QTimeLine                 m_timeLine;
    QTimer                    m_delayTimer;
    bool                      m_emitFinished;
};

typedef std::vector<DealGraphicsItem *>   DealGraphicsItemVector;

class DealAnimation: public QObject
{
    Q_OBJECT
public:
    enum
    {
	PerDealDuration=400,
	PerCardDelay=60
    };

    DealAnimation();
    virtual ~DealAnimation();

    // this duration may not be the total for the entire deal.  This the duration for one round.
    // So, if you have ten stacks and are dealing 5 cards to eacy stack.  The entire duration will
    // be the duration x 5.
    inline void setDuration(int durInMilSecs=PerDealDuration){m_duration=durInMilSecs;}
    inline int getDuration()const{return m_duration;}

    // the option of sending the complete signal is for cases like the initial deal of the cards.
    // In that case the caller does not care when it is done as long as the user can't do anything
    // until the deal is complete.  The use of the CardAnimationLock will ensure this whether or not
    // the caller wants to get the notification.
    bool dealCards(const DealItemVector & itemVector,bool createMoveRecord=true);

    void stopAni();

signals:
    void cardsMoved(const CardMoveRecord & moveRecord);

public slots:
     void slotAniFinished(DealGraphicsItem * pDealGraphicsItem);

private:
    bool cardsRemaining();
    void cleanUpGraphicsItem(DealGraphicsItem * pDealGraphicsItem);
    void cleanUpGraphicsItems();
    void noAniStackUdpates();
    void buildAniStackUpdates();

    CardMoveRecord            m_moveRecord;
    DealItemVector            m_dealItemVector;
    DealGraphicsItemVector    m_graphicsItemVector;
    bool                      m_aniRunning;
    bool                      m_stopAni;
    int                       m_duration;
    bool                      m_createMoveRecord;
};

#endif