#include "component_game_object.h"

/* DEPENDENCIES: Sprite, Physics, EntityShadow */

static void _component_game_object_sprite_tick(ComponentGameObject* self, ECS* ecs);
static void _component_game_object_height_tick(ComponentGameObject* self, ECS* ecs);
static void _component_game_object_shadow_tick(ComponentGameObject* self, ECS* ecs);

/* Tick's a game object's sprite.. */
static void 
_component_game_object_sprite_tick(ComponentGameObject* self, ECS* ecs)
{
	ComponentSprite* sprite;
	ComponentPhysics* physics;
	f32 spriteYPercent;
	f32 spriteMinimumYOffset;

	vec2 size;

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

	component_sprite_size_get(sprite, size);
	
	glm_vec3_copy(physics->position, sprite->position);

	/* Sprite offsets will be set so that they will always be their size above the ground */
	sprite->offset[1] = -(size[1] / 2) - self->height;

	/* Used for creating an illusion of depth; 
	 * lower things are rendered above others, using GL_DEPTH_BUFFER */
	/* (0.9f, 1.0f) reserved for HUD elements, etc. */
	spriteYPercent = (sprite->position[1] / ecs->game->renderer.camera.orthographic.size[1]);

	sprite->position[2] = (1 - (COMPONENT_GAME_OBJECT_DEPTH_MAX * spriteYPercent)) + sprite->offset[2];
}

/* Tick's a game object's height and related properties. */
static void 
_component_game_object_height_tick(ComponentGameObject* self, ECS* ecs)
{
	if (self->isAffectedByGravity)
		self->heightVelocity += self->gravity;

	self->height += self->heightVelocity;

	if (self->height < 0.0f)
	{
		if (self->isBounce)
			self->heightVelocity = -self->heightVelocity * COMPONENT_GAME_OBJECT_BOUNCE_FRICTION;
		else
			self->heightVelocity = 0.0f;
	}
	
	self->height = self->height < 0.0f ? 0.0f : self->height;
	self->height = self->height > COMPONENT_GAME_OBJECT_HEIGHT_MAX ? COMPONENT_GAME_OBJECT_HEIGHT_MAX : self->height;

	self->isAirborne = self->height > 0.0f ? true : false;
}

/* Tick a game object's shadow. */
static void
_component_game_object_shadow_tick(ComponentGameObject* self, ECS* ecs)
{
	ComponentSprite* shadowSprite;
	ComponentSprite* sprite;
	ComponentPhysics* physics;
	f32 heightPercent;

	shadowSprite = ecs_component_get(ecs, ECS_COMPONENT_SPRITE, self->shadowID);
	sprite = ecs_component_get(ecs, ECS_COMPONENT_SPRITE, self->component.id);
	physics = ecs_component_get(ecs, ECS_COMPONENT_PHYSICS, self->component.id);

	glm_vec2_copy(physics->size, shadowSprite->size);
	glm_vec3_copy(physics->position, shadowSprite->position);

	shadowSprite->position[2] = sprite->position[2] + COMPONENT_GAME_OBJECT_SHADOW_Z_OFFSET;

	heightPercent = 1 - (self->height / COMPONENT_GAME_OBJECT_HEIGHT_MAX);

	glm_vec2_scale(shadowSprite->size, heightPercent, shadowSprite->size);

	glm_vec4_copy(sprite->color, shadowSprite->color);
}


/* Sets game_object component info. */
/* This assumes a one-frame sprite. */
void
component_game_object_init
(
	ComponentGameObject* self,
	ECS* ecs,
	Texture texture,
	vec2 size,
	vec2 physicsSize,
	vec3 position,
	f32 friction,
	f32 velocityMax,
	bool isBounce,
	bool isAffectedByGravity,
	bool isSolid
)
{
	ivec2 atlasSize;
	ivec2 frameSize;

	frameSize[0] = (s32)texture.size[0];
	frameSize[1] = (s32)texture.size[1];

	glm_ivec2_one(atlasSize);

	component_game_object_atlas_init
	(
		self,
		ecs,
		texture,
		frameSize,
		atlasSize,
		size,
		physicsSize,
		position,
		friction,
		velocityMax,
		isBounce,
		isAffectedByGravity,
		isSolid
	);
}

/* Sets game_object component info, with an atlas. */
void
component_game_object_atlas_init
(
	ComponentGameObject* self,
	ECS* ecs,
	Texture texture,
	ivec2 frameSize,
	ivec2 atlasSize,
	vec2 size,
	vec2 physicsSize,
	vec3 position,
	f32 friction,
	f32 velocityMax,
	bool isBounce,
	bool isAffectedByGravity,
	bool isSolid
)
{
	ComponentSprite* sprite;
	ComponentSprite* shadowSprite;
	ComponentPhysics* physics;
	
	sprite = ecs_component_get(ecs, ECS_COMPONENT_SPRITE, self->component.id);
	shadowSprite = ecs_component_get(ecs, ECS_COMPONENT_SPRITE, self->shadowID);
	physics = ecs_component_get(ecs, ECS_COMPONENT_PHYSICS, self->component.id);

	component_sprite_atlas_init
	(
		sprite, 
		texture, 
		frameSize, 
		atlasSize, 
		size, 
		position
	);

	component_physics_init
	(
		physics, 
		physicsSize, 
		position, 
		velocityMax, 
		friction, 
		isSolid
	);

	component_sprite_init
	(
		shadowSprite,
		ecs->game->resources.textures[TEXTURE_SHADOW],
		physics->size,
		physics->position
	);

	self->isBounce = isBounce;
	self->isAffectedByGravity = isAffectedByGravity;

	_component_game_object_sprite_tick(self, ecs);
	_component_game_object_shadow_tick(self, ecs);
	_component_game_object_height_tick(self, ecs);
}

/* Runs on game object add. */
void
component_game_object_add(ComponentGameObject* self, ECS* ecs)
{
	self->shadowID = ecs_entity_add(ecs);
	entity_shadow_init(ecs, self->shadowID);

	ecs_component_add(ecs, ECS_COMPONENT_PHYSICS, self->component.id);
	ecs_component_add(ecs, ECS_COMPONENT_SPRITE, self->component.id);
	
	self->gravity = COMPONENT_GAME_OBJECT_GRAVITY;
}

/* Runs on game object delete. */
void
component_game_object_delete(ComponentGameObject* self, ECS* ecs)
{
	ecs_entity_delete(ecs, self->shadowID);

	ecs_component_delete(ecs, ECS_COMPONENT_SPRITE, self->component.id);
	ecs_component_delete(ecs, ECS_COMPONENT_PHYSICS, self->component.id);
}

/* Ticks game_object component. */
void
component_game_object_tick(ComponentGameObject* self, ECS* ecs)
{
	_component_game_object_height_tick(self, ecs);
	_component_game_object_sprite_tick(self, ecs);
	_component_game_object_shadow_tick(self, ecs);
}
