/*
 * Win_QrfeDeviceChangeDetector.cpp
 *
 *  Created on: 25.11.2008
 *      Author: stefan.detter
 */

#include "../inc/Win_QrfeDeviceChangeDetector.h"

#include <QCoreApplication>

#include <dbt.h>
#include <guiddef.h>
#include <initguid.h>
#include <usbiodef.h>
#include <hidclass.h>

#if QRFE_DEVICEDETECTOR_DEBUG == 0
#define trc(x,y)		{}
#endif


Win_QrfeDeviceChangeDetector::Win_QrfeDeviceChangeDetector()
#ifdef QRFE_DEVICEDETECTOR_DEBUG
: QrfeTraceModule("CWinDeviceChangeDetecter")
#endif
{
	m_notifyDeviceInterface_USB = INVALID_HANDLE_VALUE;
	m_notifyDeviceInterface_HID = INVALID_HANDLE_VALUE;
}

Win_QrfeDeviceChangeDetector::~Win_QrfeDeviceChangeDetector()
{

}

bool Win_QrfeDeviceChangeDetector::registerNotification()
{
	// Register device notification
	DEV_BROADCAST_DEVICEINTERFACE deviceFilter =
	{ 0 };

	ZeroMemory(&deviceFilter, sizeof(deviceFilter));
	deviceFilter.dbcc_size = sizeof(deviceFilter);
	deviceFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

	deviceFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
	m_notifyDeviceInterface_USB = RegisterDeviceNotification((HANDLE)this->winId(),
			&deviceFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
	if (m_notifyDeviceInterface_USB == 0)
	{
		trc(1, "Could not register device notifictaion for usb devices.");
		trc(1, "The notify handle: " + QString::number((ulong)m_notifyDeviceInterface_USB));
		trc(1, "The last error: " + QString::number((ulong)GetLastError()));
		return false;
	}
	trc(9, "Device notification for USB devices registered.");

	deviceFilter.dbcc_classguid = GUID_DEVINTERFACE_HID;
	m_notifyDeviceInterface_HID = RegisterDeviceNotification((HANDLE)this->winId(),
			&deviceFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
	if (m_notifyDeviceInterface_HID == 0)
	{
		trc(1, "Could not register device notifictaion for hid devices.");
		trc(1, "The notify handle: " + QString::number((ulong)m_notifyDeviceInterface_HID));
		trc(1, "The last error: " + QString::number((ulong)GetLastError()));
		return false;
	}
	trc(9, "Device notification for HID devices registered.");

	return true;
}

bool Win_QrfeDeviceChangeDetector::unregisterNotification()
{
	// Unegister device notification
	if (NULL != m_notifyDeviceInterface_USB)
	{
		UnregisterDeviceNotification(m_notifyDeviceInterface_USB);
		m_notifyDeviceInterface_USB = INVALID_HANDLE_VALUE;
	}

	if (NULL != m_notifyDeviceInterface_HID)
	{
		UnregisterDeviceNotification(m_notifyDeviceInterface_HID);
		m_notifyDeviceInterface_HID = INVALID_HANDLE_VALUE;
	}

	return true;
}

bool Win_QrfeDeviceChangeDetector::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
	trc(10, "NativeEvent: " + eventType.toHex());
	return winEvent((MSG*)message, result);
}

bool Win_QrfeDeviceChangeDetector::winEvent(MSG * msg, long * /*result*/)
{
	static quint32 count = 0;

	if (msg->message == WM_DEVICECHANGE)
	{

		trc(10, "Msg: "
				"message(" + QString::number(msg->message) + ") / "
				"wParam (" + QString::number((quint64)msg->wParam) + ") / "
				"lParam (" + QString::number((quint64)msg->lParam) + ") / "
				"handle (" + QString::number((quint64)msg->hwnd) + ") / "
				"time (" + QString::number(msg->time) + ") / "
		);
		switch (msg->wParam)
		{
		case DBT_CONFIGCHANGECANCELED:
			trc(10, "DBT_CONFIGCHANGECANCELED");
			break;
		case DBT_CONFIGCHANGED:
			trc(10, "DBT_CONFIGCHANGED");
			break;
		case DBT_CUSTOMEVENT:
			trc(10, "DBT_CUSTOMEVENT");
			break;
		case DBT_DEVICEARRIVAL:
		{
			trc(9, "DBT_DEVICEARRIVAL");
			DEV_BROADCAST_HDR* hdr = (DEV_BROADCAST_HDR*) msg->lParam;
			switch (hdr->dbch_devicetype)
			{
			case DBT_DEVTYP_DEVICEINTERFACE:
			{
				trc(9, "--DBT_DEVTYP_DEVICEINTERFACE");
				DEV_BROADCAST_DEVICEINTERFACE* inter =
						(DEV_BROADCAST_DEVICEINTERFACE*) hdr;

				QString name = QString::fromWCharArray(inter->dbcc_name).toLower();
				bool ok;
				quint16 vendorID =
						name.mid(name.indexOf("vid_") + 4, 4).toUInt(&ok, 16);
				if (!ok)
					return false;
				quint16 productID =
						name.mid(name.indexOf("pid_") + 4, 4).toUInt(&ok, 16);
				if (!ok)
					return false;

				trc(9, "---- " + name);

				if (GUID_DEVINTERFACE_USB_DEVICE == inter->dbcc_classguid)
				{
					trc(9, "------- USB Device was detected...");
					emit usbDeviceAttached(name, vendorID, productID);
				}
				else if (GUID_DEVINTERFACE_HID == inter->dbcc_classguid)
				{
					trc(9, "------- HID Device was detected...");
					emit hidDeviceAttached(name, vendorID, productID);
				}
				else
					return false;

				trc(9, "--------- with the VendorID: " + name.mid(name.indexOf("vid_") + 4, 4));
				trc(9, "--------- and the ProductID: " + name.mid(name.indexOf("pid_") + 4, 4));

				break;
			}
			case DBT_DEVTYP_HANDLE:
				trc(9, "--DBT_DEVTYP_HANDLE");
				break;
			case DBT_DEVTYP_OEM:
				trc(9, "--DBT_DEVTYP_OEM");
				break;
			case DBT_DEVTYP_PORT:
			{
				trc(9, "--DBT_DEVTYP_PORT");
				DEV_BROADCAST_PORT* port = (DEV_BROADCAST_PORT*) hdr;
				trc(9, "---- " + QString::fromWCharArray(port->dbcp_name));
                emit serialPortAttached(QString::fromWCharArray(port->dbcp_name));
				port = 0;
				break;
			}
			case DBT_DEVTYP_VOLUME:
				trc(9, "--DBT_DEVTYP_VOLUME");
				break;
			}
			break;
		}
		case DBT_DEVICEQUERYREMOVE:
			trc(10, "DBT_DEVICEQUERYREMOVE");
			break;
		case DBT_DEVICEQUERYREMOVEFAILED:
			trc(10, "DBT_DEVICEQUERYREMOVEFAILED");
			break;
		case DBT_DEVICEREMOVECOMPLETE:
		{
			trc(9, "DBT_DEVICEREMOVECOMPLETE");
			DEV_BROADCAST_HDR* hdr = (DEV_BROADCAST_HDR*) msg->lParam;
			switch (hdr->dbch_devicetype)
			{
			case DBT_DEVTYP_DEVICEINTERFACE:
			{
				trc(9, "--DBT_DEVTYP_DEVICEINTERFACE");
				DEV_BROADCAST_DEVICEINTERFACE* inter =
						(DEV_BROADCAST_DEVICEINTERFACE*) hdr;

				QString name = QString::fromWCharArray(inter->dbcc_name).toLower();
				trc(9, "---- " + name);
				bool ok;
				quint16 vendorID = name.mid(name.indexOf("vid_", 0,
						Qt::CaseInsensitive) + 4, 4).toUInt(&ok, 16);
				if (!ok)
					return false;
				quint16 productID = name.mid(name.indexOf("pid_", 0,
						Qt::CaseInsensitive) + 4, 4).toUInt(&ok, 16);
				if (!ok)
					return false;

				if (GUID_DEVINTERFACE_USB_DEVICE == inter->dbcc_classguid)
				{
					trc(9, "------- USB Device was removed...");
					emit usbDeviceRemoved(name, vendorID, productID);
				}
				else if (GUID_DEVINTERFACE_HID == inter->dbcc_classguid)
				{
					trc(9, "------- HID Device was removed...");
					emit hidDeviceRemoved(name, vendorID, productID);
				}
				else
					return false;

				trc(9, "--------- with the VendorID: " + name.mid(name.indexOf("vid_") + 4, 4));
				trc(9, "--------- and the ProductID: " + name.mid(name.indexOf("pid_") + 4, 4));

				break;
			}
			case DBT_DEVTYP_HANDLE:
				trc(9, "--DBT_DEVTYP_HANDLE");
				break;
			case DBT_DEVTYP_OEM:
				trc(9, "--DBT_DEVTYP_OEM");
				break;
			case DBT_DEVTYP_PORT:
			{
				trc(9, "--DBT_DEVTYP_PORT");
				DEV_BROADCAST_PORT* port = (DEV_BROADCAST_PORT*) hdr;
                trc(9, "---- " + QString::fromWCharArray(port->dbcp_name));
                emit serialPortRemoved(QString::fromWCharArray(port->dbcp_name));
                port = 0;
				break;
			}
			case DBT_DEVTYP_VOLUME:
				trc(9, "--DBT_DEVTYP_VOLUME");
				break;
			}
			break;
		}
		case DBT_DEVICEREMOVEPENDING:
			trc(10, "DBT_DEVICEREMOVEPENDING");
			break;
		case DBT_DEVICETYPESPECIFIC:
			trc(10, "DBT_DEVICETYPESPECIFIC");
			break;
		case DBT_DEVNODES_CHANGED:
			trc(10, "DBT_DEVNODES_CHANGED");
			break;
		case DBT_QUERYCHANGECONFIG:
			trc(10, "DBT_QUERYCHANGECONFIG");
			break;
		case DBT_USERDEFINED:
			trc(10, "DBT_USERDEFINED");
			break;

		default:
			trc(10, "default");
			break;
		}
	}

	return false;
}

