#ifndef KPUZZLEAPP_H 
#define KPUZZLEAPP_H 

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif 

#include <qobject.h>

#include <qpoint.h>
#include <qsize.h>
#include <qstring.h>
#include <qpixmap.h>
#include <qlist.h>
#include <qpaintdevice.h>
#include <qtimer.h>
#include <qcache.h>

class KPuzzleWidget;
class CPicview;

// Game Types - when do you lose a game
// Only the max time counts
#define STD_GAME 1
// You have a certain amount of time for every piece
#define PIECE_TIME_GAME 2
// You have a certain amount of allowed faults for every piece
#define PIECE_FAULTS_GAME 3
// You have a certain amount of allowed faults for the entire game
#define FAULTS_SUM_GAME 4
// You have got a limited time for every piece, then the next piece is shown
// (but there are various rounds)
#define UNBEARABLE_GAME 5

// Ways to turn a piece
#define TURN_LR 1
#define TURN_UD 2

//GameStatus
#define GS_NOT_STARTED 1
#define GS_RUNNING 2
#define GS_SHOW_LARGE 3
#define GS_PAUSED 4

#define GS_WINNING 10
#define GS_SHOW_LARGE_WINNING 11

#define GS_LOSING 20
#define GS_SHOW_LARGE_LOSING 21

#define GS_SHOW_HIGHSCORES 30

// Reasons to terminate
#define TM_WON 1
#define TM_LOST 2
#define TM_QUIT 3
#define TM_FORCE 4

// Status of a piece

struct PieceState
{
	QPoint pos; // Which piece?
	int turn; // How is the piece turned?
	int time; // How many seconds has this piece been seen?
	int faults; // How many times has the player tried to place this piece?
};

// Scoring system

struct ScoreData
{
	int maxTime; 
	int timeScore1; // You get ->this score if you stay below
	int score1Time; // ->this time
	int timeScore2;
	int score2Time;
	int timeScore3;
	int score3Time;

	int rightPieceScore;
	int wrongPieceScore;

	int bonus; // winning bonus

	int maxFaults; // PIECE_FAULTS_GAME or FAULTS_SUM_GAME
	int maxPieceTime; // PIECE_TIME_GAME or UNBEARABLE_GAME (time for every round)
	int maxRounds; // UNBEARABLE_GAME
};

class KPuzzleApp : public QObject
{
	Q_OBJECT
 public:
	KPuzzleApp(KPuzzleWidget* view);
	bool initialize(); // initializes a new game
	void done(); // called when the game terminates
	virtual ~KPuzzleApp();

	void initGameData();
	void calcPiecesCount();

// Data
 public:
	KPuzzleWidget* mainWidget() { return _mainWidget; }
	void setMainWidget(KPuzzleWidget* w) { _mainWidget = w; }
	QPixmap* mainPixmap() { return _mainPixmap; }
	QPixmap* gamePixmap() { return _gamePixmap; }

	// Data which does not change during the game
	QSize pixmapSize() { return _mainPixmap->size(); }
	QSize pieceSize() { return _pieceSize; }
	QSize pieceSizeDisp() { return _pieceSize + QSize(2 * _displace,2 * _displace); }
	bool maskedPieces() { return _useMask; }
	int displace() { return _displace; }

	// Data which *does* change
	QPixmap* currentPiece() { return _currentPiece; }
	QPixmap* currentPieceTurned();
	QSize piecesCount() { return _piecesCount; }
	int piecesTotal() { return _piecesCount.width() * _piecesCount.height(); }
	int availablePiecesCount() { return _pieceList->count(); }

	int status() { return _status; }
	bool running() { return _running; }
	int score() { return _score; }

	QPixmap* largePxm() { return _largePxm; }

 protected:
	void setScore(int s) { _score = s; emit sigScore(s); }
	PieceState* pieceState(int nr = -1) { return _pieceList->at(nr == -1 ? _currentPieceNr : nr); }
	int elapsed() { return _elapsed; }
	QBitmap* getPieceMask(int x,int y);

 protected:
	KPuzzleWidget* _mainWidget;
	CPicview* _picview;
	CPicview* _fullview;
	QTimer _timer;
	QCache<QBitmap>* _maskCache;

	// This does not change during the game
	// Data from the dialog
	QString _filename;
	char _gameType;
	char _dialog_pieceSize;
	char _difficulty;
	bool _scale;
	bool _useMask;
	int _displace;

	QPixmap* _mainPixmap;
	QPixmap* _gamePixmap;

	ScoreData _scoreData;

	// This data forms the game status
	QPixmap* _currentPiece;
	int _currentPieceNr;
	QList<PieceState>* _pieceList; // Holds information about the status of every piece
	QSize _pieceSize;
	QSize _piecesCount;

	int _score; 
        char* _gameStatus;
        int _faults;
        int _rounds;
	bool _running; // false when the game has been terminated, e.g. by signal "won"
	int _elapsed; // Elapsed timer ticks
	int _status;

	QPixmap* _largePxm;

 private:

// Implementation
 public:
	bool setNextPiece(bool currentInvalid = false,bool forceUpdate = false);
	bool setPrevPiece(bool currentInvalid = false);

	bool setPiece(QPoint pos); // returns true if the position was right

	void changeLRTurn() { _pieceList->at(_currentPieceNr)->turn ^= TURN_LR; }
	void changeUDTurn() { _pieceList->at(_currentPieceNr)->turn ^= TURN_UD; }
	void turnCW() { if (++(_pieceList->at(_currentPieceNr)->turn) > 3)
		_pieceList->at(_currentPieceNr)->turn = 0; }
	void turnCCW() { if (--(_pieceList->at(_currentPieceNr)->turn) < 0)
		_pieceList->at(_currentPieceNr)->turn = 3; }

	QPixmap getLargePixmap(QSize size);

	void setStatus(int s);
	void pause(bool p);

	void startTimer() { _timer.start(1000); }
	bool timerActive() const { return _timer.isActive(); }
	void stopTimer() { _timer.stop(); ASSERT(!_timer.isActive()); }

	void updateAll();

        void stopGame(int why) { emit sigTerm(why); }
 
 protected:
	void getPiece();
	bool pieceAvailable(int nr);
	void minglePieces();
	
	void changeScore(char reason);
	void showHighscoreDialog();		

 public slots:
	void slotNewGame();
        void slotStopGame() { stopGame(TM_QUIT); }	
	void slotPause();
	void slotPauseToggle(bool on);
        void slotShowLarge();

	void slotTimer();
        void slotTerm(int reason);

 signals:
	void sigPiecePlaced();
	void sigWinning();
	void sigTerm(int);
	void sigScore(int);
	void sigTime(const char*);
};

#endif // KPUZZLEAPP_H 
