#include "mp_interface.h"

#include <qtimer.h>
#include <kapp.h>
#include <klocale.h>
#include <kmessagebox.h>
#include "defines.h"
#include "types.h"
#include "meeting.h"
#include "internal.h"
#include "keys.h"
#include "wizard.h"

/*****************************************************************************/
/* Constructor & Destructor                                                  */
/*****************************************************************************/
MPInterface::MPInterface(const MPGameInfo &_gameInfo,
						 KAccel *_kacc, QWidget *parent, const char *name)
: QWidget(parent, name), internal(0), gameInfo(_gameInfo),
  nbLocalHumans(0), hbl(this, 0, 5),
  kacc(_kacc), kcn(new KeyConnection(kacc))
{
	ASSERT( gameInfo.maxNbLocalPlayers>=1 );

	hbl.setResizeMode( QLayout::Fixed );
	
	kcf.resize(gameInfo.maxNbLocalPlayers+1);
	for (uint i=0; i<kcf.size(); i++) {
		kcf[i].ptr  = new KeyConfiguration(i, *kcn);
		kcf[i].init = FALSE;
	}
}

MPInterface::~MPInterface()
{
	delete internal;
	delete kcn;
	for (uint i=0; i<kcf.size(); i++) delete kcf[i].ptr;
}

/*****************************************************************************/
/* Game creation                                                             */
/*****************************************************************************/
void MPInterface::clear()
{
	if (internal) {
		stop(server);
		delete internal;
		internal = 0;
		kcf[nbLocalHumans].ptr->remove();
	}
}

void MPInterface::dialog()
{
	clear();
	
	// configuration wizard
	ConnectionData cd;
	MPWizard wiz(gameInfo, cd, this);
	connect(&wiz, SIGNAL(configureKeys(uint)), SLOT(configureKeys(uint)));
	if ( wiz.exec()==QDialog::Rejected ) {
		singleHuman(); // create a single game
		return;
	}

	// net meeting
	QList<RemoteHostData> rhd;
	rhd.setAutoDelete(TRUE);
	if (cd.network) {
		cId id(kapp->name(), gameInfo.gameId);
		MPOptionWidget *ow = newOptionWidget(cd.server);
		NetMeeting *nm;
		if (cd.server) nm = new ServerNetMeeting(id, cd.rhd, ow, rhd, this);
		else nm = new ClientNetMeeting(id, cd.rhd, ow, this);
		int res = nm->exec();
		if (ow) {
			if (res) ow->saveData();
			delete ow;
		}
		delete nm;
		if (!res) {
			singleHuman();
			return;
		}
	}

	createLocalGame(cd);
	if (cd.server) createServerGame(rhd);
	else createClientGame(cd.rhd);
}

void MPInterface::specialLocalGame(uint nbHumans, uint nbAIs)
{
	clear();

	ConnectionData cd;
	BoardData bd;
	PlayerCheckBox::Type t;
	KConfig *kconf = kapp->config();
	kconf->setGroup(MP_GROUP);
	for (uint i=0; i<(nbHumans+nbAIs); i++) {
		bd.type = (i<nbHumans ? PlayerCheckBox::Human : PlayerCheckBox::AI);
		bd.name = QString::null;
		t = (PlayerCheckBox::Type)
			kconf->readNumEntry(QString(MP_PLAYER_TYPE).arg(i),
								PlayerCheckBox::Empty);
		if ( bd.type==t )
			bd.name = kconf->readEntry(QString(MP_PLAYER_NAME).arg(i),
									   QString::null);
		if ( bd.name.isNull() )
			bd.name = (i<nbHumans ? i18n("Human %1").arg(i+1)
					   : i18n("AI %1").arg(i-nbHumans+1));
		cd.rhd.bds += bd;
	}
	cd.server  = TRUE;
	cd.network = FALSE;
	ASSERT( (nbHumans+nbAIs)<=gameInfo.maxNbLocalPlayers );
	ASSERT( gameInfo.AIAllowed || nbAIs==0 );

	createLocalGame(cd);
	QList<RemoteHostData> rhd;
	createServerGame(rhd);
}

void MPInterface::createServerGame(const QList<RemoteHostData> &rhd)
{
	internal = (rhd.count()
		 ? (Local *)new NetworkServer(this, boards, rhd, gameInfo.interval)
		 : (Local *)new LocalServer(this, boards, gameInfo.interval));
	init(TRUE);
}

void MPInterface::createClientGame(const RemoteHostData &rhd)
{
	QList<RemoteHostData> r;
	r.append((RemoteHostData *)&rhd);
	internal = new Client(this, boards, r);
	init(FALSE);
}

void MPInterface::createLocalGame(const ConnectionData &cd)
{
	server = cd.server;
	nbLocalHumans = 0;
	for (uint i=0; i<cd.rhd.bds.count(); i++)
		if ( cd.rhd.bds[i].type==PlayerCheckBox::Human ) nbLocalHumans++;
	
	// add or remove boards
	uint old_s = boards.count();
	uint new_s = cd.rhd.bds.count();
	for (uint i=new_s; i<old_s; i++) {
		delete boards[i].ptr;
		boards.remove(boards.at(i));
	}
	Data d;
	for(uint i=old_s; i<new_s; i++) {
		d.ptr = newBoard(i);
		hbl.addWidget(d.ptr);
		d.ptr->show();
		connect(d.ptr, SIGNAL(activateKeys(bool)),
				SLOT(activateKeys(bool)));
		boards += d;
	}

	insertKeys(nbLocalHumans);
	
	// init local boards
	int k = 0;
	for (uint i=0; i<boards.count(); i++) {
		bool h = ( cd.rhd.bds[i].type==PlayerCheckBox::Human );
		boards[i].humanIndex = (h ? k : -1);
		if (h) {
			kcf[nbLocalHumans].ptr->connect(k, boards[i].ptr);
			k++;
		}
		boards[i].name = cd.rhd.bds[i].name;
		boards[i].ptr->init(!h, cd.network || boards.count()>1, server, i==0,
							cd.rhd.bds[i].name);
	}
}

/*****************************************************************************/
/* Key management                                                            */
/*****************************************************************************/
void MPInterface::setAction(const QString &description, const QString &action,
							const char *member)
{
	kcn->append(description, action, member);
}

void MPInterface::setKeysConfiguration(const QArray<const char **> &defaultKeys)
{
	uint i = defaultKeys.size();
	kcf[i].ptr->init(defaultKeys);
	kcf[i].init = TRUE;
}

void MPInterface::insertKeys(uint nbHumans)
{
	if ( !kcf[nbHumans].init ) { // check if initialized
		kcf[nbHumans].ptr->init( (nbHumans ? kcf[nbHumans-1].ptr : 0) );
		kcf[nbHumans].init = TRUE;
	}
	kcf[nbHumans].ptr->insert();
}

void MPInterface::configureKeys(uint nbHumans)
{
	insertKeys(nbHumans);
	kcf[nbHumans].ptr->configure();
	kcf[nbHumans].ptr->remove();
}

void MPInterface::activateKeys(bool activate)
{
	// find the sending board
	uint i;
	for (i=0; i<boards.count(); i++) if ( sender()==boards[i].ptr ) break;
	int hi = boards[i].humanIndex;
	if ( hi==-1 ) return; // AI board (no keys)
	kcf[nbLocalHumans].ptr->activateBoard(hi, activate);
}

void MPInterface::configureKeys()
{
	kcf[nbLocalHumans].ptr->configure();
}

/*****************************************************************************/
/* Misc. functions                                                           */
/*****************************************************************************/
uint MPInterface::nbPlayers() const
{
	return internal->nbPlayers();
}

QString MPInterface::playerName(uint i) const
{
	ASSERT(server);
	return internal->playerName(i);
}

QDataStream &MPInterface::readingStream(uint i) const
{
	ASSERT(server);
	return internal->ioBuffer(i)->reading;
}

QDataStream &MPInterface::writingStream(uint i) const
{
	return internal->ioBuffer(i)->writing;
}

QDataStream &MPInterface::dataToClientsStream() const
{
	ASSERT(server);
	return *internal->globalStream();
}

void MPInterface::immediateWrite()
{
	internal->writeData(server);
}

void MPInterface::hostDisconnected(uint, const QString &msg)
{
	ERROR_BOX(msg, QString::null);
	
	if ( !disconnected ) { // to avoid multiple calls
		disconnected = TRUE;
		// the zero timer is used to be outside the "internal" class
		QTimer::singleShot(0, this, SLOT(singleHumanSlot()));
	}
}

void MPInterface::singleHumanSlot()
{
	disconnected = FALSE;
	singleHuman();
}

void MPInterface::paintEvent(QPaintEvent *e)
{
	QPainter p(this);
	p.fillRect(e->rect(), darkGray);
}
