#include "PlayerStates.h"
#include "Player.h"
#include "Layers.h"
#include "physics_world.h"

static inline
void InternalSpriteFlipWithMovement(Player* self) {
    if(self->moveInput.x > 0.f)
        self->facing = 1;
    if(self->moveInput.x < -0.1f)
        self->facing = -1;
    sprite_flip_horizontal(self->sprite, self->facing != 1);
}

void PlayerAnimationExit(Player* self) {
    self->animationTriggers = 0;
}

void PlayerIdleEnter(Player* self) {
    self->currentAnimation = self->idle;
    animation_sprite_play_from(self->currentAnimation, 0.f);
}

const State* PlayerIdleUpdate(Player* self, float deltaTime) {
    if(!veqf(self->moveInput, ZeroVector))
        return PlayerWalk();
    if(self->attackInput)
        return PlayerJabA();
    return PlayerIdle();
}

void PlayerWalk_Enter(Player* self) {
    self->currentAnimation = self->walk;
    animation_sprite_play_from(self->currentAnimation, 0.f);
}

const State* PlayerWalk_Update(Player* self, float deltaTime) {
    rigidbody_set_velocity(self->rigidbody, vmulf(vnormalizedf(self->moveInput), PLAYER_SPEED));

    if(veqf(self->moveInput, ZeroVector))
        return PlayerIdle();
    if(self->attackInput)
        return PlayerJabA();
    InternalSpriteFlipWithMovement(self);
    return PlayerWalk();
}

void PlayerAttackEnter(Player* self) {
    self->attackInput = 0;
    rigidbody_set_velocity(self->rigidbody, ZeroVector);
    InternalSpriteFlipWithMovement(self);
}

static
void PlayerAttackTrigger(Player* self) {
    Collider* found = physics_world_box_query(vaddf(self->transform.position, MakeVector(self->facing * (0.2f + 0.1f), 0.f)),
        MakeVector(0.1f, 0.06f), PHYSICS_LAYER_COMBAT, self->rigidbody);
    if(found != NULL) {
        PhysicsEntity entity = collider_get_owner(found);
        entity.message_receiver->handle_message(entity.data, 1, (void*)1u);
    }
    ++self->animationTriggers;
}

void PlayerJabA_Enter(Player* self) {
    PlayerAttackEnter(self);
    self->currentAnimation = self->jab_a;
    animation_sprite_play_from(self->currentAnimation, 0.f);
}

const State* PlayerJabA_Update(Player* self, float deltaTime) {
    const float ntime = animation_sprite_get_time_normalized(self->currentAnimation);
    if(self->animationTriggers == 0 && ntime > 0.5f)
        PlayerAttackTrigger(self);
    if(self->attackInput && ntime > 1.0f)
        return PlayerJabB();
    if(!veqf(self->moveInput, ZeroVector) && ntime > 1.05f)
        return PlayerWalk();
    if(ntime >= 2.0f)
        return PlayerIdle();
    return PlayerJabA();
}

void PlayerJabB_Enter(Player* self) {
    PlayerAttackEnter(self);
    self->currentAnimation = self->jab_b;
    animation_sprite_play_from(self->currentAnimation, 0.f);
}

const State* PlayerJabB_Update(Player* self, float deltaTime) {
    const float ntime = animation_sprite_get_time_normalized(self->currentAnimation);
    if(self->animationTriggers == 0 && ntime > 0.5f)
        PlayerAttackTrigger(self);
    if(self->attackInput && ntime > 1.0f)
        return PlayerJabA();
    if(!veqf(self->moveInput, ZeroVector) && ntime > 1.05f)
        return PlayerWalk();
    if(ntime >= 2.0f)
        return PlayerIdle();
    return PlayerJabB();
}