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

import "io_usb.jncx"
import "io_UsbEnumerator.jnc"
import "ui_ComboBox.jnc"
import "ui_PropertyGrid.jnc"
import "std_String.jnc"
import "std_Array.jnc"

namespace ui {

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

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

bool isUsbDeviceLess(
	io.UsbDeviceEntry const* entry1,
	io.UsbDeviceEntry const* entry2
) {
	return
		entry1.m_device.m_bus < entry2.m_device.m_bus ||
		entry1.m_device.m_bus == entry2.m_device.m_bus &&
		entry1.m_device.m_address < entry2.m_device.m_address;
}

void refreshUsbDeviceListUi(
	ComboBox* deviceCombo,
	EnumProperty* deviceProp
) {
	size_t count;
	io.UsbDeviceEntry const* deviceList = io.enumerateUsbDevices(io.UsbEnumFlags.All, &count);

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

	deviceArray.sort(isUsbDeviceLess);

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

	io.UsbDeviceEntry const* prevEntry = deviceCombo.m_currentData;
	deviceCombo.clear();

	std.StringBuilder description;
	std.StringBuilder toolTip;

	for (size_t i = 0; i < count; i++, option++) {
		io.UsbDeviceEntry const* entry = deviceArray[i];
		io.UsbDeviceDescriptor const* deviceDescriptor = entry.m_device.m_deviceDescriptor;

		description = $"%03d:%03d - %3 by %4"(
			entry.m_device.m_bus,
			entry.m_device.m_address,
			entry.m_description,
			entry.m_manufacturer
		);

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

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

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

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

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

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

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

		char const* text = description.detachString();
		size_t j = deviceCombo.addItem(text, entry);
		deviceCombo.m_itemToolTip[j] = toolTip.detachString();

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

	deviceProp.setOptions(optionArray, count);

	if (!prevEntry)
		return;

	size_t i = findUsbDeviceComboIndex(
		deviceCombo,
		prevEntry.m_device.m_deviceDescriptor.m_vendorId,
		prevEntry.m_device.m_deviceDescriptor.m_productId,
		prevEntry.m_device.m_bus,
		prevEntry.m_device.m_address
	);

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

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

	size_t itemCount = deviceCombo.m_itemCount;
	for (size_t i = 0; i < itemCount; i++) {
		io.UsbDeviceEntry const* entry = deviceCombo.m_itemData[i];
		if (entry &&
			entry.m_device.m_deviceDescriptor.m_vendorId == vid &&
			entry.m_device.m_deviceDescriptor.m_productId == pid
		) {
			if ((bus == -1 || entry.m_device.m_bus == bus) &&
				(address == -1 || entry.m_device.m_address == address))
				return i; // exact match

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

	return bestIndex;
}

size_t findUsbInterfaceComboIndex(
	ComboBox* interfaceCombo,
	uint_t interfaceId,
	uint_t altSettingId = -1
) {
	size_t bestIndex = -1;

	size_t itemCount = interfaceCombo.m_itemCount;
	for (size_t i = 0; i < itemCount; i++) {
		io.UsbInterfaceDescriptor const* ifaceDesc = interfaceCombo.m_itemData[i];
		if (ifaceDesc && ifaceDesc.m_interfaceId == interfaceId) {
			if (altSettingId == -1 || ifaceDesc.m_altSettingId == altSettingId)
				return i; // exact match

			bestIndex = i; // IID match, but different altSettingId
		}
	}

	return bestIndex;
}

size_t findUsbEndpointComboIndex(
	ComboBox* interfaceCombo,
	uint_t endpointId
) {
	size_t itemCount = interfaceCombo.m_itemCount;
	for (size_t i = 0; i < itemCount; i++) {
		io.UsbEndpointDescriptor const* epDesc = interfaceCombo.m_itemData[i];
		if (epDesc && epDesc.m_endpointId == endpointId)
			return i;
	}

	return -1;
}

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

//! @}

} // namespace ui
