// RfCommServerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "RfCommServer.h"
#include "RfCommServerDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CRfCommServerDlg dialog




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

void CRfCommServerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_CHECK_AUTHENTICATION, cbAuthentication);
	DDX_Control(pDX, IDC_CHECK_ENCRYPTION, cbEncryption);
	DDX_Control(pDX, IDC_LIST_CLIENTS, lvClients);
	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_EDIT_SERVICE_NAME, edServiceName);
}

BEGIN_MESSAGE_MAP(CRfCommServerDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BUTTON_CLEAR, &CRfCommServerDlg::OnBnClickedButtonClear)
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_BUTTON_LISTEN, &CRfCommServerDlg::OnBnClickedButtonListen)
	ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CRfCommServerDlg::OnBnClickedButtonClose)
	ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, &CRfCommServerDlg::OnBnClickedButtonDisconnect)
	ON_BN_CLICKED(IDC_BUTTON_SEND, &CRfCommServerDlg::OnBnClickedButtonSend)
	ON_BN_CLICKED(IDC_BUTTON_GET_BUFFERS, &CRfCommServerDlg::OnBnClickedButtonGetBuffers)
	ON_BN_CLICKED(IDC_BUTTON_SET_BUFFERS, &CRfCommServerDlg::OnBnClickedButtonSetBuffers)
END_MESSAGE_MAP()


// CRfCommServerDlg message handlers

BOOL CRfCommServerDlg::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,
		&CRfCommServerDlg::wclBluetoothManagerNumericComparison);
	__hook(&CwclBluetoothManager::OnPasskeyNotification, &wclBluetoothManager,
		&CRfCommServerDlg::wclBluetoothManagerPasskeyNotification);
	__hook(&CwclBluetoothManager::OnPasskeyRequest, &wclBluetoothManager,
		&CRfCommServerDlg::wclBluetoothManagerPasskeyRequest);
	__hook(&CwclBluetoothManager::OnPinRequest, &wclBluetoothManager,
		&CRfCommServerDlg::wclBluetoothManagerPinRequest);

	__hook(&CwclRfCommServer::OnClosed, &wclRfCommServer, &CRfCommServerDlg::wclRfCommServerClosed);
	__hook(&CwclRfCommServer::OnConnect, &wclRfCommServer, &CRfCommServerDlg::wclRfCommServerConnect);
	__hook(&CwclRfCommServer::OnData, &wclRfCommServer, &CRfCommServerDlg::wclRfCommServerData);
	__hook(&CwclRfCommServer::OnDisconnect, &wclRfCommServer, &CRfCommServerDlg::wclRfCommServerDisconnect);
	__hook(&CwclRfCommServer::OnListen, &wclRfCommServer, &CRfCommServerDlg::wclRfCommServerListen);

	lvClients.InsertColumn(0, _T("Address"), 0, 200);
	lvClients.InsertColumn(1, _T("Name"), 0, 200);
	lvClients.SetExtendedStyle(lvClients.GetExtendedStyle() | LVS_EX_FULLROWSELECT);

	edText.SetWindowText(_T("Send this text to selected client"));
	edServiceName.SetWindowText(_T("Bluetooth Framework server"));

	// In real application you should always analize the result code.
	// In this demo we assume that all is always OK.
	wclBluetoothManager.Open();
	
	cbAuthentication.SetCheck(wclRfCommServer.Authentication);
	cbEncryption.SetCheck(wclRfCommServer.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 CRfCommServerDlg::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 CRfCommServerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


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

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

	wclRfCommServer.Close();
	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(&wclRfCommServer);
}

CwclBluetoothRadio* CRfCommServerDlg::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 CRfCommServerDlg::OnBnClickedButtonListen()
{
	CwclBluetoothRadio* Radio = GetRadio();
	if (Radio != NULL)
	{
		// Switch radio in discoverable and connectable mode.
		// Ignore any errors. In real application you need to check the result.
		Radio->SetDiscoverable(true);
		Radio->SetConnectable(true);

		CString s;
		edServiceName.GetWindowText(s);

		wclRfCommServer.Authentication = (cbAuthentication.GetCheck() != 0);
		wclRfCommServer.Encryption = (cbEncryption.GetCheck() != 0);

		wclRfCommServer.Service = SerialPortServiceClass_UUID;
		wclRfCommServer.ServiceName = tstring(s);
		
		int Res = wclRfCommServer.Listen(Radio);
		if (Res != WCL_E_SUCCESS)
			AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
	}
}

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

CString CRfCommServerDlg::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;
}

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

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

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

void CRfCommServerDlg::OnBnClickedButtonClose()
{
	int Res = wclRfCommServer.Close();
	if (Res != WCL_E_SUCCESS)
		AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
}

void CRfCommServerDlg::OnBnClickedButtonDisconnect()
{
	POSITION Pos = lvClients.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select client"));
	else
	{
		int Item = lvClients.GetNextSelectedItem(Pos);
		int Res = ((CwclRfCommServerClientConnection*)lvClients.GetItemData(Item))->Disconnect();
		if (Res != WCL_E_SUCCESS)
			AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
	}
}

void CRfCommServerDlg::OnBnClickedButtonSend()
{
	POSITION Pos = lvClients.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select client"));
	else
	{
		int Item = lvClients.GetNextSelectedItem(Pos);
		CString s;
		edText.GetWindowText(s);
		CStringA Ansi(s);
		unsigned long Sent;
		int Res = ((CwclRfCommServerClientConnection*)lvClients.GetItemData(Item))->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 CRfCommServerDlg::OnBnClickedButtonGetBuffers()
{
	POSITION Pos = lvClients.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select client"));
	else
	{
		int Item = lvClients.GetNextSelectedItem(Pos);
		CwclRfCommServerClientConnection* Client = (CwclRfCommServerClientConnection*)lvClients.GetItemData(Item);
		
		// Get read buffer.
		unsigned long Size = 0;
		int Res = Client->GetReadBufferSize(Size);
		if (Res != WCL_E_SUCCESS)
			lbEvents.AddString(_T("Get read buffer error: 0x") + IntToHex(Res));
		else
			edReadBuffer.SetWindowText(IntToStr(Size));
		
		// Get write buffer.
		Size = 0;
		Res = Client->GetWriteBufferSize(Size);
		if (Res != WCL_E_SUCCESS)
			lbEvents.AddString(_T("Get write buffer error: 0x") + IntToHex(Res));
		else
			edWriteBuffer.SetWindowText(IntToStr(Size));
	}
}

void CRfCommServerDlg::OnBnClickedButtonSetBuffers()
{
	POSITION Pos = lvClients.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select client"));
	else
	{
		int Item = lvClients.GetNextSelectedItem(Pos);
		CwclRfCommServerClientConnection* Client = (CwclRfCommServerClientConnection*)lvClients.GetItemData(Item);
		
		// Set read buffer.
		CString s;
		edReadBuffer.GetWindowText(s);
		unsigned long Size = StrToInt(s);
		int Res = Client->SetReadBufferSize(Size);
		if (Res != WCL_E_SUCCESS)
			lbEvents.AddString(_T("Set read buffer error: 0x") + IntToHex(Res));
		
		// Set write buffer.
		edWriteBuffer.GetWindowText(s);
		Size = StrToInt(s);
		Res = Client->SetWriteBufferSize(Size);
		if (Res != WCL_E_SUCCESS)
			lbEvents.AddString(_T("Set write buffer error: 0x") + IntToHex(Res));
	}
}

CString CRfCommServerDlg::GetDeviceName(const __int64 Address) const
{
	CwclBluetoothRadio* Radio = GetRadio();
	if (Radio != NULL)
	{
		tstring DevName;
		int Res = Radio->GetRemoteName(Address, DevName);
		if (Res == WCL_E_SUCCESS)
			return CString(DevName.c_str());
		
		return (_T("Error: 0x") + IntToHex(Res));
	}

	return _T("");
}

void CRfCommServerDlg::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 CRfCommServerDlg::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 CRfCommServerDlg::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 CRfCommServerDlg::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 CRfCommServerDlg::wclRfCommServerClosed(void* Sender, const int Reason)
{
	lbEvents.AddString(_T("Server closed on radio ") +
		CString(((CwclRfCommServer*)Sender)->Radio->ApiName.c_str()) +
		_T(" with reason: 0x") + IntToHex(Reason));
	
	lvClients.DeleteAllItems();
	
	edReadBuffer.SetWindowText(_T(""));
	edWriteBuffer.SetWindowText(_T(""));
}

void CRfCommServerDlg::wclRfCommServerConnect(void* Sender,
	CwclRfCommServerClientConnection* const Client, const int Error)
{
	UNREFERENCED_PARAMETER(Sender);

	CwclRfCommServerConnection* Server = (CwclRfCommServerConnection*)(Client->Server);
	
	lbEvents.AddString(_T("Client connected: ") + IntToHex(Client->Address) +
		_T(" on Radio ") + CString(Server->Radio->ApiName.c_str()) +
		_T(" error: 0x") + IntToHex(Error));

	int Item = lvClients.GetItemCount();
	lvClients.InsertItem(Item, IntToHex(Client->Address));
	lvClients.SetItemText(Item, 1, GetDeviceName(Client->Address));
	lvClients.SetItemData(Item, (DWORD_PTR)Client);
}

void CRfCommServerDlg::wclRfCommServerData(void* Sender, CwclRfCommServerClientConnection* const Client,
	const void* const Data, const unsigned long Size)
{
	UNREFERENCED_PARAMETER(Sender);

	if (Size > 0)
	{
		CStringA a((PCHAR)Data, Size);
		CString s(a);
		
		CwclRfCommServerConnection* Server = (CwclRfCommServerConnection*)(Client->Server);
		lbEvents.AddString(_T("Received: ") + s + _T(" from ") + IntToHex(Client->Address) +
			_T(" on radio ") + CString(Server->Radio->ApiName.c_str()));
	}
}

void CRfCommServerDlg::wclRfCommServerDisconnect(void* Sender, CwclRfCommServerClientConnection* const Client,
	const int Reason)
{
	UNREFERENCED_PARAMETER(Sender);

	CwclRfCommServerConnection* Server = (CwclRfCommServerConnection*)(Client->Server);
	
	lbEvents.AddString(_T("Client disconnected: ") + IntToHex(Client->Address) +
		_T(" on radio ") + CString(Server->Radio->ApiName.c_str()) +
		_T(" reason: 0x") + IntToHex(Reason));
	
	for (int i = 0; i < lvClients.GetItemCount(); i++)
	{
		if (((CwclRfCommServerClientConnection*)lvClients.GetItemData(i)) == Client)
		{
			lvClients.DeleteItem(i);
			break;
		}
	}
}

void CRfCommServerDlg::wclRfCommServerListen(void* Sender)
{
	lbEvents.AddString(_T("Server listening on radio ") +
		CString(((CwclRfCommServer*)Sender)->Radio->ApiName.c_str()) +
		_T(" on channel: ") + IntToStr(((CwclRfCommServer*)Sender)->AssignedChannel));
}
