#pragma once

// remove this ifdef when client or menu will actually make use of this stuff
#ifdef SVQC

/// If this global exists, only functions with spawnfunc_ name prefix qualify as spawn functions
noref bool require_spawnfunc_prefix;
.bool spawnfunc_checked;
/// Not for production use, provides access to a dump of the entity's fields when it is parsed from map data
noref string __fullspawndata;
.string fullspawndata;

// Optional type checking; increases compile time too much to be enabled by default
#if 0
	bool entityfieldassignablefromeditor(int i)
	{
		switch (entityfieldtype(i))
		{
			case FIELD_STRING:
			case FIELD_FLOAT:
			case FIELD_VECTOR:
				return true;
		}
		return false;
	}

	#define _spawnfunc_checktypes(fld) \
		if (s == #fld) \
		if (!entityfieldassignablefromeditor(i)) \
			LOG_FATALF("Entity field '%s' cannot be whitelisted", s);
#else
	#define _spawnfunc_checktypes(fld)
#endif
	#define _spawnfunc_check(fld) \
		if (s == #fld) \
			continue;

	noref int __spawnfunc_expecting;
	noref entity __spawnfunc_expect;
	noref bool __spawnfunc_unreachable_workaround = true;

	.void(entity) __spawnfunc_constructor;
	noref IntrusiveList g_spawn_queue;

	#define SPAWNFUNC_INTERNAL_FIELDS(X) \
		X(string, classname, "spawnfunc") \
		X(string, target, string_null) \
		X(string, target2, string_null) \
		X(string, target3, string_null) \
		X(string, target4, string_null) \
		X(string, targetname, string_null) \
		/**/

	#define X(T, fld, def) .T fld, __spawnfunc_##fld;
	SPAWNFUNC_INTERNAL_FIELDS(X)
	#undef X

	void __spawnfunc_defer(entity prototype, void(entity) constructor)
	{
		IL_PUSH(g_spawn_queue, prototype);
		#define X(T, fld, def) \
		{ \
			prototype.__spawnfunc_##fld = prototype.fld; \
			prototype.fld = def; \
		}
		SPAWNFUNC_INTERNAL_FIELDS(X);
		#undef X
		prototype.__spawnfunc_constructor = constructor;
	}

	noref IntrusiveList g_map_entities;
	#define __spawnfunc_spawn_all() MACRO_BEGIN \
		g_map_entities = IL_NEW(); \
		IL_EACH(g_spawn_queue, true, __spawnfunc_spawn(it)); \
	MACRO_END
#ifdef SVQC
	void _SV_OnEntityPreSpawnFunction(entity this);
#endif
	void __spawnfunc_spawn(entity prototype)
	{
		entity e = new(clone);
		copyentity_qc(prototype, e);
		IL_PUSH(g_map_entities, e);
		#define X(T, fld, def) \
		{ \
			e.fld = e.__spawnfunc_##fld; \
			e.__spawnfunc_##fld = def; \
		}
		SPAWNFUNC_INTERNAL_FIELDS(X);
		#undef X
#ifdef SVQC
		_SV_OnEntityPreSpawnFunction(e);
		if (wasfreed(e))
			return;
#endif
		e.__spawnfunc_constructor(e);
	}

/// This function simply avoids expanding IL_NEW during compilation for each spawning entity
void g_spawn_queue_spawn()
{
	g_spawn_queue = IL_NEW();
}

noref bool __spawnfunc_first;

#define spawnfunc(id) \
	void __spawnfunc_##id(entity this); \
	ACCUMULATE void spawnfunc_##id(entity this) \
	{ \
		if (!__spawnfunc_first) \
		{ \
			__spawnfunc_first = true; \
			static_init_early(); \
		} \
		bool dospawn = true; \
		if (__spawnfunc_expecting > 1) \
			__spawnfunc_expecting = 0; \
		else if (__spawnfunc_expecting) \
		{ \
			/* engine call */ \
			if (!g_spawn_queue) \
				g_spawn_queue_spawn(); \
			__spawnfunc_expecting = 0; \
			this = __spawnfunc_expect; \
			__spawnfunc_expect = NULL; \
			dospawn = false; \
		} \
		else \
			/* userland call */ \
			assert(this); \
		if (!this.sourceLoc) \
			this.sourceLoc = __FILE__":"STR(__LINE__); \
		this.classname = #id; \
		if (!this.spawnfunc_checked) \
		{ \
			if (__fullspawndata) \
				/* not supported in old DP */ \
				/* must be read inside the real spawnfunc */ \
				this.fullspawndata = __fullspawndata; \
			this.spawnfunc_checked = true; \
			if (this) \
			{ \
				/* not worldspawn, delay spawn */ \
				/* clear some dangerous fields (TODO: properly support these in the map!) */ \
				this.think = func_null; \
				this.nextthink = 0; \
				__spawnfunc_defer(this, __spawnfunc_##id); \
			} \
			else \
				/* world might not be "worldspawn" */ \
				this.__spawnfunc_constructor = __spawnfunc_##id; \
		} \
		if (dospawn) \
			__spawnfunc_##id(this); \
		if (__spawnfunc_unreachable_workaround) \
			return; \
	} \
	void __spawnfunc_##id(entity this)

#endif // SVQC
