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

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

#include "..\..\..\..\Source\Bluetooth\wclBluetooth.h"

using namespace std;
using namespace wclCommon;
using namespace wclBluetooth;

class CBluetoothManager
{
private:
	CwclBluetoothRadio*		Radio;
	HANDLE					CompleteEvent;
	HANDLE					TerminateEvent;
	HANDLE					InitEvent;
	int						InitResult;
	HANDLE					RunThread;
	CwclBluetoothManager*	Manager;
	RTL_CRITICAL_SECTION	Cs;

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

	tstring atow(const string& a) const
	{
		return tstring(a.begin(), a.end());
	}

	string btoa(const bool b) const
	{
		if (b)
			return "true";
		return "false";
	}

	void GetRadioInformation() const
	{
		system("cls");

		cout << "RADIO INFORMATION" << endl;
		cout << "=================" << endl;
		cout << "  Driver: " << wtoa(Radio->ApiName).c_str() << endl;
		cout << "  Available: " << Radio->Available << endl;

		bool BoolValue;
		int Res = Radio->GetConnectable(BoolValue);
		if (Res != WCL_E_SUCCESS)
			cout << "  GetConnectable: error 0x" << hex << Res << dec << endl;
		else
			cout << "  Connectable: " << btoa(BoolValue) << endl;

		Res = Radio->GetDiscoverable(BoolValue);
		if (Res != WCL_E_SUCCESS)
			cout << "  GetDiscoverable: error 0x" << hex << Res << dec << endl;
		else
			cout << "  Discoverable: " << btoa(BoolValue) << endl;

		unsigned char Version;
		unsigned short Revision;
		Res = Radio->GetHciVersion(Version, Revision);
		if (Res != WCL_E_SUCCESS)
			cout << "  HCI version: error 0x" << hex << Res << dec << endl;
		else
			cout << "  HCI version: " << Version << "." << Revision << endl;

		Res = Radio->GetLmpVersion(Version, Revision);
		if (Res != WCL_E_SUCCESS)
			cout << "  LMP version: error 0x" << hex << Res << dec << endl;
		else
			cout << "  LMP version: " << Version << "." << Revision << endl;

		unsigned short Manu;
		Res = Radio->GetManufacturer(Manu);
		if (Res != WCL_E_SUCCESS)
			cout << "  Manufacturer: error 0x" << hex << Res << dec << endl;
		else
			cout << "  Manufacturer: " << Manu << endl;

		tstring Name;
		Res = Radio->GetName(Name);
		if (Res != WCL_E_SUCCESS)
			cout << "  Name: error 0x" << hex << Res << dec << endl;
		else
			cout << "  Name: " << wtoa(Name).c_str() << endl;

		system("pause");
	}

	void SetDiscoverable(const bool On)
	{
		system("cls");
		cout << "CHANGE DISCOVERABLE MODE" << endl;
		cout << "========================" << endl;
		int Res = Radio->SetDiscoverable(On);
		if (Res != WCL_E_SUCCESS)
			cout << "  Change discoverable mode failed: 0x" << hex << Res << dec << endl;
		else
			cout << "  Discoverable mode has been changed" << endl;
		system("pause");
	}

	void SetConnectable(const bool On)
	{
		system("cls");
		cout << "CHANGE CONNECTABLE MODE" << endl;
		cout << "=======================" << endl;
		int Res = Radio->SetConnectable(On);
		if (Res != WCL_E_SUCCESS)
			cout << "  Change connectable mode failed: 0x" << hex << Res << dec << endl;
		else
			cout << "  Connectable mode has been changed" << endl;
		system("pause");
	}

	void SwitchRadio(const bool On)
	{
		system("cls");
		cout << "CHANGE RADIO STATE" << endl;
		cout << "===================" << endl;
		int Res;
		if (On)
			Res = Radio->TurnOn();
		else
			Res = Radio->TurnOff();
		if (Res != WCL_E_SUCCESS)
			cout << "  Change radio state failed: 0x" << hex << Res << dec << endl;
		else
			cout << "  Radio state has been changed" << endl;
		system("pause");
	}

	void Discover(const bool Ble)
	{
		system("cls");
		cout << "DISOCVER REMOTE DEVICES" << endl;
		cout << "=======================" << endl;

		wclBluetoothDiscoverKind Kind;
		if (Ble)
			Kind = dkBle;
		else
			Kind = dkClassic;
		int Res = Radio->Discover(10, Kind);
		if (Res != WCL_E_SUCCESS)
			cout << "Failed to start discovering: 0x" << hex << Res << dec << endl;
		else
			WaitForSingleObject(CompleteEvent, INFINITE);

		cout << "Press any key to continiue" << endl;
		system("pause");
	}

	void EnumConnected()
	{
		while (true)
		{
			system("cls");
			cout << "ENUMARATE CONNECTED DEVICES" << endl;
			cout << "===========================" << endl;

			wclBluetoothAddresses Devices;
			int Res = Radio->EnumConnectedDevices(Devices);
			if (Res != WCL_E_SUCCESS || Devices.size() == 0)
			{
				if (Res != WCL_E_SUCCESS)
					cout << "Failed to enumerate connected devices: 0x" << hex << Res << dec << endl;
				else
					cout << "No connected devices found" << endl;
				cout << "Press any key to continiue" << endl;
				system("pause");
				return;
			}

			cout << "Select device to disconnect" << endl;
			int i = 0;
			for (wclBluetoothAddresses::iterator d = Devices.begin(); d != Devices.end(); d++)
			{
				i++;
				cout << "  " << i << " : " << hex << (*d) << dec << endl;
			}
			cout << "  0 - Exit" << endl;

			cout << "Select option: ";
			int opt;
			while (true)
			{
				cin >> opt;
				if (opt == 0)
					return;

				if (opt > i)
				{
					cout << "Invalid device number" << endl;
					continue;
				}

				wclBluetoothAddresses::iterator d = Devices.begin();
				std::advance(d, i - 1);
				Res = Radio->RemoteDisconnect(*d);
				if (Res != WCL_E_SUCCESS)
				{
					cout << "Disconnect failed: 0x" << hex << Res << dec << endl;
					continue;
				}
				
				cout << "Disconnected" << endl;
				cout << "Press any key to continiue" << endl;
				system("pause");
				break;
			}
		}
	}

	void Manager_OnDiscoveringStarted(void* Sender, CwclBluetoothRadio* const Radio)
	{
		UNREFERENCED_PARAMETER(Sender);
		UNREFERENCED_PARAMETER(Radio);

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

	void Manager_OnDiscoveringCompleted(void* Sender, CwclBluetoothRadio* const Radio, 
		const int Error)
	{
		UNREFERENCED_PARAMETER(Sender);
		UNREFERENCED_PARAMETER(Radio);

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

	void Manager_OnDeviceFound(void* Sender, CwclBluetoothRadio* const Radio, 
		const __int64 Address)
	{
		UNREFERENCED_PARAMETER(Sender);
		UNREFERENCED_PARAMETER(Radio);

		cout << "Device found: " << hex << Address << dec << endl;
	}

	void Manager_OnStatusChanged(void* Sender, CwclBluetoothRadio* const Radio)
	{
		UNREFERENCED_PARAMETER(Sender);

		cout << "  == Radio " << wtoa(Radio->ApiName.c_str()) << " status changed. Available: " << btoa(Radio->Available) << " ==" << endl;
	}

	void Manager_AfterOpen(void* sender)
	{
		UNREFERENCED_PARAMETER(sender);

		cout << "Bluetooth manager opened" << endl;
	}

	static UINT __stdcall _ManagerThread(LPVOID Param)
	{
		((CBluetoothManager*)Param)->ManagerThread();
		return 0;
	}

	void ManagerThread()
	{
		Manager = new CwclBluetoothManager();
		Manager->MessageProcessing = mpAsync;
		__hook(&CwclBluetoothManager::AfterOpen, Manager, &CBluetoothManager::Manager_AfterOpen);
		__hook(&CwclBluetoothManager::OnStatusChanged, Manager, &CBluetoothManager::Manager_OnStatusChanged);
		__hook(&CwclBluetoothManager::OnDeviceFound, Manager, &CBluetoothManager::Manager_OnDeviceFound);
		__hook(&CwclBluetoothManager::OnDiscoveringCompleted, Manager, &CBluetoothManager::Manager_OnDiscoveringCompleted);
		__hook(&CwclBluetoothManager::OnDiscoveringStarted, Manager, &CBluetoothManager::Manager_OnDiscoveringStarted);

		InitResult = Manager->Open();
		if (InitResult != WCL_E_SUCCESS)
			cout << "Failed to open Blkuetooth Manager: 0x" << hex << InitResult << dec << endl;
		else
		{
			if (Manager->Count == 0)
			{
				InitResult = WCL_E_BLUETOOTH_HARDWARE_NOT_AVAILABLE;
				cout << "No Bluetooth hardware was found" << endl;
			}

			if (InitResult != WCL_E_SUCCESS)
				Manager->Close();
		}

		SetEvent(InitEvent);

		if (InitResult == WCL_E_SUCCESS)
		{
			WaitForSingleObject(TerminateEvent, INFINITE);
			Manager->Close();
		}

		__unhook(Manager);
		delete Manager;
		Manager = NULL;
	}

	void ShowRadioMenu()
	{
		bool Terminated = false;
		do
		{
			system("cls");
			cout << "RADIO MENU: " << wtoa(Radio->ApiName.c_str()) << endl;
			cout << "  1 - Get information" << endl;
			cout << "  2 - Discoverable ON" << endl;
			cout << "  3 - Discoverable OFF" << endl;
			cout << "  4 - Connectable ON" << endl;
			cout << "  5 - Connectable OFF" << endl;
			cout << "  6 - Turn ON" << endl;
			cout << "  7 - Turn OFF" << endl;
			cout << "  8 - Discover LE" << endl;
			cout << "  9 - Discover Classic" << endl;
			cout << "  0 - Enum connected " << endl;
			cout << "  e - Exit" << endl;
			cout << "Select option: ";
			char opt;
			cin >> opt;

			switch (opt)
			{
			case '0':
				EnumConnected();
				break;
			case '1':
				GetRadioInformation();
				break;
			case '2':
				SetDiscoverable(true);
				break;
			case '3':
				SetDiscoverable(false);
				break;
			case '4':
				SetConnectable(true);
				break;
			case '5':
				SetConnectable(false);
				break;
			case '6':
				SwitchRadio(true);
				break;
			case '7':
				SwitchRadio(false);
				break;
			case '8':
				Discover(true);
				break;
			case '9':
				Discover(false);
				break;
			case 'e':
				Terminated = true;
				break;
			}
		} while (!Terminated);
	}

public:
	CBluetoothManager()
	{
		Radio = NULL;
		CompleteEvent = NULL;
		TerminateEvent = NULL;
		InitEvent = NULL;
		InitResult = WCL_E_SUCCESS;
		RunThread = NULL;
		Manager = NULL;
		InitializeCriticalSection(&Cs);
	}

	~CBluetoothManager()
	{
		DeleteCriticalSection(&Cs);
	}

	void Run()
	{
		CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (CompleteEvent == NULL)
			cout << "Create completion event failed" << endl;
		else
		{
			TerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
			if (TerminateEvent == NULL)
				cout << "Create termination event failed" << endl;
			else
			{
				InitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
				if (InitEvent == NULL)
					cout << "Create initialization event failed" << endl;
				else
				{
					InitResult = WCL_E_SUCCESS;
					RunThread = (HANDLE)_beginthreadex(NULL, 0, _ManagerThread, this, 0, NULL);
					if (RunThread == NULL)
						cout << "Create thread failed" << endl;
					else
					{
						WaitForSingleObject(InitEvent, INFINITE);
						if (InitResult == WCL_E_SUCCESS)
						{
							bool Terminated = false;
							do
							{
								system("cls");
								cout << "BLUETOOTH MANAGER DEMO" << endl;
								cout << "======================" << endl;

								for (size_t i = 0; i < Manager->Count; i++)
									cout << (i + 1) << " - " << wtoa(Manager->Radios[i]->ApiName.c_str()) << endl;
								cout << "0 - Exit" << endl;
								cout << "Select Radio" << endl;

								size_t Index;
								cin >> Index;
								if (Index == 0)
									Terminated = true;
								else
								{
									Index--;
									if (Index >= Manager->Count)
									{
										cout << "Wrong selection" << endl;
										system("pause");
									}
									else
									{
										Radio = Manager->Radios[Index];
										ShowRadioMenu();
									}
								}
							} while (!Terminated);

							SetEvent(TerminateEvent);
						}
						WaitForSingleObject(RunThread, INFINITE);
						CloseHandle(RunThread);
					}

					CloseHandle(InitEvent);
					InitEvent = NULL;
				}
				CloseHandle(TerminateEvent);
				TerminateEvent = NULL;
			}
			CloseHandle(CompleteEvent);
			CompleteEvent = NULL;
		}
	}
};

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

	CBluetoothManager* Manager = new CBluetoothManager();
	Manager->Run();
	delete Manager;
}