#pragma once

IntrusiveList g_events;
IntrusiveList g_components;
STATIC_INIT(components) { g_events = IL_NEW(); g_components = IL_NEW(); }

/** Components always interpolate from the previous state */
#define COMPONENT(com) \
	void com_##com##_interpolate(entity it, float a); \
	.bool com_##com

#define FOREACH_COMPONENT(com, body) IL_EACH(g_components, it.com_##com, body)


#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T

#define emit(T, ...) \
	MACRO_BEGIN \
	IL_EACH(g_events, it.evt_##T##_listener, it.evt_##T(__VA_ARGS__)); \
	MACRO_END

#define subscribe(listener, T, fn) \
	MACRO_BEGIN \
	listener.evt_##T = (fn); \
	listener.evt_##T##_listener = true; \
	IL_PUSH(g_events, listener); \
	MACRO_END


/**
 * framelimit 0 is no limit, interpolation does not apply
 * framerate below minfps will result in less than 100% speed
 */
#define SYSTEM(sys, frameLimit, minfps) \
	void sys_##sys##_update(entity this, float dt); \
	noref float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
	noref float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))

#define SYSTEM_UPDATE(sys) \
	MACRO_BEGIN \
	static float t = 0; \
	float dt = autocvar_xon_sys_##sys##_dt; \
	float minfps = autocvar_xon_sys_##sys##_minfps; \
	static float accumulator = 0; \
	float a = 0; \
	if (dt) { \
		accumulator += min(frametime, 1 / (minfps)); \
	} else { \
		accumulator += frametime; \
		dt = accumulator; \
		a = 1; \
	} \
	while (accumulator >= dt) \
	{ \
		time = t; \
		FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \
		t += dt; \
		accumulator -= dt; \
	} \
	if (!a) a = accumulator / dt; \
	FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \
	MACRO_END
