#include "multypo.h"

#include <QX11Info>
#include <QDebug>
#include <QVBoxLayout>
#include <QLabel>
#include <QMap>
#include <QTextDocument>

#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include <X11/Xutil.h>

MultypoWindow::MultypoWindow(QWidget *parent) :
	QWidget(parent),
    state(Naming),
	xiInited(false)
{
	/* Prepare colors */
	setStyleSheet("background-color: black; font-size: 45pt");

	/* Prepare conents */
	setLayout(new QVBoxLayout(this));
	mainLabel = new QLabel(this);
    mainLabel->setTextFormat(Qt::RichText);
    setLabel("Namen eingeben, dann Return", BASE_COLOR);
	layout()->addWidget(mainLabel);

	/* Fullscreen, no cursor */
	setWindowState(Qt::WindowFullScreen);
	setCursor(QCursor(Qt::BlankCursor));

	words.open(stdin,QIODevice::ReadOnly);
}

MultypoWindow::~MultypoWindow()
{
}

void MultypoWindow::setLabel(QString text, QString color)
{
    mainLabel->setText(QString("<font color=%1>%2</span>").arg(color).arg(Qt::escape(text)));
}

void MultypoWindow::nextWord() {
	QByteArray tmp = words.readLine().trimmed();
	currentWord = QString::fromUtf8(tmp);
	qDebug() << "New word:" << currentWord;
	if (currentWord.isEmpty()) { // game over
		setLabel("GAME OVER", READY_COLOR);
		for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
			it.value()->showScore();
		}
		state = Scoring;
	} else {
		for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
            it.value()->nextWord();
        }
		setLabel(currentWord, BASE_COLOR);
        state = Playing;
	}
}

bool MultypoWindow::allPlayersWaiting()
{
    for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
        if (it.value()->getState() != Player::Waiting) return false;
    }
    return true;
}

int MultypoWindow::typingPlayers()
{
    int n = 0;
    for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
        if (it.value()->getState() == Player::Typing) ++n;
    }
    return n;
}

void MultypoWindow::handleKeyPress(int device, QString string)
{
	qDebug() << "Device" << device << "String" << string;

	if (string == "Escape") {
		close();
		return;
	}

	if (!players.contains(device)) {
		if (state > Naming)
			return;
		players[device] = new Player(this);
	}
	Player *player = players[device];
	player->handleKey(string);
    
    if (state == Naming) {
        // someone's still naming (or nobody's there yet)
        if (!players.empty() && allPlayersWaiting()) {
            qDebug() << "Game starting";
            nextWord();
        }
    }
    else if (state == Playing) { // all players are waiting or typing
		if (player->getState() == Player::Typing && player->getCurrentWord() == currentWord) {
            int points = typingPlayers();
            player->wordComplete(points);
			QString readyString = QString("READY: %1 points").arg(points);
			if (allPlayersWaiting()) {
				nextWord();
			}
		}
	}
}

bool MultypoWindow::handleX11Event(XEvent *event)
{
	Display *dpy = x11Info().display();
	/* Handle the first map event: We are finally visible! */
	if (!xiInited && event->type == MapNotify) {
		/* XInput Extension available? */
		int eventID, errorID;
		if (!XQueryExtension(dpy, "XInputExtension", &xiOpcode, &eventID, &errorID)) {
		   qCritical() << "X Input extension not available";
		}

		/* Which version of XI2? We support 2.0 */
		int major = 2, minor = 1;
		if (XIQueryVersion(dpy, &major, &minor) == BadRequest) {
			qCritical() << "XI2 not available. Server supports" << major << "." <<
						minor;
		}

		qDebug() << "System supports XI2";


		// Now let's listen to keypress events
		XIEventMask eventmask;
		unsigned char mask [1] = { 0 }; // will change
		eventmask.deviceid = XIAllMasterDevices;
		eventmask.mask_len = sizeof (mask); // in bytes, for whatever reason...
		eventmask.mask = mask;
		XISetMask(mask, XI_KeyPress);
		XISelectEvents (dpy, winId(), &eventmask, 1);

		// finally, grab all focuses
		int ndevices;
		XIDeviceInfo* devices = XIQueryDevice(dpy, XIAllMasterDevices, &ndevices);
		for (int i = 0; i < ndevices; ++i) {
			if (devices[i].use != XIMasterKeyboard) continue;
			qDebug() << "Found master keyboard with ID" << devices[i].deviceid;
			XISetFocus(dpy, devices[i].deviceid, winId(), CurrentTime);
		}
		XIFreeDeviceInfo(devices);

		xiInited = true;

		return false;
	}
	else if (xiInited && event->type == GenericEvent
		&& event->xcookie.type == GenericEvent && event->xcookie.extension == xiOpcode
		&& event->xcookie.evtype == XI_KeyPress) { 
		
		if (XGetEventData(dpy, &event->xcookie) != True) {
			qCritical() << "Error getting event data";
			// FIXME return true;
		}
		
		/* Handle XI event */
		XIDeviceEvent *d_ev = (XIDeviceEvent*) event->xcookie.data;
	    KeyCode keycode = d_ev->detail;
	    int kbdid = d_ev->deviceid;

		if (!(d_ev->flags & XIKeyRepeat)) {
			int keysyms_per_keycode;
			KeySym keysym = XGetKeyboardMapping (dpy, keycode, 1, &keysyms_per_keycode)[0];
			handleKeyPress(kbdid, XKeysymToString(keysym));
		}

		XFreeEventData(dpy, &event->xcookie);

		return true;
	}
	else {
		return false;
	}
}
