//	Altirra - Atari 800/800XL/5200 emulator
//	Native device emulator - serial emulation options UI
//	Copyright (C) 2009-2015 Avery Lee
//
//	This program is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation; either version 2 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include <stdafx.h>
#include <setupapi.h>
#include <vd2/system/error.h>
#include <at/atnativeui/dialog.h>
#include <at/atnativeui/uiproxies.h>
#include "resource.h"
#include "serialconfig.h"

class ATSUIDialogSerialOptions final : public VDDialogFrameW32 {
public:
	ATSUIDialogSerialOptions(ATSSerialConfig& cfg);

protected:
	bool OnLoaded();
	void OnDataExchange(bool write);

	void FindSerialPorts();

	ATSSerialConfig& mConfig;

	VDUIProxyComboBoxControl mComboSerialPort;

	struct PortInfo {
		VDStringW mPath;
		VDStringW mFriendlyName;
		VDStringW mDesc;
	};

	vdvector<PortInfo> mPorts;
};

ATSUIDialogSerialOptions::ATSUIDialogSerialOptions(ATSSerialConfig& cfg)
	: VDDialogFrameW32(IDD_SERIAL_OPTIONS)
	, mConfig(cfg)
{
}

bool ATSUIDialogSerialOptions::OnLoaded() {
	AddProxy(&mComboSerialPort, IDC_SERIAL_PORT);

	FindSerialPorts();

	for(const auto& port : mPorts)
		mComboSerialPort.AddItem(port.mFriendlyName.c_str());

	mComboSerialPort.SetSelection(0);

	return VDDialogFrameW32::OnLoaded();
}

void ATSUIDialogSerialOptions::OnDataExchange(bool write) {
	if (write) {
		int index = mComboSerialPort.GetSelection();

		if (index >= 0 && (unsigned)index < mPorts.size())
			mConfig.mSerialPath = mPorts[index].mPath;
	} else {
		if (!mConfig.mSerialPath.empty()) {
			int index = 0;

			for(const PortInfo& port : mPorts) {
				if (!port.mPath.comparei(mConfig.mSerialPath)) {
					mComboSerialPort.SetSelection(index);
					break;
				}

				++index;
			}
		}
	}
}

void ATSUIDialogSerialOptions::FindSerialPorts() {
	HDEVINFO hdi = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_INTERFACEDEVICE | DIGCF_PRESENT);

	if (!hdi)
		return;

	vdblock<char> buf(4096);

	DWORD idx = 0;
	for(;;) {
		SP_DEVICE_INTERFACE_DATA data = {sizeof(SP_DEVICE_INTERFACE_DATA)};
		if (!SetupDiEnumDeviceInterfaces(hdi, NULL, &GUID_DEVINTERFACE_COMPORT, idx++, &data))
			break;

		PortInfo portInfo;

		((SP_DEVICE_INTERFACE_DETAIL_DATA *)buf.data())->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
		SP_DEVINFO_DATA spdd = {sizeof(SP_DEVINFO_DATA)};
		DWORD reqSize = 0;
		if (!SetupDiGetDeviceInterfaceDetail(hdi, &data, (SP_DEVICE_INTERFACE_DETAIL_DATA *)buf.data(), buf.size(), &reqSize, &spdd)) {
			if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
				continue;

			buf.resize(reqSize);
			((SP_DEVICE_INTERFACE_DETAIL_DATA *)buf.data())->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
			if (!SetupDiGetDeviceInterfaceDetail(hdi, &data, (SP_DEVICE_INTERFACE_DETAIL_DATA *)buf.data(), buf.size(), NULL, &spdd))
				continue;
		}

		portInfo.mPath = ((SP_DEVICE_INTERFACE_DETAIL_DATA *)buf.data())->DevicePath;

		DWORD type;
		DWORD reqsize;
		if (!SetupDiGetDeviceRegistryPropertyW(hdi, &spdd, SPDRP_DEVICEDESC, &type, (PBYTE)buf.data(), buf.size(), &reqsize) || type != REG_SZ)
			continue;

		portInfo.mDesc = (LPCWSTR)buf.data();

		if (!SetupDiGetDeviceRegistryPropertyW(hdi, &spdd, SPDRP_FRIENDLYNAME, &type, (PBYTE)buf.data(), buf.size(), &reqsize) || type != REG_SZ)
			continue;

		portInfo.mFriendlyName = (LPCWSTR)buf.data();

		mPorts.push_back(portInfo);
	}

	SetupDiDestroyDeviceInfoList(hdi);
}

bool ATSUIShowDialogSerialOptions(VDGUIHandle hParent, ATSSerialConfig& cfg) {
	ATSUIDialogSerialOptions dlg(cfg);

	return dlg.ShowDialog(hParent) != 0;
}
