#include "component_action_spawn.h"

/* 
	DEPENDENCIES:
	EntityCupper
	EntityChip
	EntityCustarpedo
	EntityCrumbler
	EntityChipper
	EntityExcorsant
*/

static GameEntityType _game_entity_type_get(ComponentActionSpawn* self, ECS* ecs);
static void _spawn_timer_set(ComponentActionSpawn* self, ECS* ecs);
static void _spawn_entity(ComponentActionSpawn* self, ECS* ecs);
static void _spawn_entity_out_of_bounds_position_get(ComponentActionSpawn* self, ECS* ecs, GameEntityType type, vec3 position, vec2 size, u32 id);
static void _spawn_entity_in_bounds_position_get(ComponentActionSpawn* self, ECS* ecs, GameEntityType type, vec3 position, vec2 size, u32 id);

/* Sets the spawn timer. */
static void
_spawn_timer_set(ComponentActionSpawn* self, ECS* ecs)
{	
	ComponentLevel* level;

	level = ecs_component_get(ecs, ECS_COMPONENT_LEVEL, self->component.id);

	self->timer = COMPONENT_ACTION_SPAWN_TIMER_BASE - (COMPONENT_ACTION_SPAWN_TIMER_LEVEL_MULTIPLIER * (level->value - 1));

	self->timer = MIN(self->timer, COMPONENT_ACTION_SPAWN_TIMER_MINIMUM);
}

/* Based on spawn rarity, randomly select an entity type to spawn. */
static GameEntityType
_game_entity_type_get(ComponentActionSpawn* self, ECS* ecs)
{
	GameEntityType type;
	Vector selectTypes;

	/* No point in doing this with only one type. */
	if (self->types.count == 1)
	{
		type = *(GameEntityType*)vector_get(&self->types, 0);
		return type;
	}

	vector_init(&selectTypes, sizeof(GameEntityType));

	/* Entities have different spawn rates. */
	/* Populate a list based on frequency (i.e., SPAWN_RARITY_COMMON types will appear 3x
	 * more likely than SPAWN_RARITY_RARE). */
	for (s32 i = 0; i < (s32)self->types.count; i++)
	{
		SpawnRarityType spawnRarityType;
		GameEntityType gameEntityType;
		u32 spawnCheckCount;

		gameEntityType = *(GameEntityType*)vector_get(&self->types, i);

		spawnRarityType = SPAWN_GAME_ENTITY_RARITY_TYPES[gameEntityType];

		spawnCheckCount = (SPAWN_RARITY_COUNT - spawnRarityType) + 1;

		for (s32 j = 0; j < (s32)spawnCheckCount; j++)
			vector_push(&selectTypes, &gameEntityType);
	}

	type = *(GameEntityType*)vector_get(&selectTypes, RANDOM_S32(0, selectTypes.count - 1));

	vector_free(&selectTypes);
	
	return type;
}

/* Spawns an entity out of bounds. */
static void
_spawn_entity_out_of_bounds_position_get(ComponentActionSpawn* self, ECS* ecs, GameEntityType type, vec3 position, vec2 size, u32 id)
{
	SpawnDirection spawnDirection;

	spawnDirection = RANDOM_S32(SPAWN_DIRECTION_NORTH, SPAWN_DIRECTION_WEST);

	switch (spawnDirection)
	{
		case SPAWN_DIRECTION_NORTH:
			position[0] = RANDOM_F32(SPAWN_OUT_OF_BOUNDS_RECTANGLE.x - (size[0] * 3), SPAWN_OUT_OF_BOUNDS_RECTANGLE.x + SPAWN_OUT_OF_BOUNDS_RECTANGLE.w + (size[0] * 3)); 
			position[1] = SPAWN_OUT_OF_BOUNDS_RECTANGLE.y - (size[1] * 3);
			break;
		case SPAWN_DIRECTION_EAST:
			position[0] = SPAWN_OUT_OF_BOUNDS_RECTANGLE.x + SPAWN_OUT_OF_BOUNDS_RECTANGLE.w + (size[0] * 3);
			position[1] = RANDOM_F32(SPAWN_OUT_OF_BOUNDS_RECTANGLE.y - (size[1] * 3), SPAWN_OUT_OF_BOUNDS_RECTANGLE.y + SPAWN_OUT_OF_BOUNDS_RECTANGLE.h + (size[1] * 3)); 
			break;
		case SPAWN_DIRECTION_SOUTH:
			position[0] = RANDOM_F32(SPAWN_OUT_OF_BOUNDS_RECTANGLE.x - (size[0] * 3), SPAWN_OUT_OF_BOUNDS_RECTANGLE.x + SPAWN_OUT_OF_BOUNDS_RECTANGLE.w + (size[0] * 3)); 
			position[1] = SPAWN_OUT_OF_BOUNDS_RECTANGLE.y + SPAWN_OUT_OF_BOUNDS_RECTANGLE.h + (size[1] * 3);
			break;
		case SPAWN_DIRECTION_WEST:
			position[0] = SPAWN_OUT_OF_BOUNDS_RECTANGLE.x - (size[0] * 3);
			position[1] = RANDOM_F32(SPAWN_OUT_OF_BOUNDS_RECTANGLE.y - (size[1] * 3), SPAWN_OUT_OF_BOUNDS_RECTANGLE.y + SPAWN_OUT_OF_BOUNDS_RECTANGLE.h + (size[1] * 3)); 
			break;
	}

}

/* Spawns an entity in bounds. */
static void
_spawn_entity_in_bounds_position_get(ComponentActionSpawn* self, ECS* ecs, GameEntityType type, vec3 position, vec2 size, u32 id)
{
	position[0] = RANDOM_F32
	(
		SPAWN_IN_BOUNDS_RECTANGLE.x,
		SPAWN_IN_BOUNDS_RECTANGLE.x + SPAWN_IN_BOUNDS_RECTANGLE.w
	); 

	position[1] = RANDOM_F32
	(
		SPAWN_IN_BOUNDS_RECTANGLE.y, 
		SPAWN_IN_BOUNDS_RECTANGLE.y + SPAWN_IN_BOUNDS_RECTANGLE.h
	); 
}

/* Initialize and position the spawn enemy. */
static void
_spawn_entity(ComponentActionSpawn* self, ECS* ecs)
{
	vec3 position;
	vec2 size;
	u32 id;
	GameEntityType type;
	SpawnEntryType spawnEntryType;
	ComponentLevel* level;
	u32 usedLevel;

	id = ecs_entity_add(ecs);
	vector_push(&self->entities, &id);

	glm_vec2_zero(size);
	glm_vec3_zero(position);

	type = _game_entity_type_get(self, ecs);

	level = ecs_component_get(ecs, ECS_COMPONENT_LEVEL, self->component.id);

	usedLevel = level->value + self->levelBonus;

	spawnEntryType = SPAWN_GAME_ENTITY_ENTRY_TYPES[type];

	switch (type)
	{
		case GAME_ENTITY_CUPPER:
			glm_vec2_copy((f32*)CUPPER_SIZE, size);
			break;
		case GAME_ENTITY_CHIP:
			glm_vec2_copy((f32*)CHIP_SIZE, size);
			break;
		case GAME_ENTITY_CHIPPER:
			glm_vec2_copy((f32*)CHIPPER_SIZE, size);
			break;
		case GAME_ENTITY_CUSTARPEDO:
			glm_vec2_copy((f32*)CUSTARPEDO_SIZE, size);
			break;
		case GAME_ENTITY_CRUMBLER:
			glm_vec2_copy((f32*)CRUMBLER_SIZE, size);
			break;
		case GAME_ENTITY_EXCORSANT:
			glm_vec2_copy((f32*)EXCORSANT_SIZE, size);
			break;
		default:
			break;
	}

	switch (spawnEntryType)
	{
		case SPAWN_ENTRY_WANDER:
			_spawn_entity_out_of_bounds_position_get(self, ecs, type, position, size, id);
			break;
		case SPAWN_ENTRY_FALL:
		case SPAWN_ENTRY_EMERGE:
			_spawn_entity_in_bounds_position_get(self, ecs, type, position, size, id);
			break;
		default:
			break;
	}

	switch (type)
	{
		case GAME_ENTITY_CUPPER:
			entity_cupper_init(ecs, id, position, self->targetID, usedLevel, &self->entities);
			break;
		case GAME_ENTITY_CHIP:
			entity_chip_init(ecs, id, position, -1);
			break;
		case GAME_ENTITY_CHIPPER:
			entity_chipper_init(ecs, id, position, self->targetID, usedLevel, &self->entities);
			break;
		case GAME_ENTITY_CUSTARPEDO:
			entity_custarpedo_init(ecs, id, position, self->targetID, usedLevel, &self->entities);
			break;
		case GAME_ENTITY_CRUMBLER:
			entity_crumbler_init(ecs, id, position, usedLevel, &self->entities);
			break;
		case GAME_ENTITY_EXCORSANT:
			entity_excorsant_init(ecs, id, position, self->targetID, usedLevel, &self->entities);
			break;
		default:
			break;
	}
}



/* Ticks action_spawn component. */
void
component_action_spawn_add(ComponentActionSpawn* self, ECS* ecs)
{
	vector_init(&self->types, sizeof(GameEntityType));
	vector_init(&self->entities, sizeof(u32));
	ecs_component_add(ecs, ECS_COMPONENT_LEVEL, self->component.id);
	ecs_component_add(ecs, ECS_COMPONENT_WAVE, self->component.id);
	_spawn_timer_set(self, ecs);
}

/* Ticks action_spawn component. */
void
component_action_spawn_delete(ComponentActionSpawn* self, ECS* ecs)
{
	vector_free(&self->types);
	vector_free(&self->entities);
	ecs_component_delete(ecs, ECS_COMPONENT_LEVEL, self->component.id);
	ecs_component_delete(ecs, ECS_COMPONENT_WAVE, self->component.id);
}

/* Ticks action_spawn component. */
void
component_action_spawn_init(ComponentActionSpawn* self, u32 targetID)
{
	self->targetID = targetID;
}

/* Adds the currently spawning entities from a wave. */
void
component_action_spawn_wave_add(ComponentActionSpawn* self, ECS* ecs, const SpawnWave* spawnWave)
{
	ComponentWave* wave;
	bool isTypeExist;

	wave = ecs_component_get(ecs, ECS_COMPONENT_WAVE, self->component.id);

	isTypeExist = false;

	/* Only get new types from the types parameter; discard redundant ones. */
	for (s32 i = 0; i < (s32)spawnWave->typeCount; i++)
	{
		GameEntityType type;

		type = spawnWave->types[i];

		for (s32 j = 0; j < (s32)self->types.count; j++)
		{
			GameEntityType checkType;

			checkType = *(GameEntityType*)vector_get(&self->types, j);

			if (checkType == type)
			{
				isTypeExist = true;
				break;
			}
		}

		if (isTypeExist)
		{
			isTypeExist = false;
			continue;
		}

		vector_push(&self->types, &type);
	}

	self->combatTimer += spawnWave->timer;
	self->isCombat = true;
	wave->value++;
}

/* Ticks action_spawn component. */
void
component_action_spawn_tick(ComponentActionSpawn* self, ECS* ecs)
{
	if (self->isCombat)
	{
		self->combatTimer--;
		self->timer--;

		if (self->timer <= 0)
		{
			self->timer = 0;

			if (self->combatTimer > 0)
			{
				_spawn_entity(self, ecs);
				_spawn_timer_set(self, ecs);
			}
		}

		if (self->combatTimer <= 0 )
			self->combatTimer = 0;
	}

	if (self->combatTimer == 0 && self->entities.count == 0)
	{
		vector_clear(&self->types);
		self->levelBonus = 0;
		self->isCombat = false;
		return;
	}


}
