// 10 june 2019
#include "test.h"

static void dummyHandler(void *sender, void *args, void *data)
{
	// do nothing
}

static void checkWithGlobalEvent(void (*f)(uiEvent *e))
{
	uiEvent *e;
	uiEventOptions opts;

	memset(&opts, 0, sizeof (uiEventOptions));
	opts.Size = sizeof (uiEventOptions);
	opts.Global = true;
	e = uiNewEvent(&opts);
	(*f)(e);
	uiEventFree(e);
}

static void checkWithNonglobalEvent(void (*f)(uiEvent *e))
{
	uiEvent *e;
	uiEventOptions opts;

	memset(&opts, 0, sizeof (uiEventOptions));
	opts.Size = sizeof (uiEventOptions);
	opts.Global = false;
	e = uiNewEvent(&opts);
	(*f)(e);
	uiEventFree(e);
}

struct checkWhileFiringParams {
	uiEvent *e;
	void (*f)(uiEvent *e);
};

static void checkWhileFiringHandler(void *sender, void *args, void *data)
{
	struct checkWhileFiringParams *p = (struct checkWhileFiringParams *) data;

	(*(p->f))(p->e);
}

static void checkWhileFiring(void (*f)(uiEvent *e))
{
	uiEvent *e;
	uiEventOptions opts;
	struct checkWhileFiringParams p;
	int handler;

	memset(&opts, 0, sizeof (uiEventOptions));
	opts.Size = sizeof (uiEventOptions);
	opts.Global = true;
	e = uiNewEvent(&opts);
	memset(&p, 0, sizeof (struct checkWhileFiringParams));
	p.e = e;
	p.f = f;
	handler = uiEventAddHandler(e, checkWhileFiringHandler, NULL, &p);
	uiEventFire(e, NULL, NULL);
	uiEventDeleteHandler(e, handler);
	uiEventFree(e);
}

static const struct checkErrorCase cases[] = {
	{
		"uiNewEvent() with NULL uiEventOptions",
		[](void) {
			uiNewEvent(NULL);
		},
		"uiNewEvent(): invalid null pointer for uiEventOptions",
	},
	{
		"uiNewEvent() with bad uiEventOptions size",
		[](void) {
			uiEventOptions opts;
	
			memset(&opts, 0, sizeof (uiEventOptions));
			opts.Size = 1;
			uiNewEvent(&opts);
		},
		"uiNewEvent(): wrong size 1 for uiEventOptions",
	},
	
	{
		"uiEventFree() with NULL uiEvent",
		[](void) {
			uiEventFree(NULL);
		},
		"uiEventFree(): invalid null pointer for uiEvent",
	},
#define checkEventFreeInternal(which) \
	{ \
		"uiEventFree() called on " #which, \
		[](void) { uiEventFree(which); }, \
		"uiEventFree(): can't free a libui-provided event", \
	}
	checkEventFreeInternal(uiControlOnFree()),
	{
		"uiEventFree() while said event is firing",
		[](void) {
			checkWhileFiring([](uiEvent *e) {
				uiEventFree(e);
			});
		},
		"uiEventFree(): can't change a uiEvent while it is firing",
	},
	{
		"uiEventFree() while handlers registered",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				int handler;
	
				handler = uiEventAddHandler(e, dummyHandler, NULL, NULL);
				uiEventFree(e);
				uiEventDeleteHandler(e, handler);
			});
		},
		"uiEventFree(): can't free event that still has handlers registered",
	},
	
	{
		"uiEventAddHandler() with NULL uiEvent",
		[](void) {
			uiEventAddHandler(NULL, NULL, NULL, NULL);
		},
		"uiEventAddHandler(): invalid null pointer for uiEvent",
	},
	{
		"uiEventAddHandler() while that event is firing",
		[](void) {
			checkWhileFiring([](uiEvent *e) {
				uiEventAddHandler(e, dummyHandler, NULL, NULL);
			});
		},
		"uiEventAddHandler(): can't change a uiEvent while it is firing",
	},
	{
		"uiEventAddHandler() with a NULL uiEventHandler",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				uiEventAddHandler(e, NULL, NULL, NULL);
			});
		},
		"uiEventAddHandler(): invalid null pointer for uiEventHandler",
	},
	{
		"uiEventAddHandler() with a non-NULL sender on a global uiEvent",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				uiEventAddHandler(e, dummyHandler, e, NULL);
			});
		},
		"uiEventAddHandler(): can't use a non-NULL sender with a global event",
	},
	{
		"uiEventAddHandler() with a NULL sender on a non-global uiEvent",
		[](void) {
			checkWithNonglobalEvent([](uiEvent *e) {
				uiEventAddHandler(e, dummyHandler, NULL, NULL);
			});
		},
		"uiEventAddHandler(): can't use a NULL sender with a non-global event",
	},
	
	{
		"uiEventDeleteHandler() with NULL uiEvent",
		[](void) {
			uiEventDeleteHandler(NULL, 5);
		},
		"uiEventDeleteHandler(): invalid null pointer for uiEvent",
	},
	{
		"uiEventDeleteHandler() while that event is firing",
		[](void) {
			checkWhileFiring([](uiEvent *e) {
				uiEventDeleteHandler(e, 5);
			});
		},
		"uiEventDeleteHandler(): can't change a uiEvent while it is firing",
	},
	{
		"uiEventDeleteHandler() with invalid handler",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				uiEventDeleteHandler(e, 5);
			});
		},
		"uiEventDeleteHandler(): event handler 5 not found",
	},
	
	{
		"uiEventFire() on a NULL uiEvent",
		[](void) {
			uiEventFire(NULL, NULL, NULL);
		},
		"uiEventFire(): invalid null pointer for uiEvent",
	},
	{
		"uiEventFire() on an event that is firing",
		[](void) {
			checkWhileFiring([](uiEvent *e) {
				uiEventFire(e, NULL, NULL);
			});
		},
		"uiEventFire(): can't recursively fire a uiEvent",
	},
	{
		"uiEventFire() with a non-NULL sender on a global event",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				uiEventFire(e, e, NULL);
			});
		},
		"uiEventFire(): can't use a non-NULL sender with a global event",
	},
	{
		"uiEventFire() with a NULL sender on a non-global event",
		[](void) {
			checkWithNonglobalEvent([](uiEvent *e) {
				uiEventFire(e, NULL, NULL);
			});
		},
		"uiEventFire(): can't use a NULL sender with a non-global event",
	},
	
	{
		"uiEventHandlerBlocked() with a NULL uiEvent",
		[](void) {
			uiEventHandlerBlocked(NULL, 5);
		},
		"uiEventHandlerBlocked(): invalid null pointer for uiEvent",
	},
	{
		"uiEventHandlerBlocked() with an invalid handler",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				uiEventHandlerBlocked(e, 5);
			});
		},
		"uiEventHandlerBlocked(): event handler 5 not found",
	},
	
	{
		"uiEventSetHandlerBlocked() with a NULL uiEvent",
		[](void) {
			uiEventSetHandlerBlocked(NULL, 5, false);
		},
		"uiEventSetHandlerBlocked(): invalid null pointer for uiEvent",
	},
	{
		"uiEventSetHandlerBlocked() while the given event is firing",
		[](void) {
			checkWhileFiring([](uiEvent *e) {
				uiEventSetHandlerBlocked(e, 5, false);
			});
		},
		"uiEventSetHandlerBlocked(): can't change a uiEvent while it is firing",
	},
	{
		"uiEventSetHandlerBlocked() with invalid handler",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				uiEventSetHandlerBlocked(e, 5, false);
			});
		},
		"uiEventSetHandlerBlocked(): event handler 5 not found",
	},
	
	{
		"uiEventInvalidateSender() with NULL uiEvent",
		[](void) {
			uiEventInvalidateSender(NULL, NULL);
		},
		"uiEventInvalidateSender(): invalid null pointer for uiEvent",
	},
	{
		"uiEventInvalidateSender() while that event is firing",
		[](void) {
			checkWhileFiring([](uiEvent *e) {
				uiEventInvalidateSender(e, NULL);
			});
		},
		"uiEventInvalidateSender(): can't change a uiEvent while it is firing",
	},
	{
		"uiEventInvalidateSender() with a global event",
		[](void) {
			checkWithGlobalEvent([](uiEvent *e) {
				uiEventInvalidateSender(e, NULL);
			});
		},
		"uiEventInvalidateSender(): can't invalidate a global event",
	},
	{
		"uiEventInvalidateSender() with a NULL sender on a non-global event",
		[](void) {
			checkWithNonglobalEvent([](uiEvent *e) {
				uiEventInvalidateSender(e, NULL);
			});
		},
		"uiEventInvalidateSender(): can't use a NULL sender with a non-global event",
	},
	{ NULL, NULL, NULL },
};

testingTest(EventErrors)
{
	checkProgrammerErrors(t, cases);
}