#include "ecs.h"

static void _ecs_function(ECS* self, ECSFunctionType type);
static s32 _draw_sort(DrawEntry* a, DrawEntry* b);

/* Sorts drawn elements by their Z value. */
static s32
_draw_sort(DrawEntry* a, DrawEntry* b)
{
	return a->drawObject.position[2] > b->drawObject.position[2] ? -1 : 1;
}

/* Executes a function upon an ECS. */
/* If a component does not have the function specfied, skips. */
static void
_ecs_function(ECS* self, ECSFunctionType type)
{
	for (s32 i = 0; i < ECS_COMPONENT_COUNT; i++)
	{
		ECSComponentList* list;

		list = &self->lists[i];

		ecs_component_list_function(list, type);
	}
}

/* Ticks the ECS. */
void
ecs_tick(ECS* self)
{
	_ecs_function(self, ECS_FUNCTION_TICK);
}

/* Draws the ECS. */
/* By Z coordinate, sorts drawable objects, and draws them in order. */
void
ecs_draw(ECS* self)
{
	Vector drawEntries; /* DrawEntry */

	vector_init(&drawEntries, sizeof(DrawEntry));

	for (s32 i = 0; i < ECS_COMPONENT_COUNT; i++)
	{
		ECSFunction drawFunction;
		ECSComponentList list;

		list = self->lists[i];

		drawFunction = list.system.functions[ECS_FUNCTION_DRAW];

		if (drawFunction)
		{
			for (s32 j = 0; j < (s32)list.components.count; j++)
			{
				DrawEntry drawEntry;
				DrawObject drawObject;
				void* component;

				component = vector_get(&list.components, j);

				memcpy(&drawObject, component, sizeof(DrawObject));

				drawEntry.drawObject = drawObject;
				drawEntry.drawFunction = drawFunction;
				drawEntry.componentPointer = component;

				if (drawObject.component.isDisabled)
					continue;

				vector_push(&drawEntries, &drawEntry);
			}
		}
	}

	/* Sort the drawable components. */
	vector_sort(&drawEntries, (SortCompareFunction)_draw_sort);

	for (s32 i = 0; i < (s32)drawEntries.count; i++)
	{
		ECSFunction drawFunction;
		DrawEntry* drawEntry;

		drawEntry = vector_get(&drawEntries, i);

		drawFunction = drawEntry->drawFunction;

		drawFunction(drawEntry->componentPointer, self);
	}

	vector_free(&drawEntries);
}

/* Initializes ECS. */
void
ecs_init(ECS* self, Game* game)
{
	self->game = game;

	for (s32 i = 0 ; i < ECS_COMPONENT_COUNT; i++)
		ecs_component_list_init(&self->lists[i], self, ECS_COMPONENT_INFO[i]);
}

/* Clears ECS (removes all components, but does not free the greater ECS struct). */
void
ecs_clear(ECS* self)
{
	for (s32 i = 0 ; i < ECS_COMPONENT_COUNT; i++)
		ecs_component_list_clear(&self->lists[i]);
}

/* Frees ECS. */
void
ecs_free(ECS* self)
{
	for (s32 i = 0 ; i < ECS_COMPONENT_COUNT; i++)
		ecs_component_list_free(&self->lists[i]);

	memset(self, '\0', sizeof(ECS));
}
