// RfCommClientDlg.cpp : implementation file
//

#include "stdafx.h"
#include "RfCommClient.h"
#include "RfCommClientDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CRfCommClientDlg dialog




CRfCommClientDlg::CRfCommClientDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CRfCommClientDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CRfCommClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_DEVICES, lvDevices);
	DDX_Control(pDX, IDC_EDIT_TIMEOUT, edTimeout);
	DDX_Control(pDX, IDC_CHECK_AUTHENTICATION, cbAuthentication);
	DDX_Control(pDX, IDC_CHECK_ENCRYPTION, cbEncryption);
	DDX_Control(pDX, IDC_EDIT_TEXT, edText);
	DDX_Control(pDX, IDC_EDIT_READ_BUFFER, edReadBuffer);
	DDX_Control(pDX, IDC_EDIT_WRITE_BUFFER, edWriteBuffer);
	DDX_Control(pDX, IDC_LIST_EVENTS, lbEvents);
	DDX_Control(pDX, IDC_CHECK_SERVICE_NAME, cbServiceName);
	DDX_Control(pDX, IDC_EDIT_SERVICE_NAME, edServiceName);
	DDX_Control(pDX, IDC_CHECK_ENUM_PAIRED, cbEnumPaired);
}

BEGIN_MESSAGE_MAP(CRfCommClientDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_BUTTON_DISCOVER, &CRfCommClientDlg::OnBnClickedButtonDiscover)
	ON_BN_CLICKED(IDC_BUTTON_CLEAR, &CRfCommClientDlg::OnBnClickedButtonClear)
	ON_BN_CLICKED(IDC_BUTTON_CONNECT, &CRfCommClientDlg::OnBnClickedButtonConnect)
	ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, &CRfCommClientDlg::OnBnClickedButtonDisconnect)
	ON_BN_CLICKED(IDC_BUTTON_SEND, &CRfCommClientDlg::OnBnClickedButtonSend)
	ON_BN_CLICKED(IDC_BUTTON_SET_BUFFERS, &CRfCommClientDlg::OnBnClickedButtonSetBuffers)
	ON_BN_CLICKED(IDC_BUTTON_GET_BUFFERS, &CRfCommClientDlg::OnBnClickedButtonGetBuffers)
	ON_BN_CLICKED(IDC_BUTTON_PAIR, &CRfCommClientDlg::OnBnClickedButtonPair)
	ON_BN_CLICKED(IDC_BUTTON_UNPAIR, &CRfCommClientDlg::OnBnClickedButtonUnpair)
END_MESSAGE_MAP()


// CRfCommClientDlg message handlers

BOOL CRfCommClientDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here

	__hook(&CwclBluetoothManager::OnNumericComparison, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerNumericComparison);
	__hook(&CwclBluetoothManager::OnPasskeyNotification, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerPasskeyNotification);
	__hook(&CwclBluetoothManager::OnPasskeyRequest, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerPasskeyRequest);
	__hook(&CwclBluetoothManager::OnPinRequest, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerPinRequest);
	__hook(&CwclBluetoothManager::OnConfirm, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerConfirm);
	__hook(&CwclBluetoothManager::OnAuthenticationCompleted, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerAuthenticationCompleted);
	__hook(&CwclBluetoothManager::OnDeviceFound, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerDeviceFound);
	__hook(&CwclBluetoothManager::OnDiscoveringCompleted, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerDiscoveringCompleted);
	__hook(&CwclBluetoothManager::OnDiscoveringStarted, &wclBluetoothManager,
		&CRfCommClientDlg::wclBluetoothManagerDiscoveringStarted);

	__hook(&CwclRfCommClient::OnConnect, &wclRfCommClient, &CRfCommClientDlg::wclRfCommClientConnect);
	__hook(&CwclRfCommClient::OnData, &wclRfCommClient, &CRfCommClientDlg::wclRfCommClientData);
	__hook(&CwclRfCommClient::OnDisconnect, &wclRfCommClient, &CRfCommClientDlg::wclRfCommClientDisconnect);

	lvDevices.InsertColumn(0, _T("Address"), 0, 150);
	lvDevices.InsertColumn(1, _T("Name"), 0, 150);
	lvDevices.InsertColumn(2, _T("Device type"), 0, 150);
	lvDevices.SetExtendedStyle(lvDevices.GetExtendedStyle() | LVS_EX_FULLROWSELECT);

	edTimeout.SetWindowText(_T("10000"));

	edText.SetWindowText(_T("Send this text to selected client"));

	edServiceName.SetWindowText(_T("Bluetooth Framework server"));

	edReadBuffer.SetWindowText(_T("0"));
	edWriteBuffer.SetWindowText(_T("0"));

	// In real application you should always analize the result code.
	// In this demo we assume that all is always OK.
	wclBluetoothManager.Open();

	cbAuthentication.SetCheck(wclRfCommClient.Authentication);
	cbEncryption.SetCheck(wclRfCommClient.Encryption);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CRfCommClientDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CRfCommClientDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

CwclBluetoothRadio* CRfCommClientDlg::GetRadio() const
{
	CwclBluetoothRadio* Radio;
	int Res = wclBluetoothManager.GetClassicRadio(Radio);
	if (Res != WCL_E_SUCCESS)
	{
		AfxMessageBox(_T("Get working radio failed"));
		return NULL;
	}
	return Radio;
}

void CRfCommClientDlg::OnDestroy()
{
	CDialog::OnDestroy();

	wclRfCommClient.Disconnect();
	wclBluetoothManager.Close();

	// On Visual Studio 2015 and above the __unhook() poroduces warning C4458
	// We we have to supress it. By some reason it can not be disabled by pragma
	// so we disabled it in the project settings.
	__unhook(&wclBluetoothManager);
	__unhook(&wclRfCommClient);
}

void CRfCommClientDlg::OnBnClickedButtonDiscover()
{
	CwclBluetoothRadio* Radio = GetRadio();
	if (Radio != NULL)
	{
		if (cbEnumPaired.GetCheck())
		{
			wclBluetoothAddresses Devices;
			int Res = Radio->EnumPairedDevices(Devices);
			if (Res != WCL_E_SUCCESS)
				AfxMessageBox(_T("Enum paired devices failed: 0x") + IntToHex(Res));
			else
			{
				lvDevices.DeleteAllItems();
				if (Devices.size() > 0)
				{
					for (wclBluetoothAddresses::iterator i = Devices.begin(); i != Devices.end(); i++)
						wclBluetoothManagerDeviceFound(&wclBluetoothManager, Radio, (*i));
					wclBluetoothManagerDiscoveringCompleted(&wclBluetoothManager, Radio, WCL_E_SUCCESS);
				}
			}
		}
		else
		{
			int Res = Radio->Discover(10, dkClassic);
			if (Res != WCL_E_SUCCESS)
				AfxMessageBox(_T("Error starting discovering: 0x") + IntToHex(Res));
		}
	}
}

CString CRfCommClientDlg::IntToHex(const int i) const
{
	CString s;
	s.Format(_T("%.8X"), i);
	return s;
}

CString CRfCommClientDlg::IntToHex(const __int64 i) const
{
	CString s;
	s.Format(_T("%.4X%.8X"), static_cast<INT32>((i >> 32) & 0x00000FFFF), static_cast<INT32>(i) & 0xFFFFFFFF);
	return s;
}

void CRfCommClientDlg::OnBnClickedButtonClear()
{
	lbEvents.ResetContent();
}

__int64 CRfCommClientDlg::StrToInt64(const CString& s) const
{
	return _tcstoi64(s, NULL, 16);
}

int CRfCommClientDlg::StrToInt(const CString& s) const
{
	return _tcstol(s, NULL, 10);
}

void CRfCommClientDlg::OnBnClickedButtonConnect()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		int Item = lvDevices.GetNextSelectedItem(Pos);

		CwclBluetoothRadio*  Radio = GetRadio();
		if (Radio != NULL)
		{
			wclRfCommClient.Address = StrToInt64(lvDevices.GetItemText(Item, 0));
			wclRfCommClient.Authentication = (cbAuthentication.GetCheck() != 0);
			wclRfCommClient.Encryption = (cbEncryption.GetCheck() != 0);
			CString s;
			edTimeout.GetWindowText(s);
			wclRfCommClient.Timeout = StrToInt(s);

			wclRfCommClient.Channel = 0;
			wclRfCommClient.Service = SerialPortServiceClass_UUID;
			
			bool Connect = false;
			if (cbServiceName.GetCheck())
			{
				GUID Service = wclRfCommClient.Service;
				wclBluetoothServices Services;
				int Res = Radio->EnumRemoteServices(wclRfCommClient.Address, &Service, Services);
				if (Res != WCL_E_SUCCESS)
					AfxMessageBox(_T("Service enumerating error: 0x") + IntToHex(Res));
				else
				{
					if (Services.size() == 0)
						AfxMessageBox(_T("Services not found"));
					else
					{
						for (wclBluetoothServices::iterator svc = Services.begin(); svc != Services.end(); svc++)
						{
							edServiceName.GetWindowText(s);
							tstring str(s);
							if ((*svc).Name == str)
							{
								wclRfCommClient.Channel = (*svc).Channel;
								Connect = true;
								break;
							}
						}
						
						if (!Connect)
							AfxMessageBox(_T("Service not found"));
					}
				}
			}
			else
				Connect = true;
			
			if (Connect)
			{
				int Res = wclRfCommClient.Connect(Radio);
				if (Res != WCL_E_SUCCESS)
					AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
			}
		}
	}
}

void CRfCommClientDlg::OnBnClickedButtonDisconnect()
{
	int Res = wclRfCommClient.Disconnect();
	if (Res != WCL_E_SUCCESS)
		AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
}

CString CRfCommClientDlg::IntToStr(const int i) const
{
	CString s;
	s.Format(_T("%d"), i);
	return s;
}

void CRfCommClientDlg::OnBnClickedButtonSend()
{
	CString s;
	edText.GetWindowText(s);
	CStringA Ansi(s);
	unsigned long Sent;
	int Res = wclRfCommClient.Write(Ansi.GetBuffer(), Ansi.GetLength(), Sent);
	if (Res != WCL_E_SUCCESS)
		AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
	else
		AfxMessageBox(_T("Sent: ") + IntToStr(Sent) + _T(" from ") + IntToStr(Ansi.GetLength()));
}

void CRfCommClientDlg::OnBnClickedButtonSetBuffers()
{
	// Try to set read buffer size first.
	CString s;
	edReadBuffer.GetWindowText(s);
	int Size = StrToInt(s);
	int Res = wclRfCommClient.SetReadBufferSize((unsigned long)Size);
	if (Res == WCL_E_SUCCESS)
		lbEvents.AddString(_T("Read buffer size changed."));
	else
		lbEvents.AddString(_T("Set read buffer size error: 0x") + IntToHex(Res));
	
	// Now write buffer.
	edWriteBuffer.GetWindowText(s);
	Size = StrToInt(s);
	Res = wclRfCommClient.SetWriteBufferSize((unsigned long)Size);
	if (Res == WCL_E_SUCCESS)
		lbEvents.AddString(_T("Write buffer size changed."));
	else
		lbEvents.AddString(_T("Set write buffer size error: 0x") + IntToHex(Res));
}

void CRfCommClientDlg::OnBnClickedButtonGetBuffers()
{
	GetBuffers();
}

void CRfCommClientDlg::GetBuffers()
{
	// Try to get read and write buffers size.
	unsigned long Size = 0;
	
	int Res = wclRfCommClient.GetReadBufferSize(Size);
	if (Res != WCL_E_SUCCESS)
		lbEvents.AddString(_T("Get read buffer error: 0x") + IntToHex(Res));
	else
		edReadBuffer.SetWindowText(IntToStr(Size));
	
	Size = 0;
	Res = wclRfCommClient.GetWriteBufferSize(Size);
	if (Res != WCL_E_SUCCESS)
		lbEvents.AddString(_T("Get write buffer error: 0x") + IntToHex(Res));
	else
		edWriteBuffer.SetWindowText(IntToStr(Size));
}

void CRfCommClientDlg::wclBluetoothManagerNumericComparison(void* Sender, CwclBluetoothRadio* const Radio,
	const __int64 Address, const unsigned long Number, bool& Confirm)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Radio);
	UNREFERENCED_PARAMETER(Address);

	// Accept any pairing.
	Confirm = true;
	lbEvents.AddString(_T("Numeric comparison: ") + IntToStr(Number));
}

void CRfCommClientDlg::wclBluetoothManagerPasskeyNotification(void* Sender,
	CwclBluetoothRadio* const Radio, const __int64 Address, const unsigned long Passkey)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Radio);
	UNREFERENCED_PARAMETER(Address);

	lbEvents.AddString(_T("Passkey notification: ") + IntToStr(Passkey));
}

void CRfCommClientDlg::wclBluetoothManagerPasskeyRequest(void* Sender,
	CwclBluetoothRadio* const Radio, const __int64 Address, unsigned long& Passkey)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Radio);
	UNREFERENCED_PARAMETER(Address);

	// Use 123456 as passkey.
	Passkey = 123456;
	lbEvents.AddString(_T("Passkey request: ") + IntToStr(Passkey));
}

void CRfCommClientDlg::wclBluetoothManagerPinRequest(void* Sender, CwclBluetoothRadio* const Radio,
	const __int64 Address, tstring& Pin)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Radio);
	UNREFERENCED_PARAMETER(Address);

	// Use '0000' as PIN.
	Pin = _T("0000");
	lbEvents.AddString(_T("Pin request: ") + CString(Pin.c_str()));
}

void CRfCommClientDlg::wclBluetoothManagerConfirm(void* Sender, CwclBluetoothRadio* const Radio,
	const __int64 Address, bool& Confirm)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Radio);
	UNREFERENCED_PARAMETER(Address);

	Confirm = true;
	lbEvents.AddString(_T("Just works pairing"));
}

void CRfCommClientDlg::wclBluetoothManagerAuthenticationCompleted(void* Sender, 
	CwclBluetoothRadio* const Radio, const __int64 Address, const int Error)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Radio);
	UNREFERENCED_PARAMETER(Address);

	lbEvents.AddString(_T("Authentication completed: ") + IntToHex(Error));
}

void CRfCommClientDlg::wclBluetoothManagerDeviceFound(void* Sender,
	CwclBluetoothRadio* const Radio, const __int64 Address)
{
	UNREFERENCED_PARAMETER(Sender);

	wclBluetoothDeviceType DevType = dtMixed;
	int Res = Radio->GetRemoteDeviceType(Address, DevType);
	
	int Item = lvDevices.GetItemCount();
	lvDevices.InsertItem(Item, IntToHex(Address));
	lvDevices.SetItemText(Item, 1, _T("")); // We can not read a device's name here.
	lvDevices.SetItemData(Item, (DWORD_PTR)Radio); // To use it later.
	if (Res != WCL_E_SUCCESS)
		lvDevices.SetItemText(Item, 2, _T("Error: 0x") + IntToHex(Res));
	else
	{
		switch (DevType)
		{
			case dtClassic:
				lvDevices.SetItemText(Item, 2, _T("Classic"));
				break;
			case dtBle:
				lvDevices.SetItemText(Item, 2, _T("BLE"));
				break;
			case dtMixed:
				lvDevices.SetItemText(Item, 2, _T("Mixed"));
				break;
			default:
				lvDevices.SetItemText(Item, 2, _T("Unknown"));
		}
	}
	
	lbEvents.AddString(_T("Device found: ") + IntToHex(Address));
}

void CRfCommClientDlg::wclBluetoothManagerDiscoveringCompleted(void* Sender,
	CwclBluetoothRadio* const Radio, const int Error)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Error);

	if (lvDevices.GetItemCount() == 0)
		AfxMessageBox(_T("No devices were found."));

	else
	{
		// Here we can update found devices names.
		for (int i = 0; i < lvDevices.GetItemCount(); i++)
		{
			CString s = lvDevices.GetItemText(i, 0);
			__int64 Address = StrToInt64(s);
			tstring DevName;
			int Res = Radio->GetRemoteName(Address, DevName);
			if (Res != WCL_E_SUCCESS)
				lvDevices.SetItemText(i, 1, _T("Error: 0x") + IntToHex(Res));
			else
				lvDevices.SetItemText(i, 1, DevName.c_str());
		}
	}
		
	lbEvents.AddString(_T("Discovering completed"));
}

void CRfCommClientDlg::wclBluetoothManagerDiscoveringStarted(void* Sender,
	CwclBluetoothRadio* const Radio)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Radio);

	lvDevices.DeleteAllItems();
	lbEvents.AddString(_T("Discovering started"));
}

void CRfCommClientDlg::wclRfCommClientConnect(void* Sender, const int Error)
{
	UNREFERENCED_PARAMETER(Sender);

	if (Error == WCL_E_SUCCESS)
	{
		lbEvents.AddString(_T("Connected"));
		
		GetBuffers();
	}
	else
		lbEvents.AddString(_T("Connect error: 0x") + IntToHex(Error));
}

void CRfCommClientDlg::wclRfCommClientData(void* Sender, const void* const Data, 
	const unsigned long Size)
{
	UNREFERENCED_PARAMETER(Sender);

	if (Size > 0)
	{
		CStringA Str((PCHAR)Data, (int)Size);
		CString s(Str);
		lbEvents.AddString(_T("Received: ") + s);
	}
}

void CRfCommClientDlg::wclRfCommClientDisconnect(void* Sender, const int Reason)
{
	UNREFERENCED_PARAMETER(Sender);

	lbEvents.AddString(_T("Disconnected: 0x") + IntToHex(Reason));
	
	edReadBuffer.SetWindowText(_T("0"));
	edWriteBuffer.SetWindowText(_T("0"));
}

void CRfCommClientDlg::OnBnClickedButtonPair()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		int Item = lvDevices.GetNextSelectedItem(Pos);
		CwclBluetoothRadio* Radio = GetRadio();
		if (Radio != NULL)
		{
			int Res = Radio->RemotePair(StrToInt64(lvDevices.GetItemText(Item, 0)), pmClassic);
			if (Res != WCL_E_SUCCESS)
				AfxMessageBox(_T("Pair failed: 0x") + IntToHex(Res));
		}
	}
}

void CRfCommClientDlg::OnBnClickedButtonUnpair()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		int Item = lvDevices.GetNextSelectedItem(Pos);
		CwclBluetoothRadio* Radio = GetRadio();
		if (Radio != NULL)
		{
			int Res = Radio->RemoteUnpair(StrToInt64(lvDevices.GetItemText(Item, 0)), pmClassic);
			if (Res != WCL_E_SUCCESS)
				AfxMessageBox(_T("Unpair failed: 0x") + IntToHex(Res));
		}
	}
}
