#ifndef AI_H
#define AI_H

#include <qtimer.h>
#include <qlayout.h>
#include <qgrid.h>
#include <kdialogbase.h>
#include <knuminput.h>
#include "dialogs.h"

class Board;
class Piece;
class AI;
class AIElementInfo;
class AIElement;

class AIPiece
{
 public:
	AIPiece() {}
	AIPiece(const Piece *, const Board *);

	void reset();
	void place(Board *);
	void setPoints(double);
	bool increment();
	enum Order { RotateRight, RotateLeft, MoveRight, MoveLeft, NoOrderLeft };
	Order emitOrder();

 private:
	uint   nbPos, nbRot, curPos, curRot;
	uint   betterRot;
	int    curDec, betterDec;
	double betterPoints;
	const Piece *piece;
};

extern const AIElementInfo OCC_LINES;
extern const AIElementInfo PEAK_TO_PEAK;
extern const AIElementInfo HOLES;
extern const AIElementInfo SPACES;
extern const AIElementInfo MEAN;

class AI : public QObject
{
 Q_OBJECT

 public:
    AI(uint thinkTime, uint orderTime);
	virtual ~AI();
	
	void add(const QString &description, const AIElementInfo &,
			 int (*)(const Board &, const Board &) = 0);
	bool configDialog(QWidget *parent);
	void configChanged();

	void launch(const Board *main);
	void stop();
	void start();

 signals:
	void rotateLeft();
	void rotateRight();
	void moveRight();
	void moveLeft();
	void dropDown();

 private slots:
	void timeout();

 protected:
	virtual void initThink();
	virtual Board *createAIBoard() = 0;

	static int nbOccupiedLines(const Board &, const Board &);
	static int nbHoles(const Board &, const Board &);
	static int nbSpaces(const Board &, const Board &);
	static int peakToPeak(const Board &, const Board &);
	static int mean(const Board &, const Board &);
	static int nbRemoved(const Board &, const Board &);

 private:
	bool think();
	void startTimer();
	bool emitOrder();
	double points() const;

	QTimer               timer;
	enum ThinkState { Thinking, GivingOrders };
	ThinkState           state;
	uint                 thinkTime, orderTime;
	bool                 stopped;
	QValueList<AIPiece>  pieces;
	QList<AIElement>     elts;
	Board               *board;
	const Board         *main;
};

//-----------------------------------------------------------------------------
struct AIElementInfo {
	const char *key;
	double      cdef, cmin, cmax;
	bool        triggered;
	int         tdef, tmin, tmax;
	bool        negative;
};

class AIElement : public QObject
{
 Q_OBJECT

 public:
	AIElement(const QString &description, const AIElementInfo &,
			  int(*)(const Board &main, const Board &current));

	bool normal() const;
	void readConfig();
	void addToLayout(QGrid *, int spacing,
					 const char *slotChanged, QObject *receiver);
	void writeConfig();
	void setDefault();
	bool isDefault() const;
	double points(const Board &main, const Board &current) const;

 signals:
	void changed();
	void triggerChanged(int);
	void coeffChanged(double);

 private slots:
    void slotTriggerChanged(int i)  { trigger = i; emit changed(); }
	void slotCoeffChanged(double d) { coeff = d;   emit changed(); }

 private:
	const QString        description;
	const AIElementInfo &info;
	int (*func)(const Board &, const Board &);
	double               coeff;
	int                  trigger;
};

//-----------------------------------------------------------------------------
class AIConfigDialog : public DialogBase
{
 Q_OBJECT

 public:
	AIConfigDialog(QList<AIElement> &, QWidget *parent,
				   const char *name = 0);
	
 private slots:
    void computeDefault();
	void slotDefault();

 private:
	QList<AIElement> &elts;

	void accept();
	void reject();
};

#endif
