#include "stdafx.h"
#include "windows.h"
#include "..\..\ftprn_api.h"
#include "..\..\ftprn_api_config.h"
#include "..\..\ftStatus.h"
#include <list>
#include <io.h>
#include <fcntl.h>

std::list<ftPrinter> g_lstDevices;
HANDLE g_listMutex = NULL;
HANDLE g_evEnumerated = NULL;
HANDLE g_evSvcStarted = NULL;
HANDLE g_evSvcStopped = NULL;
HANDLE g_evRdpConnected = NULL; 
HANDLE g_evRdpDisconnected = NULL;

BOOL InitSyncItems()
{
	BOOL bRes = FALSE;
	do
	{
		g_listMutex = CreateMutex(NULL, FALSE, NULL);
		if (g_listMutex == NULL)
			break;
		g_evEnumerated = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (g_evEnumerated == NULL)
			break;
		g_evSvcStarted = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (g_evSvcStarted == NULL)
			break;
		g_evSvcStopped = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (g_evSvcStopped == NULL)
			break;
		g_evRdpConnected = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (g_evRdpConnected == NULL)
			break;
		g_evRdpDisconnected = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (g_evRdpDisconnected == NULL)
			break;
		
		bRes = TRUE;
	} while (false);
	return bRes;
}

void DeinitSyncItems()
{
	if (g_listMutex)
	{
		CloseHandle(g_listMutex);
		g_listMutex = NULL;
	}
	if (g_evEnumerated)
	{
		CloseHandle(g_evEnumerated);
		g_evEnumerated = NULL;
	}
	if (g_evSvcStarted)
	{
		CloseHandle(g_evSvcStarted);
		g_evSvcStarted = NULL;
	}
	if (g_evSvcStopped)
	{
		CloseHandle(g_evSvcStopped);
		g_evSvcStopped = NULL;
	}
	if (g_evRdpConnected)
	{
		CloseHandle(g_evRdpConnected);
		g_evRdpConnected = NULL;
	}
	if (g_evRdpDisconnected)
	{
		CloseHandle(g_evRdpDisconnected);
		g_evRdpDisconnected = NULL;
	}
}

void NotificationCallback(FT_PRINTER_EVENT eEvent, LPVOID lpParam, LPVOID lpContext)
{
	switch (eEvent)
	{
	case evSvcStarted:
		SetEvent(g_evSvcStarted);
		break;
	case evSvcStopped:
		SetEvent(g_evSvcStopped);
		break;
	case evRdpConnected:
		SetEvent(g_evRdpConnected);
		break;
	case evRdpNotConnected:
		SetEvent(g_evRdpDisconnected);
		break;
	case evRdpConnectionBroken:
		SetEvent(g_evRdpDisconnected);
		break;

	case evPrinterPlugged:
	{
		FT_PRINTER* pPrinter = (FT_PRINTER*)lpParam;
		if (!pPrinter)
			break;

		WaitForSingleObject(g_listMutex, INFINITE);
		auto it = g_lstDevices.begin();		
		// this printer is selected by default
		if (pPrinter->bSelected)
		{
			for (; it != g_lstDevices.end(); ++it)
				it->bSelected = FALSE;
		}
		// find printer
		for (it = g_lstDevices.begin(); it != g_lstDevices.end(); ++it)
			if ((*it) == (*pPrinter))
				break;

		if (it == g_lstDevices.end())
		{
			g_lstDevices.push_back(*pPrinter);
		}
		else
		{
			*it = *pPrinter;
		}
		ReleaseMutex(g_listMutex);
		break;
	}

	case evPrinterUnplugged:
	{
		FT_PRINTER* pPrinter = (FT_PRINTER*)lpParam;
		if (!pPrinter)
			break;

		WaitForSingleObject(g_listMutex, INFINITE);
		// find printer
		auto it = g_lstDevices.begin();
		for (; it != g_lstDevices.end(); ++it)
			if ((*it) == (*pPrinter))
				break;

		if (it != g_lstDevices.end())
		{
			g_lstDevices.erase(it);
		}
		ReleaseMutex(g_listMutex);
		break;
	}

	case evPrintersListed:
		SetEvent(g_evEnumerated);
		break;
	}

}

int wmain(int argc, WCHAR* argv[])
{
	if (InitSyncItems())
	{
		if (FtPrnVirtApiLoad())
		{
			SetConsoleOutputCP(936);
			if (_setmode(_fileno(stdout), _O_U16TEXT) == -1)
			{
				wprintf(L"Error setting file translation mode\n");
			}

			ftStatus stat = FtPrnVirtApiInit(NotificationCallback, NULL);
			if (stat == ftSuccess)
			{
				HANDLE handles[] = { g_evSvcStopped, g_evRdpDisconnected, g_evEnumerated };
				DWORD dwWait = WaitForMultipleObjects(_countof(handles), handles, FALSE, 5000);
				if (dwWait != WAIT_OBJECT_0 + 2)
				{
					switch (dwWait)
					{
					case WAIT_OBJECT_0:
						wprintf(L"Service is not running.\n");
						break;
					case WAIT_OBJECT_0 + 1:
						wprintf(L"No connection to workstation module.\n");
						break;
					case WAIT_TIMEOUT:
						wprintf(L"Wait timeout: unable to get printer list.\n");
						break;
					}
					return 0;
				}
				WaitForSingleObject(g_listMutex, INFINITE);
				if (g_lstDevices.empty())
				{
					wprintf(L"No virtual printers.\n");
				}
				else
				{
					DWORD dwUniqId = 0;
					ftPrinter Printer;
					stat = FtPrnGetDefaultPrinter(&Printer);

					for (auto it = g_lstDevices.begin(); it != g_lstDevices.end(); ++it)
					{
						if (stat == ftSuccess && it->dwUniqId == Printer.dwUniqId)
						{
							wprintf(L"%d. %-50s\t[default]\n", it->dwUniqId, it->wsName);
						}
						else
						{
							wprintf(L"%d. %-50s\n", it->dwUniqId, it->wsName);
							dwUniqId = it->dwUniqId;
						}
					}

					// Select default printer
					stat = FtPrnSelectPrinter(dwUniqId);
					if (stat == ftSuccess)
						wprintf(L"Success\n");
					else if (stat == ftNotFound)
						wprintf(L"Printer not found\n");
					else
						wprintf(L"Error: ftStatus=%d\n", stat);

					// Get default printer
					stat = FtPrnGetDefaultPrinter(&Printer);
					if (stat == ftSuccess)
						wprintf(L"%d. %s\n", Printer.dwUniqId, Printer.wsName);
					else if (stat == ftNotFound)
						wprintf(L"DefaultPrinter not found\n");
					else
						wprintf(L"Error: ftStatus=%d\n", stat);
				}
				ReleaseMutex(g_listMutex);

				FtPrnVirtApiCleanUp();
			}
			else
			{
				wprintf(L"Printer API dll initialization failed\n");
			}
			FtPrnVirtApiFree();
		}
		else
		{
			wprintf(L"Error: unable to load API dll \"%s\". GLE=%d\n", cPrintApiDllName, GetLastError());
		}
		DeinitSyncItems();
	}
	else
	{
		wprintf(L"Error: unable to create events. GLE=%d\n", GetLastError());
	}
	return 0;
}

