//..............................................................................
//
// HID device lists
//

import "io_usb.jncx"
import "io_UsbDescriptors.jnc"
import "io_hid.jncx"
import "io_HidEnumerator.jnc"
import "io_AutoHidDb.jnc"
import "ui_ComboBox.jnc"
import "ui_PropertyGrid.jnc"
import "std_String.jnc"
import "std_Array.jnc"

namespace ui {

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

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

struct HidDeviceEntry {
	io.HidDeviceDesc const* m_deviceDesc;
	string_t m_description;
	string_t m_toolTip;
}

bool isHidDeviceLess(
	HidDeviceEntry const* device1,
	HidDeviceEntry const* device2
) {
	return device1.m_description < device2.m_description;
}

void refreshHidDeviceListUi(
	ComboBox* deviceCombo,
	EnumProperty* deviceProp
) {
	io.HidDeviceDesc const* prevDesc = deviceCombo.m_currentData;
	deviceCombo.clear();

	size_t count;
	io.HidDeviceDesc const* desc = io.enumerateHidDevices(&count);

	std.Array deviceArray;
	deviceArray.setCount(count);
	std.StringBuilder toolTip;
	for (size_t i = 0; i < count; i++, desc = desc.m_next) {
		io.HidUsagePage const* page = io.g_hidDb.getUsagePage(desc.m_usagePage);
		string_t usageName = page.getUsageName(desc.m_usage);

		HidDeviceEntry* entry = new HidDeviceEntry;
		entry.m_deviceDesc = desc;
		entry.m_description = $"%1 IF%2 (%3: %4)"(
			desc.m_product,
			desc.m_interfaceId,
			page.m_name,
			usageName
		);

		toolTip =
			"<table>"
			$"<tr><td>Device&nbsp;name:</td><td>%s</td></tr>"
			$"<tr><td>Description:</td><td>%s</td></tr>"
			$"<tr><td>Manufacturer:</td><td>%s</td></tr>"
			$"<tr><td>Vendor ID:</td><td>0x%04X</td></tr>"
			$"<tr><td>Product ID:</td><td>0x%04X</td></tr>"
			$"<tr><td>Bus:</td><td>%s</td></tr>"
			$"<tr><td>Interface:</td><td>%d</td></tr>"
			$"<tr><td>Usage&nbsp;page:</td><td>0x%02X - %s</td></tr>"
			$"<tr><td>Usage:</td><td>0x%02X - %s</td></tr>"(
				desc.m_path,
				desc.m_product,
				desc.m_manufacturer,
				desc.m_vendorId,
				desc.m_productId,
				typeof(io.HidBusType).getValueString(desc.m_busType),
				desc.m_interfaceId,
				desc.m_usagePage,
				page.m_name,
				desc.m_usage,
				usageName
			);

		if (desc.m_serialNumber)
			toolTip += $"<tr><td>Serial&nbsp;number:</td><td>%s</td></tr>"(desc.m_serialNumber);

		toolTip += "</table>";

		entry.m_toolTip = toolTip.detachString();
		deviceArray[i] = entry;
	}

	deviceArray.sort(isHidDeviceLess);

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

	for (size_t i = 0; i < count; i++, option++) {
		HidDeviceEntry* entry = deviceArray[i];
		size_t i = deviceCombo.addItem(entry.m_description, entry.m_deviceDesc);
		deviceCombo.m_itemToolTip[i] = entry.m_toolTip;
		option.m_text = entry.m_description;
		option.m_value = entry.m_deviceDesc;
	}

	deviceProp.setOptions(optionArray, count);

	if (prevDesc) {
		size_t index = findHidDeviceComboIndex(deviceCombo, prevDesc.m_path);
		if (index == -1)
			index = findHidDeviceComboIndex(
				deviceCombo,
				prevDesc.m_vendorId,
				prevDesc.m_productId,
				prevDesc.m_usagePage,
				prevDesc.m_usage
			);

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

size_t findHidDeviceComboIndex(
	ui.ComboBox* deviceCombo,
	string_t path
) {
	for (size_t i = 0; i < deviceCombo.m_itemCount; i++) {
		io.HidDeviceDesc const* desc = deviceCombo.m_itemData[i];
		if (desc && desc.m_path == path)
			return i;
	}

	return -1;
}

size_t findHidDeviceComboIndex(
	ui.ComboBox* deviceCombo,
	uint_t vid,
	uint_t pid,
	uint_t usagePage,
	uint_t usage
) {
	for (size_t i = 0; i < deviceCombo.m_itemCount; i++) {
		io.HidDeviceDesc const* desc = deviceCombo.m_itemData[i];
		if (desc &&
			desc.m_vendorId == vid &&
			desc.m_productId == pid &&
			desc.m_usagePage == usagePage &&
			desc.m_usage == usage
		)
			return i;
	}

	return -1;
}

size_t findHidDeviceComboIndex(
	ui.ComboBox* deviceCombo,
	uint_t vid,
	uint_t pid
) {
	for (size_t i = 0; i < deviceCombo.m_itemCount; i++) {
		io.HidDeviceDesc const* desc = deviceCombo.m_itemData[i];
		if (desc &&	desc.m_vendorId == vid && desc.m_productId == pid)
			return i;
	}

	return -1;
}

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

//! @}

} // namespace ui
