// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "pch.h"
#include <process.h>

#include "..\..\..\..\Source\WiFi\wclWiFi.h"

using namespace std;

using namespace wclCommon;
using namespace wclWiFi;

string wtoa(const tstring& w)
{
	return string(w.begin(), w.end());
}

class CWiFiWatcher
{
private:
	HANDLE	CompleteEvent;
	HANDLE	Thread;
	
	void wclWiFiDirectDeviceWatcherDiscoveringStarted(void* Sender)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "Discovering started" << endl;
	}

	void wclWiFiDirectDeviceWatcherDeviceFound(void* Sender, const tstring& Id, 
		const tstring& Name)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "Found device " << wtoa(Name).c_str() << endl;
		Devices.push_back(Id);
	}

	void wclWiFiDirectDeviceWatcherDiscoveringCompleted(void* Sender, const int Error)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "Discovering completed with result: 0x" << hex << Error << dec << endl;
		SetEvent(CompleteEvent);
	}

	static UINT __stdcall _ThreadProc(LPVOID Param)
	{
		((CWiFiWatcher*)Param)->ThreadProc();
		return 0;
	}

	void ThreadProc()
	{
		CwclWiFiDirectDeviceWatcher* Watcher = new CwclWiFiDirectDeviceWatcher();
		Watcher->MessageProcessing = mpAsync;
		__hook(&CwclWiFiDirectDeviceWatcher::OnDeviceFound, Watcher, &CWiFiWatcher::wclWiFiDirectDeviceWatcherDeviceFound);
		__hook(&CwclWiFiDirectDeviceWatcher::OnDiscoveringCompleted, Watcher, &CWiFiWatcher::wclWiFiDirectDeviceWatcherDiscoveringCompleted);
		__hook(&CwclWiFiDirectDeviceWatcher::OnDiscoveringStarted, Watcher, &CWiFiWatcher::wclWiFiDirectDeviceWatcherDiscoveringStarted);
		
		cout << "Try to find WiFi Direct devices" << endl;
		int Res = Watcher->Discover();
		if (Res != WCL_E_SUCCESS)
			cout << "Failed to start WiFi Direct device watcher: 0x" << hex << Res << dec << endl;
		else
			WaitForSingleObject(CompleteEvent, INFINITE);

		__unhook(Watcher);
		delete Watcher;
	}

public:
	CWiFiWatcher()
	{
		CompleteEvent = NULL;
		Devices.clear();
		Thread = NULL;
	}

	std::list<tstring> Devices;
	
	void Discover()
	{
		CompleteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
		Thread = (HANDLE)_beginthreadex(NULL, 0, _ThreadProc, this, 0, NULL);
		WaitForSingleObject(Thread, INFINITE);
		CloseHandle(Thread);
		CloseHandle(CompleteEvent);
	}
};

class CWiFiClient
{
private:
	tstring	Id;
	HANDLE	TerminateEvent;
	HANDLE	Thread;
	
	void wclWiFiDirectClientDeviceConnected(void* Sender, CwclWiFiDirectDevice* const Device, 
		const int Error)
	{
		UNREFERENCED_PARAMETER(Sender);

		if (Error != WCL_E_SUCCESS)
			cout << "Device " << wtoa(Device->Name).c_str() << " connect error: 0x" << hex << Error << dec << endl;
		else
		{
			cout << "Device " << wtoa(Device->Name).c_str() << " has been connected" << endl;
			cout << "  Local IP: " << wtoa(Device->LocalAddress).c_str() << endl;
			cout << "  Remote IP: " << wtoa(Device->RemoteAddress).c_str() << endl;
		}
	}

	void wclWiFiDirectClientDeviceDisconnected(void* Sender, CwclWiFiDirectDevice* const Device, 
		const int Reason)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "Device " << wtoa(Device->Name).c_str() << " has been disconnected by reason: 0x" << hex << Reason << dec << endl;
	}

	void wclWiFiDirectClientPairCompleted(void* Sender, CwclWiFiDirectDevice* const Device, 
		const int Result)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "Pairing with device " << wtoa(Device->Name).c_str() << " has been completed with result: 0x" << hex << Result << dec << endl;
	}

	void wclWiFiDirectClientPairConfirm(void* Sender, CwclWiFiDirectDevice* const Device, bool& Confirm)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "Pairing with device " << wtoa(Device->Name).c_str() << " has been confirmed" << endl;
		Confirm = true;
	}

	void wclWiFiDirectClientPairDisplayPin(void* Sender, CwclWiFiDirectDevice* const Device, 
		const tstring& Pin)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "To pair with device " << wtoa(Device->Name).c_str() << " type this PIN: " << wtoa(Pin) << " on device keyboard" << endl;
	}

	void wclWiFiDirectClientPairGetParams(void* Sender, CwclWiFiDirectDevice* const Device,
		short& GroupOwnerIntent, wclWiFiDirectConfigurationMethods& ConfigurationMethods,
		wclWiFiDirectPairingProcedure& PairingProcedure)
	{
		UNREFERENCED_PARAMETER(Sender);
		UNREFERENCED_PARAMETER(Device);

		cout << "Connection is accepted with following parameters:" << endl;
		cout << "  Pairing procedure: Invitation" << endl;
		cout << "  GroupOwnerIntent: default (14)" << endl;
		cout << "  Configuration (pairing) method: Push Button (Configm Only)" << endl;

		PairingProcedure = ppInvitation;
		GroupOwnerIntent = 14;
		ConfigurationMethods.clear();
		ConfigurationMethods.insert(cmPushButton);
	}

	void wclWiFiDirectClientPairProvidePin(void* Sender, CwclWiFiDirectDevice* const Device, 
		tstring& Pin)
	{
		UNREFERENCED_PARAMETER(Sender);
		UNREFERENCED_PARAMETER(Device);

		cout << "Device requires pairing with entering PIN on Advertiser side. This type of pairing is not supported in this demo" << endl;
		Pin = _T("");
	}

	static UINT __stdcall _ThreadProc(LPVOID Param)
	{
		((CWiFiClient*)Param)->ThreadProc();
		return 0;
	}

	void ThreadProc()
	{
		CwclWiFiDirectClient* Client = new CwclWiFiDirectClient();
		Client->MessageProcessing = mpAsync;
		__hook(&CwclWiFiDirectClient::OnDeviceConnected, Client , &CWiFiClient::wclWiFiDirectClientDeviceConnected);
		__hook(&CwclWiFiDirectClient::OnPairCompleted, Client, &CWiFiClient::wclWiFiDirectClientPairCompleted);
		__hook(&CwclWiFiDirectClient::OnPairConfirm, Client, &CWiFiClient::wclWiFiDirectClientPairConfirm);
		__hook(&CwclWiFiDirectClient::OnPairDisplayPin, Client, &CWiFiClient::wclWiFiDirectClientPairDisplayPin);
		__hook(&CwclWiFiDirectClient::OnPairProvidePin, Client, &CWiFiClient::wclWiFiDirectClientPairProvidePin);
		__hook(&CwclWiFiDirectClient::OnDeviceDisconnected, Client, &CWiFiClient::wclWiFiDirectClientDeviceDisconnected);
		__hook(&CwclWiFiDirectClient::OnPairGetParams, Client, &CWiFiClient::wclWiFiDirectClientPairGetParams);

		int Res = Client->Connect(Id);
		if (Res != WCL_E_SUCCESS)
			cout << "Connect failed: 0x" << hex << Res << dec << endl;

		WaitForSingleObject(TerminateEvent, INFINITE);

		if (Client->State != dsDisconnected)
		{
			Res = Client->Disconnect();
			if (Res != WCL_E_SUCCESS)
				cout << "Disconnect failed: 0x" << hex << Res << dec << endl;
		}
		__unhook(Client);
		delete Client;
	}

public:
	CWiFiClient()
	{
		TerminateEvent = NULL;
		Thread = NULL;
		Id = _T("");
	}

	void Connect(tstring _Id)
	{
		TerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
		Id = _Id;
		Thread = (HANDLE)_beginthreadex(NULL, 0, _ThreadProc, this, 0, NULL);
	}

	void Disconnect()
	{
		SetEvent(TerminateEvent);
		WaitForSingleObject(Thread, INFINITE);
		CloseHandle(Thread);
		CloseHandle(TerminateEvent);
		Id = _T("");
	}
};

int main(int argc, char **argv)
{
	UNREFERENCED_PARAMETER(argc);
	UNREFERENCED_PARAMETER(argv);

	system("cls");
	cout << "WIFI DIRECT CLIENT DEMO" << endl;
	cout << "=======================" << endl;
	cout << "The demo shows how to discover WiFi Direct devices and connect to one." << endl;
	cout << "This demo will connect to first found WiFi Direct device" << endl;

	CWiFiWatcher* Watcher = new CWiFiWatcher();
	Watcher->Discover();

	int Ndx = -1;
	if (Watcher->Devices.size() == 0)
		cout << "No WiFi Direct devices were found" << endl;
	else
	{
		int n = 0;
		for (list<tstring>::iterator i = Watcher->Devices.begin(); i != Watcher->Devices.end(); i++)
		{
			cout << n << ": " << wtoa((*i)).c_str() << endl;
			n++;
		}
		cout << "Select device: ";
		cin >> Ndx;
	}

	CWiFiClient* Client = NULL;
	if (Ndx > -1)
	{
		Client = new CWiFiClient();
		std::list<tstring>::iterator i = Watcher->Devices.begin();
		std::advance(i, Ndx);
		Client->Connect((*i));
	}

	system("pause");

	if (Client != NULL)
	{
		Client->Disconnect();
		delete Client;
	}

	delete Watcher;
}