#include <stdio.h>
#include <windows.h>
#include <time.h>
#include "qcommon.h"

static guid_t  mask, hash;
static etkey_t result;
static DWORD   numThreads;
static HANDLE  mutex;
static BOOL    finish = FALSE;

DWORD WINAPI CreateGUIDSeekThread(void* data)
{
	etkey_t    etkey  = "0000001002000000000000000000";
	guid_t     guid   = {0};
	size_t     thread = (size_t) data, i;
	int        j;
	time_t     ts;
	struct tm *tm;

	for (ts = 0; !finish; ts++)
	{
		tm = localtime(&ts);

		for (i = thread; i < 9999 && !finish; i += numThreads)
		{
			sprintf(etkey + 10, "%04i%02i%02i%02i%02i%02i%03i", tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int) i);
			CalculateGUID(guid, etkey + 10);

			for (j = 0; j < sizeof(guid_t) - 1; j++)
			{
				if (mask[j] != 0 && mask[j] != guid[j])
				{
					goto next;
				}
			}

			if (WaitForSingleObject(mutex, 0) == WAIT_OBJECT_0)
			{
				finish = TRUE;
				memcpy(result, etkey, sizeof(etkey_t) - 1);
				memcpy(hash, guid, sizeof(guid_t) - 1);
				return 0;
			}

			next:;
		}
	}

	return 0;
}

int main(int argc, char **argv)
{
	size_t      i, len;
	SYSTEM_INFO si;
	HANDLE      thread;
	HANDLE     *threads;
	char        c;

	if (argc != 2)
	{
		fprintf(stderr, "usage: %s <pattern>\n", argv[0]);
		return 1;
	}

	for (i = 0, len = strlen(argv[1]); i < len && i < sizeof(guid_t); i++)
	{
		c = argv[1][i];

		if (c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F' || c >= '0' && c <= '9')
		{
			mask[i] = toupper(argv[1][i]);
		}
	}

	GetSystemInfo(&si);
	numThreads = si.dwNumberOfProcessors;

	if (numThreads <= 0)
	{
		return 2;
	}

	mutex = CreateMutex(NULL, FALSE, NULL);

	threads = malloc(sizeof(HANDLE) * numThreads);

	for (i = 0; i < numThreads; i++)
	{
		thread = CreateThread(NULL, 0, CreateGUIDSeekThread, (LPVOID) i, 0, NULL);

		if (!thread)
		{
			fprintf(stderr, "Failed to spawn thread #%llu\n", i);
			return 3;
		}

		threads[i] = thread;
	}

	fprintf(stderr, "Seeking the matching GUID on %lu threads...\n", numThreads);

	WaitForMultipleObjects(numThreads, threads, TRUE, INFINITE);

	for (i = 0; i < numThreads; i++)
	{
		CloseHandle(threads[i]);
	}

	ReleaseMutex(mutex);
	CloseHandle(mutex);

	fprintf(stderr, "Found: %s [%s]\n", hash, result);

	if (hash[0])
	{
		FILE *fd = fopen(hash, "w");
		fprintf(fd, "%s", result);
		fclose(fd);
	}

	return 0;
}