#include "Enemy.h"
#include "Layers.h"
#include "debug.h"
#include "game_world.h"
#include "physics.h"
#include "physics_world.h"
#include "sprite.h"

START_REFLECT(Enemy)
    REFLECT_TYPECLASS(Enemy, Transformable)
    REFLECT_TYPECLASS(Enemy, Drop)
    REFLECT_TYPECLASS(Enemy, PhysicsEntity)
    REFLECT_TYPECLASS(Enemy, BehaviourEntity)
    REFLECT_TYPECLASS(Enemy, Damagable)
END_REFLECT(Enemy)

impl_Transformable_for(Enemy,
    EnemyGetTransform
)

impl_Drop_for(Enemy,
    EnemyDestroy
)

impl_BehaviourEntity_for(Enemy,
    EnemyStart,
    EnemyUpdate,
    EnemyDraw,
    EnemyGetDepth
)

impl_PhysicsEntity_for(Enemy,
    EnemyGetRigidBody,
    EnemyOnCollision,
    EnemyOnOverlap
)

impl_Damagable_for(Enemy,
    EnemyDamage
)

Enemy* MakeEnemy() {
    Enemy* self = malloc(sizeof(Enemy));
    ASSERT_RETURN(self != NULL, NULL, "Failed to allocate Enemy");
    *self = (Enemy){
        .transform = IdentityTransform,
        .behaviour = NULL,
        .rigidbody = NULL,
        .collider = NULL,
        .hitbox = NULL,
        .sprite = sprite_new_empty(),
        .facing = 1,
        .stun_time = 0.f,
        .idleAnim = NULL,
        .walkAnim = NULL,
        .hurtAnim = NULL,
        .currentAnimation = NULL,
        .health = 15,
    };

    self->rigidbody = rigidbody_make(Enemy_as_PhysicsEntity(self));
    self->collider = collider_new(Enemy_as_PhysicsEntity(self), shape_new_square(MakeVector(0.2f, 0.05f)), 0,
                                  PHYSICS_LAYER_DEFAULT, PHYSICS_LAYER_DEFAULT);
    self->hitbox = collider_new(Enemy_as_PhysicsEntity(self), shape_new((Vector[]){
        MakeVector(-0.1f, -0.9f),
        MakeVector( 0.1f, -0.9f),
        MakeVector( 0.1f,  0.0f),
        MakeVector(-0.1f,  0.0f),
    }, 4), 1, PHYSICS_LAYER_COMBAT, 0x0);
    PhysicsEntity pe = Enemy_as_PhysicsEntity(self);
    LOG_INFO("enemy instantiated mirroring as: %s", pe.mirror->get_typestring(pe.data));

    sprite_set_origin(self->sprite, MakeVector(0.45f, 0.925f));

    self->idleAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Idle.png", IVectorFrom(512)), 1.5f, LoopMode_Loop);
    self->walkAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Walk.png", IVectorFrom(512)), 1.5f, LoopMode_Loop);
    self->hurtAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Hurt.png", IVectorFrom(512)), 5.f, LoopMode_Stop);

    return self;
}

Enemy* SpawnEnemy(Vector location, const State* entryState) {
    Enemy* self = MakeEnemy();
    self->behaviour = state_machine_init(self, entryState);
    rigidbody_get_transform(self->rigidbody)->position = location;
    game_world_add_entity(Enemy_as_BehaviourEntity(self));
    physics_world_add_entity(Enemy_as_PhysicsEntity(self));
    return self;
}

void EnemyStart(Enemy* self) {}

void EnemyUpdate(Enemy* self, float deltaTime) {
    state_machine_update(self->behaviour, deltaTime);
    if(self->stun_time > 0.0f)
        self->stun_time -= deltaTime;
}

void EnemyDraw(Enemy* self) {
    sprite_flip_horizontal(self->sprite, self->facing == -1);
    animation_sprite_draw(self->currentAnimation, &self->transform);
}

void EnemyDestroy(Enemy* self) {
    state_machine_destroy(self->behaviour);
    collider_destroy(self->collider);
    rigidbody_destroy(self->rigidbody);
    sprite_destroy(self->sprite);

    physics_world_remove_entity(Enemy_as_PhysicsEntity(self));
    free(self);
}

void EnemyOnCollision(Enemy* self, Collision collision) {}
void EnemyOnOverlap(Enemy* self, Collider* other) {}

Transform* EnemyGetTransform(Enemy* self) {
    return &self->transform;
}

RigidBody* EnemyGetRigidBody(Enemy* self) {
    return self->rigidbody;
}

int EnemyDamage(Enemy* self, DamageEventData* data) {
    if(self->stun_time > 0.f)
        return 0;
    self->health -= data->damageAmount;
    LOG_INFO("Damage received");
    if(self->health <= 0) {
        game_world_destroy_entity(Enemy_as_BehaviourEntity(self));
        return 1;
    } else {
        self->stun_time = data->stun;
        float direction = ((data->origin.x - self->transform.position.x) > 0) * 2 - 1;
        self->facing = direction;
        rigidbody_get_transform(self->rigidbody)->position = vaddf(self->transform.position, MakeVector(-direction * data->knockback, 0));
        return 0;
    }
}
