/*
 * Copyright (C) 1995 Paul Olav Tvete <paul@troll.no>
 * Copyright (C) 2000-2009 Stephan Kulow <coolo@kde.org>
 * Copyright (C) 2009-2010 Parker Coates <coates@kde.org>
 *
 * License of original code:
 * -------------------------------------------------------------------------
 *   Permission to use, copy, modify, and distribute this software and its
 *   documentation for any purpose and without fee is hereby granted,
 *   provided that the above copyright notice appear in all copies and that
 *   both that copyright notice and this permission notice appear in
 *   supporting documentation.
 *
 *   This file is provided AS IS with no warranties of any kind.  The author
 *   shall have no liability with respect to the infringement of copyrights,
 *   trade secrets or any patents by this file or any part thereof.  In no
 *   event will the author be liable for any lost revenue or profits or
 *   other special, indirect and consequential damages.
 * -------------------------------------------------------------------------
 *
 * License of modifications/additions made after 2009-01-01:
 * -------------------------------------------------------------------------
 *   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 should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * -------------------------------------------------------------------------
 */

#ifndef DEALER_H
#define DEALER_H

// own
#include "gamestate.h"
#include "patpile.h"
#include "speeds.h"
#include "view.h"
// KCardGame
#include <KCardDeck>
#include <KCardScene>
// Qt
#include <QMap>
#include <QStack>
#include <QTimer>
#include <QSet>

class DealerInfo;
class MessageBox;
class MoveHint;
class SolverInterface;
class SolverThread;

class QAction;

#define scores_group "Scores"

class DealerScene : public KCardScene
{
    Q_OBJECT

public:
    enum { None = 0, Hint = 1, Demo = 2, Draw = 4, Deal = 8, Redeal = 16 } Actions;

    explicit DealerScene( const DealerInfo * di );
    ~DealerScene();

    virtual void initialize() = 0;

    void relayoutScene() override;
    void updateWonItem();

    void addPatPile( PatPile * pile );
    void removePatPile( PatPile * pile );
    void clearPatPiles();
    QList<PatPile*> patPiles() const;

    void setAutoDropEnabled( bool enabled );
    bool autoDropEnabled() const;

    int gameNumber() const;

    int gameId() const;

    void setActions( int actions );
    int actions() const;

    virtual QList<QAction*> configActions() const;

    void startHint();
    void stopHint();
    bool isHintActive() const;
 
    void startDrop();
    void stopDrop();
    bool isDropActive() const;

    void startDemo();
    void stopDemo();
    bool isDemoActive() const;

    void setSolverEnabled( bool enabled );
    SolverInterface * solver() const;
    void startSolver();

    virtual bool isGameLost() const;
    virtual bool isGameWon() const;

    bool allowedToStartNewGame();
    int moveCount() const;

    void saveFile( QIODevice * io );
    bool loadFile( QIODevice * io );
    void saveLegacyFile( QIODevice * io );
    bool loadLegacyFile( QIODevice * io );
    
    virtual void mapOldId(int id);
    virtual int oldId() const;
    void recordGameStatistics();

    QImage createDump() const;

Q_SIGNALS:
    void undoPossible(bool poss);
    void redoPossible(bool poss);
    void newCardsPossible(bool poss);
    void gameInProgress(bool inProgress);

    void demoActive(bool active);
    void hintActive(bool active);
    void dropActive(bool active);
    void updateMoves(int moves);

    void solverStateChanged(const QString &text);
    void newDeal();

    void cardsPickedUp();
    void cardsPutDown();

public Q_SLOTS:
    void startNew( int dealNumber = -1 );

    void undo();
    void redo();

    void stop();

    void drawDealRowOrRedeal();
    virtual bool tryAutomaticMove( KCard * card );

protected:
    bool allowedToAdd(const KCardPile * pile, const QList<KCard*> & cards) const override;
    bool allowedToRemove(const KCardPile * pile, const KCard * card) const override;

    virtual bool checkAdd( const PatPile * pile, const QList<KCard*> & oldCards, const QList<KCard*> & newCards ) const;
    virtual bool checkRemove( const PatPile * pile, const QList<KCard*> & cards ) const;
    virtual bool checkPrefering( const PatPile * pile, const QList<KCard*> & oldCards, const QList<KCard*> & newCards ) const;

    void cardsMoved( const QList<KCard*> & cards, KCardPile * oldPile, KCardPile * newPile ) override;

    void mouseDoubleClickEvent( QGraphicsSceneMouseEvent * mouseEvent ) override;
    void mousePressEvent( QGraphicsSceneMouseEvent * mouseEvent ) override;
    void mouseReleaseEvent( QGraphicsSceneMouseEvent * mouseEvent ) override;

    virtual void restart( const QList<KCard*> & cards ) = 0;

    void setSolver( SolverInterface * solver );

    virtual QList<MoveHint> getHints();

    // reimplement these to store and load game-specific information in the state structure
    virtual QString getGameState() const;
    virtual void setGameState( const QString & state );

    // reimplement these to store and load game-specific options in the saved game file
    virtual QString getGameOptions() const;
    virtual void setGameOptions( const QString & options );

    void addCardForDeal( KCardPile * pile, KCard * card, bool faceUp, QPointF startPos );
    void startDealAnimation();

    void setNeededFutureMoves( int moves );
    int neededFutureMoves() const;

    void setDeckContents( int copies = 1,
                          const QList<KCardDeck::Suit> & suits = KCardDeck::standardSuits() );

    void multiStepMove( const QList<KCard*> & cards,
                        KCardPile * pile,
                        const QList<KCardPile*> & freePiles,
                        const QList<KCardPile*> & freeCells,
                        int duration  );

    QList<MoveHint> getSolverHints();

protected Q_SLOTS:
    void takeState();
    virtual void animationDone();
    virtual bool newCards();
    virtual bool drop();

private Q_SLOTS:
    void stopAndRestartSolver();
    void slotSolverEnded();
    void slotSolverFinished( int result );

    void demo();

    void showWonMessage();

private:
    void undoOrRedo( bool undo );

    void resetInternals();

    MoveHint chooseHint();

    void won();

    int speedUpTime( int delay ) const;

    void multiStepSubMove( QList<KCard*> cards,
                           KCardPile * pile,
                           QList<KCardPile*> freePiles,
                           const QList<KCardPile*> & freeCells );
    void continueMultiStepMove();

    const DealerInfo * const m_di;

    SolverInterface * m_solver;
    SolverThread * m_solverThread;
    QList<MOVE> m_winningMoves;

    KCard * m_peekedCard;
    MessageBox * m_wonItem;

    QTimer m_solverUpdateTimer;
    QTimer m_demoTimer;
    QTimer m_dropTimer;

    int m_dealNumber;
    int m_loadedMoveCount;
    int m_neededFutureMoves;
    int m_supportedActions;

    bool m_autoDropEnabled;
    bool m_solverEnabled;

    bool m_dealStarted;
    bool m_dealWasEverWinnable;
    bool m_dealHasBeenWon;
    bool m_dealWasJustSaved;
    bool m_statisticsRecorded;
    bool m_playerReceivedHelp;

    // We need a flag to avoid telling someone the game is lost
    // just because the winning animation moved the cards away
    bool m_toldAboutWonGame;
    bool m_toldAboutLostGame;

    QSet<KCard*> m_cardsRemovedFromFoundations;
    qreal m_dropSpeedFactor;
    bool m_interruptAutoDrop;

    bool m_dealInProgress;
    bool m_hintInProgress;
    bool m_demoInProgress;
    bool m_dropInProgress;

    bool m_hintQueued;
    bool m_demoQueued;
    bool m_dropQueued;
    bool m_newCardsQueued;
    bool m_takeStateQueued;

    QStack<GameState*> m_undoStack;
    GameState * m_currentState;
    QStack<GameState*> m_redoStack;
    QHash<KCard*,CardState> m_lastKnownCardStates;

    QList<QPair<KCard*,KCardPile*> > m_multiStepMoves;
    int m_multiStepDuration;

    QList<PatPile*> m_patPiles;

    QMap<KCard*,QPointF> m_initDealPositions;
};

#endif
