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

// TODO once this is filled in, verify against the documentation that this is all the error cases
// TODO add an internal marker to uiEvent as well, just to be safe?

static const struct checkErrorCase casesBeforeOSVtable[] = {
	{
		"uiRegisterControlType() with NULL name",
		[](void) {
			uiRegisterControlType(NULL, NULL, NULL, 0);
		},
		"uiRegisterControlType(): invalid null pointer for name",
	},
	{
		"uiRegisterControlType() with NULL vtable",
		[](void) {
			uiRegisterControlType("name", NULL, NULL, 0);
		},
		"uiRegisterControlType(): invalid null pointer for uiControlVtable",
	},
	{
		"uiRegisterControlType() with vtable with wrong size",
		[](void) {
			uiControlVtable vtable;
	
			memset(&vtable, 0, sizeof (uiControlVtable));
			vtable.Size = 1;
			uiRegisterControlType("name", &vtable, NULL, 0);
		},
		"uiRegisterControlType(): wrong size 1 for uiControlVtable",
	},
#define checkVtableMethod(name) \
	{ \
		"uiRegisterControlType() with NULL " #name " method", \
		[](void) { \
			uiControlVtable vtable; \
			vtable = *testVtable(); \
			vtable.name = NULL; \
			uiRegisterControlType("name", &vtable, NULL, 0); \
		}, \
		"uiRegisterControlType(): required uiControlVtable method " #name "() missing for uiControl type name", \
	}
	checkVtableMethod(Init),
	checkVtableMethod(Free),
	{
		"uiRegisterControlType() with NULL OS vtable",
		[](void) {
			uiRegisterControlType("name", testVtable(), NULL, 0);
		},
		"uiRegisterControlType(): invalid null pointer for uiControlOSVtable",
	},
	{ NULL, NULL, NULL },
};

static const struct checkErrorCase casesAfterOSVtable[] = {
	{
		"uiCheckControlType() with a NULL pointer",
		[](void) {
			uiCheckControlType(NULL, uiControlType());
		},
		"uiCheckControlType(): invalid null pointer for uiControl",
	},
	{
		"uiCheckControlType() with non-control",
		[](void) {
			// TODO make sure this is fine; if not, just use the following check instead
			static char buf[] = "this is not a uiControl but is big enough to at the very least not cause a problem with UB hopefully";

			uiCheckControlType(buf, uiControlType());
		},
		"uiCheckControlType(): object passed in not a uiControl",
	},
	{
		"uiCheckControlType() with uiControl without uiControlType() marker",
		[](void) {
			uiCheckControlType(uiprivTestHookControlWithInvalidControlMarker(), uiControlType());
		},
		"uiCheckControlType(): object passed in not a uiControl",
	},
	{
		"uiCheckControlType() with a control with an unknown type",
		[](void) {
			uiCheckControlType(uiprivTestHookControlWithInvalidType(), testControlType);
		},
		"uiCheckControlType(): unknown uiControl type 5 found in uiControl (this is likely not a real uiControl or some data is corrupt)",
	},
	{
		"uiCheckControlType() with a control with an unknown type, even with uiControlType()",
		[](void) {
			uiCheckControlType(uiprivTestHookControlWithInvalidType(), uiControlType());
		},
		"uiCheckControlType(): unknown uiControl type 5 found in uiControl (this is likely not a real uiControl or some data is corrupt)",
	},
	{
		"uiCheckControlType() asking for an unknown control type",
		[](void) {
			uiControl *c;

			c = uiNewControl(testControlType, NULL);
			uiCheckControlType(c, 5);
			uiControlFree(c);
		},
		"uiCheckControlType(): unknown uiControl type 5 requested",
	},
	{
		"uiCheckControlType() with a type mismatch",
		[](void) {
			uiControl *c;

			c = uiNewControl(testControlType, NULL);
			uiCheckControlType(c, testControlType2);
			uiControlFree(c);
		},
		"uiCheckControlType(): wrong uiControl type passed: got TestControl, want TestControl2",
	},

	{
		"uiNewControl() with uiControlType()",
		[](void) {
			uiNewControl(uiControlType(), NULL);
		},
		"uiNewControl(): uiControlType() passed in when specific control type needed",
	},
	{
		"uiNewControl() with an unknown control type",
		[](void) {
			uiNewControl(5, NULL);
		},
		"uiNewControl(): unknown uiControl type 5 requested",
	},
	{
		"uiNewControl() with Init() failing",
		[](void) {
			uiNewControl(testControlType, testControlFailInit);
		},
		"uiNewControl(): invalid init data for TestControl",
	},

	{
		"uiControlFree() with a NULL uiControl",
		[](void) {
			uiControlFree(NULL);
		},
		"uiControlFree(): invalid null pointer for uiControl",
	},
	{
		"uiControlFree() with a uiControl that still has a parent",
		[](void) {
			uiControl *c, *d;

			c = uiNewControl(testControlType, NULL);
			d = uiNewControl(testControlType, NULL);

			// this should fail
			uiControlSetParent(c, d);
			uiControlFree(c);

			uiControlSetParent(c, NULL);
			uiControlFree(d);
			uiControlFree(c);
		},
		"uiControlFree(): cannot be called on a control with has a parent",
	},

	{
		"uiControlSetParent() with a NULL uiControl",
		[](void) {
			uiControlSetParent(NULL, NULL);
		},
		"uiControlSetParent(): invalid null pointer for uiControl",
	},
	{
		"uiControlSetParent() to unparent an already parentless control",
		[](void) {
			uiControl *c;

			c = uiNewControl(testControlType, NULL);
			uiControlSetParent(c, NULL);
			uiControlFree(c);
		},
		"uiControlSetParent(): cannot set a control with no parent to have no parent",
	},
	{
		"uiControlSetParent() to reparent an already parented control",
		[](void) {
			uiControl *c, *d, *e;

			c = uiNewControl(testControlType, NULL);
			d = uiNewControl(testControlType, NULL);
			e = uiNewControl(testControlType, NULL);

			// this should fail
			uiControlSetParent(c, d);
			uiControlSetParent(c, e);

			uiControlSetParent(c, NULL);
			uiControlFree(e);
			uiControlFree(d);
			uiControlFree(c);
		},
		"uiControlSetParent(): cannot set a control with a parent to have another parent",
	},

	{
		"uiControlImplData() with a NULL uiControl",
		[](void) {
			uiControlImplData(NULL);
		},
		"uiControlImplData(): invalid null pointer for uiControl",
	},
	{ NULL, NULL, NULL },
};

testingTest(ControlErrors)
{
	checkProgrammerErrors(t, casesBeforeOSVtable);
	checkProgrammerErrors(t, controlOSVtableCases);
	checkProgrammerErrors(t, casesAfterOSVtable);
}