// SPDX-License-Identifier: GPL-2.0
#include <QShortcut>
#include <QDebug>
#include <QMessageBox>
#include <QMenu>
#include "core/btdiscovery.h"

#include <QBluetoothUuid>

#include "ui_btdeviceselectiondialog.h"
#include "btdeviceselectiondialog.h"

#if defined(Q_OS_WIN)
Q_DECLARE_METATYPE(QBluetoothDeviceDiscoveryAgent::Error)
#endif
#if QT_VERSION < 0x050500
Q_DECLARE_METATYPE(QBluetoothDeviceInfo)
#endif

BtDeviceSelectionDialog::BtDeviceSelectionDialog(QWidget *parent) :
	QDialog(parent),
	ui(new Ui::BtDeviceSelectionDialog),
	remoteDeviceDiscoveryAgent(0)
{
	ui->setupUi(this);

	// Quit button callbacks
	QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
	connect(quit, SIGNAL(activated()), this, SLOT(reject()));
	connect(ui->quit, SIGNAL(clicked()), this, SLOT(reject()));

	// Translate the UI labels
	ui->localDeviceDetails->setTitle(tr("Local Bluetooth device details"));
	ui->selectDeviceLabel->setText(tr("Select device:"));
	ui->deviceAddressLabel->setText(tr("Address:"));
	ui->deviceNameLabel->setText(tr("Name:"));
	ui->deviceState->setText(tr("Bluetooth powered on"));
	ui->changeDeviceState->setText(tr("Turn on/off"));
	ui->discoveredDevicesLabel->setText(tr("Discovered devices"));
	ui->scan->setText(tr("Scan"));
	ui->clear->setText(tr("Clear"));
	ui->save->setText(tr("Save"));
	ui->save->setDefault(true);
	ui->quit->setText(tr("Quit"));

	// Disable the save button because there is no device selected
	ui->save->setEnabled(false);

	// Add event for item selection
	connect(ui->discoveredDevicesList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
		this, SLOT(currentItemChanged(QListWidgetItem*,QListWidgetItem*)));

#if defined(Q_OS_WIN)
	ULONG       ulRetCode = SUCCESS;
	WSADATA     WSAData = { 0 };

	// Initialize WinSock and ask for version 2.2.
	ulRetCode = WSAStartup(MAKEWORD(2, 2), &WSAData);
	if (ulRetCode != SUCCESS) {
		QMessageBox::critical(this, "Bluetooth",
						   tr("Could not initialize Winsock version 2.2"), QMessageBox::Ok);
		return;
	}

	// Initialize the device discovery agent
	initializeDeviceDiscoveryAgent();

	// On Windows we cannot select a device or show information about the local device
	ui->localDeviceDetails->hide();
#else
	// Initialize the local Bluetooth device
	localDevice = new QBluetoothLocalDevice();

	// Populate the list with local bluetooth devices
	QList<QBluetoothHostInfo> localAvailableDevices = localDevice->allDevices();
	int availableDevicesSize = localAvailableDevices.size();

	if (availableDevicesSize > 1) {
		int defaultDeviceIndex = -1;

		for (int it = 0; it < availableDevicesSize; it++) {
			QBluetoothHostInfo localAvailableDevice = localAvailableDevices.at(it);
			ui->localSelectedDevice->addItem(localAvailableDevice.name(),
							 QVariant::fromValue(localAvailableDevice.address()));

			if (localDevice->address() == localAvailableDevice.address())
				defaultDeviceIndex = it;
		}

		// Positionate the current index to the default device and register to index changes events
		ui->localSelectedDevice->setCurrentIndex(defaultDeviceIndex);
		connect(ui->localSelectedDevice, SIGNAL(currentIndexChanged(int)),
			this, SLOT(localDeviceChanged(int)));
	} else {
		// If there is only one local Bluetooth adapter hide the combobox and the label
		ui->selectDeviceLabel->hide();
		ui->localSelectedDevice->hide();
	}

	// Update the UI information about the local device
	updateLocalDeviceInformation();

	// Initialize the device discovery agent
	if (localDevice->isValid())
		initializeDeviceDiscoveryAgent();
#endif
}

BtDeviceSelectionDialog::~BtDeviceSelectionDialog()
{
	delete ui;

#if defined(Q_OS_WIN)
	// Terminate the use of Winsock 2 DLL
	WSACleanup();
#else
	// Clean the local device
	delete localDevice;
#endif
	if (remoteDeviceDiscoveryAgent) {
		// Clean the device discovery agent
		if (remoteDeviceDiscoveryAgent->isActive()) {
			remoteDeviceDiscoveryAgent->stop();
#if defined(Q_OS_WIN)
			remoteDeviceDiscoveryAgent->wait();
#endif
		}

		delete remoteDeviceDiscoveryAgent;
	}
}

void BtDeviceSelectionDialog::on_changeDeviceState_clicked()
{
#if defined(Q_OS_WIN)
	// TODO add implementation
#else
	if (localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) {
		ui->dialogStatus->setText(tr("Trying to turn on the local Bluetooth device..."));
		localDevice->powerOn();
	} else {
		ui->dialogStatus->setText(tr("Trying to turn off the local Bluetooth device..."));
		localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);
	}
#endif
}

void BtDeviceSelectionDialog::on_save_clicked()
{
	// Get the selected device. There will be always a selected device if the save button is enabled.
	QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
	QBluetoothDeviceInfo remoteDeviceInfo = currentItem->data(Qt::UserRole).value<QBluetoothDeviceInfo>();

	// Save the selected device
	selectedRemoteDeviceInfo.reset(new QBluetoothDeviceInfo(remoteDeviceInfo));
	QString address = remoteDeviceInfo.address().isNull() ? remoteDeviceInfo.deviceUuid().toString() :
								remoteDeviceInfo.address().toString();
	saveBtDeviceInfo(address.toUtf8().constData(), remoteDeviceInfo);
	if (remoteDeviceDiscoveryAgent->isActive()) {
		// Stop the SDP agent if the clear button is pressed and enable the Scan button
		remoteDeviceDiscoveryAgent->stop();
#if defined(Q_OS_WIN)
		remoteDeviceDiscoveryAgent->wait();
#endif
		ui->scan->setEnabled(true);
	}

	// Close the device selection dialog and set the result code to Accepted
	accept();
}

void BtDeviceSelectionDialog::on_clear_clicked()
{
	ui->dialogStatus->setText(tr("Remote devices list was cleared."));
	ui->discoveredDevicesList->clear();

	if (remoteDeviceDiscoveryAgent->isActive()) {
		// Stop the SDP agent if the clear button is pressed and enable the Scan button
		remoteDeviceDiscoveryAgent->stop();
#if defined(Q_OS_WIN)
		remoteDeviceDiscoveryAgent->wait();
#endif
		ui->scan->setEnabled(true);
	}
}

void BtDeviceSelectionDialog::on_scan_clicked()
{
	ui->dialogStatus->setText(tr("Scanning for remote devices..."));
	ui->discoveredDevicesList->clear();
	remoteDeviceDiscoveryAgent->start();
	ui->scan->setEnabled(false);
}

void BtDeviceSelectionDialog::remoteDeviceScanFinished()
{
	if (remoteDeviceDiscoveryAgent->error() == QBluetoothDeviceDiscoveryAgent::NoError) {
		ui->dialogStatus->setText(tr("Scanning finished successfully."));
	} else {
		deviceDiscoveryError(remoteDeviceDiscoveryAgent->error());
	}

	ui->scan->setEnabled(true);
}

void BtDeviceSelectionDialog::hostModeStateChanged(QBluetoothLocalDevice::HostMode mode)
{
#if defined(Q_OS_WIN)
	// TODO add implementation
#else
	bool on = !(mode == QBluetoothLocalDevice::HostPoweredOff);

	//: %1 will be replaced with "turned on" or "turned off"
	ui->dialogStatus->setText(tr("The local Bluetooth device was %1.")
				  .arg(on? tr("turned on") : tr("turned off")));
	ui->deviceState->setChecked(on);
	ui->scan->setEnabled(on);
#endif
}

void BtDeviceSelectionDialog::addRemoteDevice(const QBluetoothDeviceInfo &remoteDeviceInfo)
{
#if defined(Q_OS_WIN)
	// On Windows we cannot obtain the pairing status so we set only the name and the address of the device
	QString deviceLabel = QString("%1 (%2)").arg(remoteDeviceInfo.name(),
						     remoteDeviceInfo.address().toString());
	QColor pairingColor = QColor(Qt::white);
#else
	// By default we use the status label and the color for the UNPAIRED state
	QColor pairingColor = QColor("#F1A9A0");
	QString pairingStatusLabel = tr("UNPAIRED");
	QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(remoteDeviceInfo.address());

	if (pairingStatus == QBluetoothLocalDevice::Paired) {
		pairingStatusLabel = tr("PAIRED");
		pairingColor = QColor(Qt::gray);
	} else if (pairingStatus == QBluetoothLocalDevice::AuthorizedPaired) {
		pairingStatusLabel = tr("AUTHORIZED_PAIRED");
		pairingColor = QColor("#89C4F4");
	}
	if (remoteDeviceInfo.address().isNull())
		pairingColor = QColor(Qt::gray);

	QString deviceLabel;

#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
	if (!remoteDeviceInfo.deviceUuid().isNull()) {
		// we have only a Uuid, no address, so show that and reset the pairing color
		deviceLabel = QString("%1 (%2)").arg(remoteDeviceInfo.name(),remoteDeviceInfo.deviceUuid().toString());
		pairingColor = QColor(Qt::white);
	} else
#endif
	deviceLabel = tr("%1 (%2)   [State: %3]").arg(remoteDeviceInfo.name(),
							      remoteDeviceInfo.address().toString(),
							      pairingStatusLabel);
#endif
	// Create the new item, set its information and add it to the list
	QListWidgetItem *item = new QListWidgetItem(deviceLabel);

	item->setData(Qt::UserRole, QVariant::fromValue(remoteDeviceInfo));
	item->setBackgroundColor(pairingColor);

	ui->discoveredDevicesList->addItem(item);
}

void BtDeviceSelectionDialog::currentItemChanged(QListWidgetItem *item, QListWidgetItem *)
{
	// If the list is cleared, we get a signal with a null item pointer
	if (!item) {
		ui->save->setEnabled(false);
		return;
	}

	// By default we assume that the devices are paired
	QBluetoothDeviceInfo remoteDeviceInfo = item->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
	QString statusMessage = tr("The device %1 can be used for connection. You can press the Save button.")
				  .arg(remoteDeviceInfo.address().isNull() ?
					       remoteDeviceInfo.deviceUuid().toString() :
					       remoteDeviceInfo.address().toString());
	bool enableSaveButton = true;

#if !defined(Q_OS_WIN)
	// On other platforms than Windows we can obtain the pairing status so if the devices are not paired we disable the button
	// except on MacOS for those devices that only give us a Uuid and not and address (as we have no pairing status for those, either)
	if (!remoteDeviceInfo.address().isNull()) {
		QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(remoteDeviceInfo.address());

		if (pairingStatus == QBluetoothLocalDevice::Unpaired) {
			statusMessage = tr("The device %1 must be paired in order to be used. Please use the context menu for pairing options.")
					.arg(remoteDeviceInfo.address().toString());
			enableSaveButton = false;
		}
	}
	if (remoteDeviceInfo.address().isNull() && remoteDeviceInfo.deviceUuid().isNull()) {
		statusMessage = tr("A device needs a non-zero address for a connection.");
		enableSaveButton = false;
	}
#endif
	// Update the status message and the save button
	ui->dialogStatus->setText(statusMessage);
	ui->save->setEnabled(enableSaveButton);
}

void BtDeviceSelectionDialog::localDeviceChanged(int index)
{
#if defined(Q_OS_WIN)
	// TODO add implementation
#else
	QBluetoothAddress localDeviceSelectedAddress = ui->localSelectedDevice->itemData(index, Qt::UserRole).value<QBluetoothAddress>();

	// Delete the old localDevice
	if (localDevice)
		delete localDevice;

	// Create a new local device using the selected address
	localDevice = new QBluetoothLocalDevice(localDeviceSelectedAddress);

	ui->dialogStatus->setText(tr("The local device was changed."));

	// Clear the discovered devices list
	on_clear_clicked();

	// Update the UI information about the local device
	updateLocalDeviceInformation();

	// Initialize the device discovery agent
	if (localDevice->isValid())
		initializeDeviceDiscoveryAgent();
#endif
}

void BtDeviceSelectionDialog::displayPairingMenu(const QPoint &pos)
{
#if defined(Q_OS_WIN)
	// TODO add implementation
#else
	QMenu menu(this);
	QAction *pairAction = menu.addAction(tr("Pair"));
	QAction *removePairAction = menu.addAction(tr("Remove pairing"));
	QAction *chosenAction = menu.exec(ui->discoveredDevicesList->viewport()->mapToGlobal(pos));
	QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
	QBluetoothDeviceInfo currentRemoteDeviceInfo = currentItem->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
	QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(currentRemoteDeviceInfo.address());

	//TODO: disable the actions
	if (pairingStatus == QBluetoothLocalDevice::Unpaired) {
		pairAction->setEnabled(true);
		removePairAction->setEnabled(false);
	} else {
		pairAction->setEnabled(false);
		removePairAction->setEnabled(true);
	}

	if (chosenAction == pairAction) {
		ui->dialogStatus->setText(tr("Trying to pair device %1")
					    .arg(currentRemoteDeviceInfo.address().toString()));
		localDevice->requestPairing(currentRemoteDeviceInfo.address(), QBluetoothLocalDevice::Paired);
	} else if (chosenAction == removePairAction) {
		ui->dialogStatus->setText(tr("Trying to unpair device %1")
					    .arg(currentRemoteDeviceInfo.address().toString()));
		localDevice->requestPairing(currentRemoteDeviceInfo.address(), QBluetoothLocalDevice::Unpaired);
	}
#endif
}

void BtDeviceSelectionDialog::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
{
	// Determine the color, the new pairing status and the log message. By default we assume that the devices are UNPAIRED.
	QString remoteDeviceStringAddress = address.toString();
	QColor pairingColor = QColor(Qt::red);
	QString pairingStatusLabel = tr("UNPAIRED");
	QString dialogStatusMessage = tr("Device %1 was unpaired.").arg(remoteDeviceStringAddress);
	bool enableSaveButton = false;

	if (pairing == QBluetoothLocalDevice::Paired) {
		pairingStatusLabel = tr("PAIRED");
		pairingColor = QColor(Qt::gray);
		enableSaveButton = true;
		dialogStatusMessage = tr("Device %1 was paired.").arg(remoteDeviceStringAddress);
	} else if (pairing == QBluetoothLocalDevice::AuthorizedPaired) {
		pairingStatusLabel = tr("AUTHORIZED_PAIRED");
		pairingColor = QColor(Qt::blue);
		enableSaveButton = true;
		dialogStatusMessage = tr("Device %1 was paired and is authorized.").arg(remoteDeviceStringAddress);
	}

	// Find the items which represent the BTH device and update their state
	QList<QListWidgetItem *> items = ui->discoveredDevicesList->findItems(remoteDeviceStringAddress, Qt::MatchContains);
	QRegularExpression pairingExpression = QRegularExpression(QString("%1|%2|%3").arg(tr("PAIRED"),
											  tr("AUTHORIZED_PAIRED"),
											  tr("UNPAIRED")));

	for (int i = 0; i < items.count(); ++i) {
		QListWidgetItem *item = items.at(i);
		QString updatedDeviceLabel = item->text().replace(QRegularExpression(pairingExpression),
								  pairingStatusLabel);

		item->setText(updatedDeviceLabel);
		item->setBackgroundColor(pairingColor);
	}

	// Check if the updated device is the selected one from the list and inform the user that it can/cannot start the download mode
	QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();

	if (currentItem != NULL && currentItem->text().contains(remoteDeviceStringAddress, Qt::CaseInsensitive)) {
		if (pairing == QBluetoothLocalDevice::Unpaired) {
			dialogStatusMessage = tr("The device %1 must be paired in order to be used. Please use the context menu for pairing options.")
						.arg(remoteDeviceStringAddress);
		} else {
			dialogStatusMessage = tr("The device %1 can now be used for connection. You can press the Save button.")
						.arg(remoteDeviceStringAddress);
		}
	}

	// Update the save button and the dialog status message
	ui->save->setEnabled(enableSaveButton);
	ui->dialogStatus->setText(dialogStatusMessage);
}

void BtDeviceSelectionDialog::error(QBluetoothLocalDevice::Error error)
{
	ui->dialogStatus->setText(tr("Local device error: %1.")
				    .arg((error == QBluetoothLocalDevice::PairingError)? tr("Pairing error. If the remote device requires a custom PIN code, "
											    "please try to pair the devices using your operating system. ")
										       : tr("Unknown error")));
}

void BtDeviceSelectionDialog::deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error error)
{
	QString errorDescription;

	switch (error) {
	case QBluetoothDeviceDiscoveryAgent::PoweredOffError:
		errorDescription = tr("The Bluetooth adaptor is powered off, power it on before doing discovery.");
		break;
	case QBluetoothDeviceDiscoveryAgent::InputOutputError:
		errorDescription = tr("Writing to or reading from the device resulted in an error.");
		break;
	default:
#if defined(Q_OS_WIN)
		errorDescription = remoteDeviceDiscoveryAgent->errorToString();
#else
		errorDescription = tr("An unknown error has occurred.");
#endif
		break;
	}

	ui->dialogStatus->setText(tr("Device discovery error: %1.").arg(errorDescription));
}

extern QString markBLEAddress(const QBluetoothDeviceInfo *device);
extern QString btDeviceAddress(const QBluetoothDeviceInfo *device, bool isBle);

QString BtDeviceSelectionDialog::getSelectedDeviceAddress()
{
	if (!selectedRemoteDeviceInfo)
		return QString();

	int btMode = ui->btMode->currentIndex();
	QBluetoothDeviceInfo *device = selectedRemoteDeviceInfo.data();

	switch (btMode) {
	case 0:		// Auto
	default:
		return markBLEAddress(device);
	case 1:		// Force LE
		return btDeviceAddress(device, true);
	case 2:		// Force classical
		return btDeviceAddress(device, false);
	}
}

QString BtDeviceSelectionDialog::getSelectedDeviceName()
{
	if (selectedRemoteDeviceInfo)
		return selectedRemoteDeviceInfo.data()->name();

	return QString();
}

QString BtDeviceSelectionDialog::getSelectedDeviceText()
{
	return formatDeviceText(getSelectedDeviceAddress(), getSelectedDeviceName());
}

QString BtDeviceSelectionDialog::formatDeviceText(const QString &address, const QString &name)
{
	if (address.isEmpty())
		return name;
	if (name.isEmpty())
		return address;
	return QString("%1 (%2)").arg(name, address);
}

void BtDeviceSelectionDialog::updateLocalDeviceInformation()
{
#if defined(Q_OS_WIN)
	// TODO add implementation
#else
	// Check if the selected Bluetooth device can be accessed
	if (!localDevice->isValid()) {
		QString na = tr("Not available");

		// Update the UI information
		ui->deviceAddress->setText(na);
		ui->deviceName->setText(na);

		// Announce the user that there is a problem with the selected local Bluetooth adapter
		ui->dialogStatus->setText(tr("The local Bluetooth adapter cannot be accessed."));

		// Disable the buttons
		ui->save->setEnabled(false);
		ui->scan->setEnabled(false);
		ui->clear->setEnabled(false);
		ui->changeDeviceState->setEnabled(false);

		return;
	}

	// Set UI information about the local device
	ui->deviceAddress->setText(localDevice->address().toString());
	ui->deviceName->setText(localDevice->name());

	connect(localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode)),
		this, SLOT(hostModeStateChanged(QBluetoothLocalDevice::HostMode)));

	// Initialize the state of the local device and activate/deactive the scan button
	hostModeStateChanged(localDevice->hostMode());

	// Add context menu for devices to be able to pair them
	ui->discoveredDevicesList->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(ui->discoveredDevicesList, SIGNAL(customContextMenuRequested(QPoint)),
		this, SLOT(displayPairingMenu(QPoint)));
	connect(localDevice, SIGNAL(pairingFinished(QBluetoothAddress, QBluetoothLocalDevice::Pairing)),
		this, SLOT(pairingFinished(QBluetoothAddress, QBluetoothLocalDevice::Pairing)));

	connect(localDevice, SIGNAL(error(QBluetoothLocalDevice::Error)),
		this, SLOT(error(QBluetoothLocalDevice::Error)));
#endif
}

void BtDeviceSelectionDialog::initializeDeviceDiscoveryAgent()
{
#if defined(Q_OS_WIN)
	// Register QBluetoothDeviceInfo metatype
	qRegisterMetaType<QBluetoothDeviceInfo>();

	// Register QBluetoothDeviceDiscoveryAgent metatype (Needed for QBluetoothDeviceDiscoveryAgent::Error)
	qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>();

	// Intialize the discovery agent
	remoteDeviceDiscoveryAgent = new WinBluetoothDeviceDiscoveryAgent(this);
#else
	// Intialize the discovery agent
	remoteDeviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(localDevice->address());

	// Test if the discovery agent was successfully created
	if (remoteDeviceDiscoveryAgent->error() == QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError) {
		ui->dialogStatus->setText(tr("The device discovery agent was not created because the %1 address does not "
					     "match the physical adapter address of any local Bluetooth device.")
					    .arg(localDevice->address().toString()));
		ui->scan->setEnabled(false);
		ui->clear->setEnabled(false);
		return;
	}
#endif
	connect(remoteDeviceDiscoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
		this, SLOT(addRemoteDevice(QBluetoothDeviceInfo)));
	connect(remoteDeviceDiscoveryAgent, SIGNAL(finished()),
		this, SLOT(remoteDeviceScanFinished()));
	connect(remoteDeviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
		this, SLOT(deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error)));
}

#if defined(Q_OS_WIN)
WinBluetoothDeviceDiscoveryAgent::WinBluetoothDeviceDiscoveryAgent(QObject *parent) : QThread(parent)
{
	// Initialize the internal flags by their default values
	running = false;
	stopped = false;
	lastError = QBluetoothDeviceDiscoveryAgent::NoError;
	lastErrorToString = tr("No error");
}

WinBluetoothDeviceDiscoveryAgent::~WinBluetoothDeviceDiscoveryAgent()
{
}

bool WinBluetoothDeviceDiscoveryAgent::isActive() const
{
	return running;
}

QString WinBluetoothDeviceDiscoveryAgent::errorToString() const
{
	return lastErrorToString;
}

QBluetoothDeviceDiscoveryAgent::Error WinBluetoothDeviceDiscoveryAgent::error() const
{
	return lastError;
}

void WinBluetoothDeviceDiscoveryAgent::run()
{
	// Initialize query for device and start the lookup service
	WSAQUERYSET queryset;
	HANDLE hLookup;
	int result = SUCCESS;

	running = true;
	lastError = QBluetoothDeviceDiscoveryAgent::NoError;
	lastErrorToString = tr("No error");

	memset(&queryset, 0, sizeof(WSAQUERYSET));
	queryset.dwSize = sizeof(WSAQUERYSET);
	queryset.dwNameSpace = NS_BTH;

	// The LUP_CONTAINERS flag is used to signal that we are doing a device inquiry
	// while LUP_FLUSHCACHE flag is used to flush the device cache for all inquiries
	// and to do a fresh lookup instead.
	result = WSALookupServiceBegin(&queryset, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup);

	if (result != SUCCESS) {
		// Get the last error and emit a signal
		lastErrorToString = qt_error_string();
		lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError;
		emit error(lastError);

		// Announce that the inquiry finished and restore the stopped flag
		running = false;
		stopped = false;

		return;
	}

	// Declare the necessary variables to collect the information
	BYTE buffer[4096];
	DWORD bufferLength = sizeof(buffer);
	WSAQUERYSET *pResults = (WSAQUERYSET*)&buffer;

	memset(buffer, 0, sizeof(buffer));

	pResults->dwSize = sizeof(WSAQUERYSET);
	pResults->dwNameSpace = NS_BTH;
	pResults->lpBlob = NULL;

	//Start looking for devices
	while (result == SUCCESS && !stopped){
		// LUP_RETURN_NAME and LUP_RETURN_ADDR flags are used to return the name and the address of the discovered device
		result = WSALookupServiceNext(hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &bufferLength, pResults);

		if (result == SUCCESS) {
			// Found a device
			QString deviceAddress(BTH_ADDR_BUF_LEN, Qt::Uninitialized);
			DWORD addressSize = BTH_ADDR_BUF_LEN;

			// Collect the address of the device from the WSAQUERYSET
			SOCKADDR_BTH *socketBthAddress = (SOCKADDR_BTH *) pResults->lpcsaBuffer->RemoteAddr.lpSockaddr;

			// Convert the BTH_ADDR to string
			if (WSAAddressToStringW((LPSOCKADDR) socketBthAddress,
						sizeof (*socketBthAddress),
						NULL,
						reinterpret_cast<wchar_t*>(deviceAddress.data()),
						&addressSize
						) != 0) {
				// Get the last error and emit a signal
				lastErrorToString = qt_error_string();
				lastError = QBluetoothDeviceDiscoveryAgent::UnknownError;
				emit(lastError);

				break;
			}

			// Remove the round parentheses
			deviceAddress.remove(')');
			deviceAddress.remove('(');

			// Save the name of the discovered device and truncate the address
			QString deviceName = QString(pResults->lpszServiceInstanceName);
			deviceAddress.truncate(BTH_ADDR_PRETTY_STRING_LEN);

			// Create an object with information about the discovered device
			QBluetoothDeviceInfo deviceInfo = QBluetoothDeviceInfo(QBluetoothAddress(deviceAddress), deviceName, 0);

			// Raise a signal with information about the found remote device
			emit deviceDiscovered(deviceInfo);
		} else {
			// Get the last error and emit a signal
			lastErrorToString = qt_error_string();
			lastError = QBluetoothDeviceDiscoveryAgent::UnknownError;
			emit(lastError);
		}
	}

	// Announce that the inquiry finished and restore the stopped flag
	running = false;
	stopped = false;

	// Restore the error status
	lastError = QBluetoothDeviceDiscoveryAgent::NoError;

	// End the lookup service
	WSALookupServiceEnd(hLookup);
}

void WinBluetoothDeviceDiscoveryAgent::stop()
{
	// Stop the inqury
	stopped = true;
}
#endif
