#include "component_action_damage.h"

/* DEPENDENCIES: Physics, Health */

static void _damage_entries_tick(ComponentActionDamage* self);
static bool _entity_in_damaged_entities(ComponentActionDamage* self, u32 id);
static void _damage_check(ComponentActionDamage* self, ECS* ecs);
static void _damage_deal(ComponentActionDamage* self, ECS* ecs, u32 id);

/* After a specified amount of time, previously damaged entities can be damaged again. */
static void
_damage_entries_tick(ComponentActionDamage* self)
{
	for (s32 i = 0; i < (s32)self->damageEntries.count; i++)
	{
		DamageEntry* damageEntry;

		damageEntry = vector_get(&self->damageEntries, i);

		damageEntry->timer--;

		if (damageEntry->timer <= 0)
			vector_remove(&self->damageEntries, i);
	}
}

/* Check to see if this entity has been damaged prior. */
static bool
_entity_in_damaged_entities(ComponentActionDamage* self, u32 id)
{
	for (s32 i = 0; i < (s32)self->damageEntries.count; i++)
	{
		DamageEntry* damageEntry;
		u32 checkID;

		damageEntry = vector_get(&self->damageEntries, i);
		checkID = damageEntry->id;

		if (checkID == id)
			return true;
	}

	return false;
}

/* Ticks health down and changes other stats. */
static void
_damage_deal(ComponentActionDamage* self, ECS* ecs, u32 id)
{
	ComponentHealth* health;
	ComponentInvincibility* invincibility;
	ComponentGameEntityType* gameEntityType;
	ComponentSprite* sprite;
	ComponentStun* stun;
	ComponentActionOnDeleteDropGoodies* actionOnDeleteDropGoodies;
	DamageEntry damageEntry;
	vec3 position;
	vec2 size;
	u32 damageNumber;

	health = ecs_component_get(ecs, ECS_COMPONENT_HEALTH, id);
	invincibility = ecs_component_get(ecs, ECS_COMPONENT_INVINCIBILITY, id);
	gameEntityType = ecs_component_get(ecs, ECS_COMPONENT_GAME_ENTITY_TYPE, id);
	actionOnDeleteDropGoodies = ecs_component_get(ecs, ECS_COMPONENT_ACTION_ON_DELETE_DROP_GOODIES, id);
	stun = ecs_component_get(ecs, ECS_COMPONENT_STUN, id);
	sprite = ecs_component_get(ecs, ECS_COMPONENT_SPRITE, id);

	damageNumber = ecs_entity_add(ecs);

	if (invincibility)
	{
		if (invincibility->isInvincible)
			return;

		invincibility->isInvincible = true;
		invincibility->value = invincibility->max;
	}

	if (stun)
	{
		stun->isStun = true;
		stun->value = stun->max;
	}


	health->value -= self->value;

	if (health->value < 0)
	{
		health->value = 0;
		health->isDead = true;

		if (actionOnDeleteDropGoodies)
		{
			u32 overkillCount;

			overkillCount = self->value / health->max;

			overkillCount = MAX(overkillCount, actionOnDeleteDropGoodies->count);

			//actionOnDeleteDropGoodies->count += overkillCount;
		}
	}

	damageEntry.id = id;
	damageEntry.timer = self->damageTimer;

	glm_vec3_copy(sprite->position, position);

	component_sprite_size_get(sprite, size);

	position[1] -= size[1];
	position[2] = 0.0f;

	entity_damage_number_init
	(
		ecs,
		damageNumber,
		position,
		self->value
	);

	vector_push(&self->damageEntries, &damageEntry);

	switch (gameEntityType->type)
	{
		case GAME_ENTITY_PLAYER:
			sound_play(&ecs->game->resources.sounds[SOUND_PLAYER_HURT], SOUND_PRIORITY);
			break;
		default:
			sound_play(&ecs->game->resources.sounds[SOUND_HURT], SOUND_NO_PRIORITY);
			break;

	}
}

/* Checks for collided enemies to damage. */
static void
_damage_check(ComponentActionDamage* self, ECS* ecs)
{
	ComponentPhysics* physics;
	ComponentHealth* health;
	ComponentAllegiance* allegiance;

	physics = ecs_component_get(ecs, ECS_COMPONENT_PHYSICS, self->component.id);

	for (s32 i = 0; i < (s32)physics->collisions.count; i++)
	{	
		u32 id;

		id = *(u32*)vector_get(&physics->collisions, i);

		health = ecs_component_get(ecs, ECS_COMPONENT_HEALTH, id);
		allegiance = ecs_component_get(ecs, ECS_COMPONENT_ALLEGIANCE, id);

		if (!health || !allegiance)
			continue;

		if 
		(
			allegiance->type == self->attackType &&
			!_entity_in_damaged_entities(self, id) &&
			!health->component.isDisabled
		)
			_damage_deal(self, ecs, id);
	}
}

/* Runs on add action damage component. */
void
component_action_damage_add(ComponentActionDamage* self, ECS* ecs)
{
	vector_init(&self->damageEntries, sizeof(DamageEntry));
}

/* Runs on delete action damage component. */
void
component_action_damage_delete(ComponentActionDamage* self, ECS* ecs)
{
	vector_free(&self->damageEntries);
}

/* Sets action_damage component info. */
void
component_action_damage_init(ComponentActionDamage* self, AllegianceType attackType, u32 damageTimer, u32 value)
{
	self->attackType = attackType;
	self->damageTimer = damageTimer;
	self->value = value;
}

/* Ticks action_damage component. */
void
component_action_damage_tick(ComponentActionDamage* self, ECS* ecs)
{
	_damage_entries_tick(self);
	_damage_check(self, ecs);
}
