// OppServerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "OppServer.h"
#include "OppServerDlg.h"

#include "..\..\..\..\Source\Bluetooth\wclUUIDs.h"
#include "..\..\..\..\Source\Bluetooth\wclCODs.h"
#include "..\..\..\..\Source\Common\wclErrors.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

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

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

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


// COppServerDlg dialog




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

void COppServerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_CLIENTS, lvClients);
	DDX_Control(pDX, IDC_LIST_LOG, lbLogs);
}

BEGIN_MESSAGE_MAP(COppServerDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_BUTTON_CLEAR, &COppServerDlg::OnBnClickedButtonClear)
	ON_BN_CLICKED(IDC_BUTTON_LISTEN, &COppServerDlg::OnBnClickedButtonListen)
	ON_BN_CLICKED(IDC_BUTTON_CLOSE, &COppServerDlg::OnBnClickedButtonClose)
	ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, &COppServerDlg::OnBnClickedButtonDisconnect)
END_MESSAGE_MAP()


// COppServerDlg message handlers

BOOL COppServerDlg::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

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

	wclRfCommServer.Authentication = false;
	wclRfCommServer.Service = OBEXObjectPushServiceClass_UUID;
	wclRfCommServer.ServiceName = _T("WCL OPP Server");

	__hook(&CwclRfCommServer::OnListen, &wclRfCommServer,
		&COppServerDlg::wclRfCommServerListen);
	__hook(&CwclRfCommServer::OnClosed, &wclRfCommServer,
		&COppServerDlg::wclRfCommServerClosed);
	__hook(&CwclRfCommServer::OnDisconnect, &wclRfCommServer,
		&COppServerDlg::wclRfCommServerDisconnect);
	__hook(&CwclRfCommServer::OnDestroyProcessor, &wclRfCommServer,
		&COppServerDlg::wclRfCommServerDestroyProcessor);
	__hook(&CwclRfCommServer::OnConnect, &wclRfCommServer,
		&COppServerDlg::wclRfCommServerConnect);
	__hook(&CwclRfCommServer::OnCreateProcessor, &wclRfCommServer,
		&COppServerDlg::wclRfCommServerCreateProcessor);
	__hook(&CwclRfCommServer::GetSdpAttributes, &wclRfCommServer,
		&COppServerDlg::wclRfCommServerGetSdpAttributes);

	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 COppServerDlg::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 COppServerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


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

	// TODO: Add your message handler code here

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

void COppServerDlg::OnBnClickedButtonClear()
{
	// TODO: Add your control notification handler code here
	lbLogs.ResetContent();
}

void COppServerDlg::OnBnClickedButtonListen()
{
	// TODO: Add your control notification handler code here
	int Res = wclBluetoothManager.Open();
	if (Res != WCL_E_SUCCESS)
		AfxMessageBox(_T("Bluetooth manager open failed: 0x") + IntToHex(Res));
	else
	{
		CwclBluetoothRadio* Radio;
		Res = wclBluetoothManager.GetClassicRadio(Radio);
		if (Res != WCL_E_SUCCESS)
			AfxMessageBox(_T("Get working radio failed: 0x") + IntToHex(Res));
		else
		{
			// 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);

			// As wrote above it is better to start listening in AfterOpen event
			// handler. But again, for demo app its OK to do it here.
			Res = wclRfCommServer.Listen(Radio);
			if (Res != WCL_E_SUCCESS)
				AfxMessageBox(_T("Failed to start listening: 0x") + IntToHex(Res));
		}

		if (Res != WCL_E_SUCCESS)
			// We must close Bluetooth Manager here!
			wclBluetoothManager.Close();
	}
}

void COppServerDlg::OnBnClickedButtonClose()
{
	// TODO: Add your control notification handler code here
	int Res = wclRfCommServer.Close();
	if (Res != WCL_E_SUCCESS)
		AfxMessageBox(_T("Close failed; 0x") + IntToHex(Res));
	else
		wclBluetoothManager.Close();
}

void COppServerDlg::wclRfCommServerListen(void* Sender)
{
	UNREFERENCED_PARAMETER(Sender);

	Trace(_T("Server listening"));
}

void COppServerDlg::Trace(const CString& Msg)
{
	lbLogs.AddString(Msg);
}

void COppServerDlg::wclRfCommServerClosed(void* Sender, const int Reason)
{
	UNREFERENCED_PARAMETER(Sender);

	Trace(_T("Server closed. Reason:: 0x") + IntToHex(Reason));
}

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

	Trace(_T("Client ") + IntToHex(Client->Address) +
		_T(" disconnected. Reason: 0x") + IntToHex(Reason));
}

void COppServerDlg::wclRfCommServerDestroyProcessor(void* Sender,
	CwclServerClientDataConnection* const Connection)
{
	UNREFERENCED_PARAMETER(Sender);

	Trace(_T("Data processor for client ") +
		IntToHex(((CwclRfCommServerClientConnection*)Connection)->Address) +
		_T(" is destroing"));

	// 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((CwclObexOppServer*)Connection->Processor);
	delete Connection->Processor;
}

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

	if (Error == WCL_E_SUCCESS)
		Trace(_T("Client ") + IntToHex(Client->Address) + _T(" connected"));
	else
		Trace(_T("Client ") + IntToHex(Client->Address) + _T(" connect failed: 0x") + IntToHex(Error));
}

void COppServerDlg::wclRfCommServerCreateProcessor(void* Sender, CwclServerClientDataConnection* const Connection)
{
	UNREFERENCED_PARAMETER(Sender);

	// Here we must create OBEX OPP server data processor.
	CwclObexOppServer* Proc = new CwclObexOppServer(Connection);
	__hook(&CwclObexOppServer::OnConnect, Proc, &COppServerDlg::OppClientConnect);
	__hook(&CwclObexOppServer::OnDisconnected, Proc, &COppServerDlg::OppClientDisconnected);
	__hook(&CwclObexOppServer::OnPutBegin, Proc, &COppServerDlg::OppClientPutBegin);
	__hook(&CwclObexOppServer::OnPutCompleted, Proc, &COppServerDlg::OppClientPutCompleted);
	__hook(&CwclObexOppServer::OnPutProgress, Proc, &COppServerDlg::OppClientPutProgress);
	__hook(&CwclObexOppServer::OnGetRequest, Proc, &COppServerDlg::OppClientGetRequest);
	__hook(&CwclObexOppServer::OnGetCompleted, Proc, &COppServerDlg::OppClientGetCompleted);
	__hook(&CwclObexOppServer::OnProgress, Proc, &COppServerDlg::OppClientProgress);
}

void COppServerDlg::OppClientConnect(void* Sender, const tstring& Description)
{
	CwclObexOppServer* Proc = (CwclObexOppServer*)Sender;
	__int64 Address = ((CwclRfCommServerClientConnection*)Proc->Connection)->Address;
	bool Accept = AfxMessageBox(_T("Client ") + IntToHex(Address) +
		_T(" requsts for connection: ") + CString(Description.c_str()) + _T(". Accept?"),
		MB_ICONQUESTION | MB_YESNO) == IDYES;
	
	int Res;
	if (Accept)
	{
		Res = Proc->Accept(_T("Accepted by user"));
		if (Res == WCL_E_SUCCESS)
		{
			int nItem = lvClients.GetItemCount();
			lvClients.InsertItem(nItem, IntToHex(Address));
			lvClients.SetItemData(nItem, (DWORD_PTR)Sender);
			Trace(_T("OBEX session opened for client: ") + IntToHex(Address));
		}
	}
	else
		Res = Proc->Reject(_T("Rejected by user"));
	
	if (Res != WCL_E_SUCCESS)
	{
		AfxMessageBox(_T("Operation failed: 0x") + IntToHex(Res));
		// Disconnect client!
		Proc->Connection->Disconnect();
	}
}

void COppServerDlg::OppClientDisconnected(void* Sender, const int Reason, const tstring& Description)
{
	CwclObexOppServer* Proc = (CwclObexOppServer*)Sender;
	__int64 Address = ((CwclRfCommServerClientConnection*)Proc->Connection)->Address;
	
	Trace(_T("Client ") + IntToHex(Address) + _T(" closed OBEX session with reason: 0x") + IntToHex(Reason) +
		_T(". Description: ") + CString(Description.c_str()));
	
	for (int i = 0; i < lvClients.GetItemCount(); i++)
	{
		if ((void*)lvClients.GetItemData(i) == Sender)
		{
			lvClients.DeleteItem(i);
			break;
		}
	}
}
void COppServerDlg::OnBnClickedButtonDisconnect()
{
	// TODO: Add your control notification handler code here
	POSITION Pos = lvClients.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select client"));
	else
	{
		int i = lvClients.GetNextSelectedItem(Pos);
		CwclObexOppServer* Proc = (CwclObexOppServer*)lvClients.GetItemData(i);
		int Res = Proc->Connection->Disconnect();
		if (Res != WCL_E_SUCCESS)
			AfxMessageBox(_T("Disconnect failed: 0x") + IntToHex(Res));
	}
}

void COppServerDlg::wclRfCommServerGetSdpAttributes(void* Sender, wclBluetoothSdpProtocols& Protocols,
	wclBluetoothSdpProfiles& Profiles, wclBluetoothSdpFormats& Formats, unsigned long& Cod)
{
	UNREFERENCED_PARAMETER(Sender);

	Protocols.push_back(OBEX_PROTOCOL_UUID16);
	wclBluetoothSdpProfile Profile;
	Profile.Uuid = OBEXObjectPushServiceClass_UUID16;
	Profile.Version = 0x0100;
	Profiles.push_back(Profile);
	Formats.push_back(0xFF); // All formats.
	Cod = COD_SERVICE_OBJECT_XFER;
}

void COppServerDlg::OppClientPutBegin(void* Sender, const tstring& Name, const tstring& Description, const tstring& Mime,
	const unsigned long Length, bool& Accept)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Description);
	UNREFERENCED_PARAMETER(Mime);
	UNREFERENCED_PARAMETER(Length);

	CwclObexOppServer* Proc = (CwclObexOppServer*)Sender;
	__int64 Address = ((CwclRfCommServerClientConnection*)Proc->Connection)->Address;
	CString Str = _T("Client ") + IntToHex(Address) + _T(" send file ") + CString(Name.c_str()) +
		_T(". Accept?");
	Accept = AfxMessageBox(Str, MB_ICONQUESTION | MB_YESNO) == IDYES;
	if (Accept)
		FFileName = Name;
}

void COppServerDlg::OppClientPutCompleted(void* Sender, const int Error, CwclStream* const Stream, bool& Accept)
{
	CwclObexOppServer* Proc = (CwclObexOppServer*)Sender;
	__int64 Address = ((CwclRfCommServerClientConnection*)Proc->Connection)->Address;
	if (Error == WCL_E_SUCCESS)
	{
		CString Str = _T("Client ") + IntToHex(Address) + _T(" completed sent file. Accept?");
		Accept = AfxMessageBox(Str, MB_ICONQUESTION | MB_YESNO) == IDYES;

		if (Accept)
		{
			CFileDialog Dlg(FALSE, NULL, FFileName.c_str());
			if (Dlg.DoModal() == IDOK)
			{
				CwclFileStream fs(tstring(Dlg.GetPathName()), CREATE_ALWAYS, GENERIC_WRITE, FILE_SHARE_READ);
				((CwclMemoryStream*)Stream)->SaveToStream(&fs);
			}
		}
	}
	else
		Trace(_T("Send file error: 0x") + IntToHex(Error));
}

void COppServerDlg::OppClientPutProgress(void* Sender, const unsigned long Position, 
	const unsigned long Length, bool& Continue)
{
	UNREFERENCED_PARAMETER(Sender);

	Trace(_T("Sending file: ") + IntToStr(Position) + _T(" from ") + IntToStr(Length));
	// Do not foget to set Continue flag!
	Continue = true;
}

void COppServerDlg::OppClientGetRequest(void* Sender, const tstring& aType, wclObexServerOperationResult& Result,
	CwclStream*& Stream)
{
	UNREFERENCED_PARAMETER(Sender);

	if (aType == _T("text/x-vCard") || aType == _T("x-obex/capability"))
	{
		Trace(_T("Requested file type: ") + CString(aType.c_str()));
		
		CFileDialog Dlg(TRUE);
		if (Dlg.DoModal() != IDOK)
		{
			Result = orForbidden;
			Stream = NULL;
		}
		else
		{
			Stream = new CwclFileStream(tstring(Dlg.GetPathName()), OPEN_EXISTING,
				GENERIC_READ, FILE_SHARE_READ);
			Result = orSuccess;
		}
	}
	else
	{
		Result = orUnsupportedMedia;
		Stream = NULL;
	}
}

void COppServerDlg::OppClientGetCompleted(void* Sender, const int Error, CwclStream* const Stream)
{
	UNREFERENCED_PARAMETER(Sender);

	Trace(_T("Get operation completed with result: 0x") + IntToHex(Error));
	if (Stream != NULL)
		delete Stream;
}

void COppServerDlg::OppClientProgress(void* Sender, const unsigned long Length, 
	const unsigned long Position)
{
	UNREFERENCED_PARAMETER(Sender);

	Trace(_T("Sending file: ") + IntToStr(Position) + _T(" from ") + IntToStr(Length));
}