#ifndef MP_INTERFACE_H
#define MP_INTERFACE_H

#include <qwidget.h>
#include <qlayout.h>
#include <qtimer.h>
#include <kapp.h>
#include <kaccel.h>

#include "mp_board.h"
#include "mp_option.h"

class Local;
class ConnectionData;
class RemoteHostData;
class KeyConnection;
class KeyConfiguration;

/**
 * This structure contains information about the game
 * configuration.
 */
typedef struct {
	/** The game version id used for identification (e.g. "4").
	 *  You should change this id when the game is made incompatible
	 *  with previous version. (changes in data for example).
	 */
        const char *gameId;
	
	/** Maximum number of local players. */
	uint maxNbLocalPlayers;
	
	/** Interval (in msec.) between data exchange. */
	uint interval;
	
	/** If there are built-in artificial intelligences that can play. */
	bool AIAllowed;
	
	/** Slot for player/AI additional configuration. These must be SLOTs which
	 * take an "int" as parameter. It must open a setting
	 * dialog for the corresponding local player/computer and save the
	 * new settings in the config file. It should probably create a group
	 * with the given number in its name.
	 * If such a pointer is set to 0 : it means there is no perticular
	 * setting.
	 */
	 const char *humanSettingSlot, *AISettingSlot;
} MPGameInfo;

/** 
 * The MPInterface class is useful for multiplayers game
 * management. Each game is represented by a class you have inherited
 * from the @ref MPBoard class.
 * 
 * Multiplayers games can take place with several (humans or eventually
 * AIs) players on the same computer (they use the same keyboard and have
 * each a @ref MPBoard widget on the screen) or/and network players.
 *
 * This class is intended to do all the hard work of sending/receiving data
 * between the players and to send the keyboard events to the right
 * @ref MPBoard. So multiplayers game should be completely transparent
 * from your point of view.
 * 
 * Note : The data exchange is done in background with a timer calling at given
 * intervals the read/write methods. Obviously this kind of things can be done
 * easily with threads but I have no experience with thread programming
 * and not all people have thread libraries and a thread-safe system.
 */

typedef struct { KeyConfiguration *ptr; bool init; } KeyConfData;

class MPInterface : public QWidget
{
 Q_OBJECT
		
 public:
	/** Constructor which takes a MPGameInfo struct as parameter.
	  * "trName" is the translated name of the game. */
    MPInterface(const MPGameInfo &gameInfo,	KAccel *kacc,
				QWidget *parent = 0, const char *name = 0);
	virtual ~MPInterface();
	
 public slots:
	/** Create a single player game for a human being.
	  * Call @ref stop if a game is already created. */
	void singleHuman()     { specialLocalGame(1, 0); }
	/** Create a local game opposing two human beings.
	  * Call @ref stop if a game is already created. */
	void humanVsHuman()    { specialLocalGame(2, 0); }
	/** Create a local game opposing a human with an AI.
	  * Call @ref stop if a game is already created. */
	void humanVsComputer() { specialLocalGame(1, 1); }

	/** Open a dialog to create a multiplayer game.
	  * Call @ref stop if a game is already created. */
	void dialog();
	
	/** Configure keys for current number of human players. */
	void configureKeys();
	
 public:
	/** Called when a new game is created. At this point
	 *  the number of players is known. */
	virtual void init(bool /*server*/) {}
	
	/** Called just before a new game is created (called by
	  * singleHuman, humanVsHuman, humanVsComputer and dialog). */
	virtual void stop(bool /*server*/) {}
	
	/** Called when the start button of the netmeeting is pressed. */
	virtual void start() {}
	
	/** Set action which get associated with keypresses. */
	void setAction(const QString &description, const QString &action,
				   const char *member);

	/** Set keys configuration for the given number of human players.
	 *  The size of the "const char **" QArray must be the number of humans.
	 *  The size of	the "const char *" array is the number of defined actions.
	 */
	void setKeysConfiguration(const QArray<const char **> &defaultKeys);
	
	/** @return the total number of players.
	  * (If called from client : return the number of local boards).
	  */
	uint nbPlayers() const;

	/** @return the player name.
		Do not call from client !
	 */
	QString playerName(uint i) const;

	/** 
	 * Create a new @ref MPBoard.
	 *
	 * @param i is the game index that goes from 0 to the number of
	 * local players : it can be used to retrieve configuration settings.
	 */
	virtual MPBoard *newBoard(uint i) = 0;

	/** 
	 * This method must read data from each client with method
	 * @ref readingStream, do the needed treatement
	 * (for instance which players has lost, which data to be resent, ...) and
	 * then write the useful data to each client with method
	 * @ref writingStream.
	 *
	 * NB: this method is also called for single player games but
	 * you probably only want to check for game over condition (it depends
	 * on game implementation that you really return data to the board).
	 */
	virtual void treatData() = 0;
	
	/** @return the reading stream for board #i.
	  * Do not call from client !
	  */
	QDataStream &readingStream(uint i) const;

	/** @return the writing stream for board #i.
	  */
	QDataStream &writingStream(uint i) const;

	/**
	 * Read data sent from server to clients "MultiplayersInterface" 
	 * (this data is not adressed to boards).
	 * These are meta data that are not directly used in game.
	 * It can be used to display "game over" infos for all
	 * local games.
	 * NB: the use of this method is optional.
	 */
	virtual void dataFromServer(QDataStream &) {}
	
	/** Used by the server to write meta data to clients.
	 *  NB: the use of this method is optional.
	 *  Do not call from client !
	 */
	QDataStream &dataToClientsStream() const;

	/** Write immediately data to clients and local boards.
	  * It is unlike the normal exchange which is driven
	  * by the timer of the server. Be aware of possible
	  * interactions.
	  */
	void immediateWrite();
	
	/** 
	 * This method should be overload if an option widget is used in the
	 * the "netmeeting" dialog). By default a
	 * null pointer is returned and so no option widget is shown.
	 * The option widget must be inherited from the @ref MPOptionWidget class.
	 */
	virtual MPOptionWidget *newOptionWidget(bool /*server*/) const 
		{ return 0; };

	/** Called when a network error occured or when a host gets disconnected.
	 *  The default implementation displays a message and calls singleHumans()
	 * 	ie it stops the current game. By overloading this method, it is
	 *  possible to continue the game at this point with the remaining players.
	 */
	virtual void hostDisconnected(uint i, const QString &msg);

 protected:
	virtual void paintEvent(QPaintEvent *);
	
 private slots:
	void activateKeys(bool activate);
	void configureKeys(uint nbHumans);
	void singleHumanSlot();

 public:
	class Data	{
	public:
		Data() {}
		MPBoard *ptr;
		int      humanIndex;
		QString  name;
	};

  private:
	Local            *internal;
	const MPGameInfo  gameInfo;
	QValueList<Data>  boards;
	uint              nbLocalHumans;
	QHBoxLayout       hbl;
	bool              server, disconnected;
	
	KAccel             *kacc;
	KeyConnection      *kcn;
	QArray<KeyConfData> kcf;

	void createServerGame(const QList<RemoteHostData> &);
	void createClientGame(const RemoteHostData &);
	void createLocalGame(const ConnectionData &);
	void specialLocalGame(uint nbHumans, uint nbComputers);
	
	void clear();
	void insertKeys(uint nbHumans);
};

#endif // MP_INTERFACE_H
