//..............................................................................
//
// USB monitor device lists
//

import "io_usbmon.jncx"
import "io_UsbMonEnumerator.jnc"
import "ui_ComboBox.jnc"
import "ui_PropertyGrid.jnc"
import "std_String.jnc"
import "std_Array.jnc"

namespace ui {

//! \addtogroup common-ui
//! @{

//..............................................................................

bool isUsbMonDeviceLess(
	io.UsbMonDeviceDesc const* device1,
	io.UsbMonDeviceDesc const* device2
) {
	return
		device1.m_captureDeviceId < device2.m_captureDeviceId ||
		device1.m_captureDeviceId == device2.m_captureDeviceId &&
		device1.m_address < device2.m_address;
}

void refreshUsbMonDeviceListUi(
	ComboBox* deviceCombo,
	EnumProperty* deviceProp
) {
	size_t count;
	io.UsbMonDeviceDesc const* deviceList = io.enumerateUsbMonDevices(io.UsbMonEnumFlags.All, &count);

	std.Array deviceArray;
	deviceArray.setCount(count);
	io.UsbMonDeviceDesc const* device = deviceList;
	for (size_t i = 0; i < count; i++, device = device.m_next)
		deviceArray[i] = device;

	deviceArray.sort(isUsbMonDeviceLess);

	io.UsbMonDeviceDesc const* prevDevice = deviceCombo.m_currentData;

	EnumPropertyOption* optionArray = new EnumPropertyOption[count];
	EnumPropertyOption* option = optionArray;

	deviceCombo.clear();

	string_t captureDevicePrefix = sys.g_systemInfo.m_osKind == sys.OsKind.Windows ?
		"USBPcap" :
		"usbmon";

	std.StringBuilder description;
	std.StringBuilder toolTip;

	for (size_t i = 0; i < count; i++, option++) {
		io.UsbMonDeviceDesc const* device = deviceArray[i];

		string_t product = device.m_productDescriptor ? device.m_productDescriptor : device.m_description;
		string_t manufacturer = device.m_manufacturerDescriptor ? device.m_manufacturerDescriptor : device.m_manufacturer;

		description = $"%1%2:%03d - %4 by %5"(
			captureDevicePrefix,
			device.m_captureDeviceId,
			device.m_address,
			product,
			manufacturer
		);

		toolTip =
			"<table>"
			$"<tr><td>Capture&nbsp;device:</td><td>%1%2:%(3; 03d)</td></tr>"
			$"<tr><td>Description:</td><td>%4</td></tr>"
			$"<tr><td>Manufacturer:</td><td>%5</td></tr>"
			$"<tr><td>Vendor&nbsp;ID:</td><td>0x%(6; 04X)</td></tr>"
			$"<tr><td>Product&nbsp;ID:</td><td>0x%(7; 04X)</td></tr>"
			$"<tr><td>Class:</td><td>%8</td></tr>"
			$"<tr><td>Speed:</td><td>%9</td></tr>"(
				captureDevicePrefix,
				device.m_captureDeviceId,
				device.m_address,
				device.m_description,
				device.m_manufacturer,
				device.m_vendorId,
				device.m_productId,
				typeof(io.UsbClass).getValueString(device.m_class),
				typeof(io.UsbSpeed).getValueString(device.m_speed)
			);

		if (device.m_driver)
			toolTip += $"<tr><td>Driver:</td><td>$(device.m_driver)</td></tr>";

		if (device.m_manufacturerDescriptor || device.m_productDescriptor || device.m_serialNumberDescriptor) {
			toolTip += $"<tr><td colspan=2><hr></td></tr>";

			if (device.m_productDescriptor)
				toolTip += $"<tr><td>Product:</td><td>$(device.m_productDescriptor)</td></tr>";

			if (device.m_manufacturerDescriptor)
				toolTip += $"<tr><td>Manufacturer:</td><td>$(device.m_manufacturerDescriptor)</td></tr>";

			if (device.m_serialNumberDescriptor)
				toolTip += $"<tr><td>Serial&nbsp;number:</td><td>$(device.m_serialNumberDescriptor)</td></tr>";
		}

		toolTip.append("</table>");

		string_t text = description.detachString();
		size_t j = deviceCombo.addItem(text, device);
		deviceCombo.m_itemToolTip[j] = toolTip.detachString();

		option.m_text = text;
		option.m_value = device;
	}

	deviceProp.setOptions(optionArray, count);

	if (!prevDevice)
		return;

	size_t i = findUsbMonDeviceComboIndex(
		deviceCombo,
		prevDevice.m_vendorId,
		prevDevice.m_productId,
		prevDevice.m_captureDeviceId,
		prevDevice.m_address
	);

	deviceCombo.m_currentIndex = i != -1 ? i : 0;
}

size_t findUsbMonDeviceComboIndex(
	ComboBox* deviceCombo,
	uint_t vid,
	uint_t pid,
	uint_t captureDeviceId = -1,
	uint_t address = -1
) {
	size_t bestIndex = -1;

	size_t itemCount = deviceCombo.m_itemCount;
	for (size_t i = 0; i < itemCount; i++) {
		io.UsbMonDeviceDesc const* device = deviceCombo.m_itemData[i];
		if (device &&
			device.m_vendorId == vid &&
			device.m_productId == pid
		) {
			if ((captureDeviceId == -1 || device.m_captureDeviceId == captureDeviceId) &&
				(address == -1 || device.m_address == address))
				return i; // exact match

			bestIndex = i; // VID/PID match, but different address
		}
	}

	return bestIndex;
}

//..............................................................................

//! @}

} // namespace ui
