#include "stdafx.h"

#include "QtLocalPeer.h"
#include <QCoreApplication>
#include <QTime>

#include <QLibrary>
#include <qt_windows.h>
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
static PProcessIdToSessionId pProcessIdToSessionId = 0;


const char* QtLocalPeer::ack = "ack";

QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
	: QObject(parent), id(appId)
{
	QString prefix = id;
	if (id.isEmpty())
	{
		id = QCoreApplication::applicationFilePath();
		id = id.toLower();
		prefix = id.section(QLatin1Char('/'), -1);
	}

	prefix.remove(QRegExp("[^a-zA-Z]"));
	prefix.truncate(6);

	QByteArray idc = id.toUtf8();
	quint16 idNum = qChecksum(idc.constData(), idc.size());
	socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16);

	if (!pProcessIdToSessionId)
	{
		QLibrary lib("kernel32");
		pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
	}

	if (pProcessIdToSessionId)
	{
		DWORD sessionId = 0;
		pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
		socketName += QLatin1Char('-') + QString::number(sessionId, 16);
	}

	server = new QLocalServer(this);
	QString lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + socketName + QLatin1String("-lockfile");
	lockFile.setFileName(lockName);
	lockFile.open(QIODevice::ReadWrite);
}



bool QtLocalPeer::isClient()
{
	if (lockFile.isLocked())
		return false;

	if (!lockFile.lock(QtLockedFile::WriteLock, false))
		return true;

	if (!server->listen(socketName))
		qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));

	QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
	return false;
}


bool QtLocalPeer::sendMessage(const QString &message, int timeout)
{
	if (!isClient())
		return false;

	QLocalSocket socket;
	bool connOk = false;
	for(int i = 0; i < 2; i++)
	{
		// Try twice, in case the other instance is just starting up
		socket.connectToServer(socketName);
		connOk = socket.waitForConnected(timeout/2);
		if (connOk || i)
			break;

		Sleep(DWORD(250));
	}

	if (!connOk)
		return false;

	QByteArray uMsg(message.toUtf8());
	QDataStream ds(&socket);
	ds.writeBytes(uMsg.constData(), uMsg.size());

	if (!socket.waitForBytesWritten(timeout))
		return false;

	// wait for ack
	if (!socket.waitForReadyRead(timeout))
		return false;

	if (socket.read(qstrlen(ack)) != ack)
		return false;

	return true;
}


void QtLocalPeer::receiveConnection()
{
	QLocalSocket* socket = server->nextPendingConnection();
	if (!socket)
		return;

	while (socket->bytesAvailable() < (int)sizeof(quint32))
		socket->waitForReadyRead();

	QDataStream ds(socket);

	quint32 remaining;
	ds >> remaining;

	QByteArray uMsg;
	uMsg.resize(remaining);

	int got = 0;
	char* uMsgBuf = uMsg.data();

	do
	{
		got = ds.readRawData(uMsgBuf, remaining);
		remaining -= got;
		uMsgBuf += got;
	}
	while (remaining && got >= 0 && socket->waitForReadyRead(2000));

	if (got < 0)
	{
		qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
		delete socket;
		return;
	}

	QString message(QString::fromUtf8(uMsg));
	socket->write(ack, qstrlen(ack));
	socket->waitForBytesWritten(1000);
	socket->waitForDisconnected(1000);
	delete socket;

	emit messageReceived(message);
}
