#include "Player.h"
#include "PlayerStates.h"
#include "debug.h"
#include "game_world.h"
#include "input_axis.h"
#include "physics_world.h"

const Vector PLAYER_SPEED = MakeVector(1.0f, 0.70f);

static inline
void Internal_PlayerInitInput(Player* self) {
    playerinput_add(self->playerInput, CompositeAxis1D_as_InputAxis(compositeaxis1d_new(
        KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_A)),
        KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_D)),
        InputEvent_Float)), (InputDelegateFn)PlayerHorizontalInput);

    playerinput_add(self->playerInput, CompositeAxis1D_as_InputAxis(compositeaxis1d_new(
        KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_S)),
        KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_W)),
        InputEvent_Float)), (InputDelegateFn)PlayerVerticalInput);

    playerinput_add(self->playerInput, KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_J)), (InputDelegateFn)PlayerAttackInput);
}

Player* MakePlayer() {
    Player* self = malloc(sizeof(Player));
    ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for new Player instance");

    *self = (Player) {
        .transform = IdentityTransform,

        .rigidbody = NULL,
        .physicsCollider = NULL,

        .playerInput = playerinput_new(self, -1),
        .moveInput = ZeroVector,
        .attackInput = 0,
        .animationTriggers = 0,

        .facing = 1,

        .sprite = sprite_new_empty(),

        .idle = NULL,
        .walk = NULL,
        .jab_a = NULL,
        .jab_b = NULL,

        .animationStateMachine = NULL,
    };

    self->rigidbody = rigidbody_make(Player_as_PhysicsEntity(self));
    self->physicsCollider = collider_new(Player_as_PhysicsEntity(self), shape_new((Vector[]){
        MakeVector(-0.2, -0.065),
        MakeVector( 0.2, -0.065),
        MakeVector( 0.2,  0.065),
        MakeVector(-0.2,  0.065)
    }, 4), 0, PHYSICS_LAYER_DEFAULT);

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

    self->idle = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Idle.png", IVectorFrom(512)), 1.5f, LoopMode_Loop);
    self->walk = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Walk.png", IVectorFrom(512)), 5.f, LoopMode_Loop);
    self->jab_a = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Jab_A.png", IVectorFrom(512)), 10.f, LoopMode_Stop);
    self->jab_b = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Jab_B.png", IVectorFrom(512)), 10.f, LoopMode_Stop);

    self->animationStateMachine = state_machine_init(self, PlayerIdle());

    return self;
}

Player* SpawnPlayer(Vector location) {
    Player* self = MakePlayer();
    self->transform.position = location;

    game_world_add_entity(Player_as_BehaviourEntity(self));
    physics_world_add_entity(Player_as_PhysicsEntity(self));

    Internal_PlayerInitInput(self);
    return self;
}

void DestroyPlayer(Player* self) {
    physics_world_remove_entity(Player_as_PhysicsEntity(self));
    collider_destroy(self->physicsCollider);
    rigidbody_destroy(self->rigidbody);

    playerinput_drop(self->playerInput);

    animation_sprite_destroy(self->idle);
    animation_sprite_destroy(self->walk);
    animation_sprite_destroy(self->jab_a);
    animation_sprite_destroy(self->jab_b);
    sprite_destroy(self->sprite);

    state_machine_destroy(self->animationStateMachine);
}

void PlayerHorizontalInput(Player* self, InputEvent value) {
    self->moveInput.x = value.as_float;
}

void PlayerVerticalInput(Player* self, InputEvent value) {
    self->moveInput.y = -value.as_float;
}

void PlayerAttackInput(Player* self, InputEvent value) {
    if(value.as_bool)
        self->attackInput = 1;
}

void PlayerStart(Player* self) {}

void PlayerUpdate(Player* self, float deltaTime) {
    state_machine_update(self->animationStateMachine, deltaTime);
}

void PlayerDraw(Player* self) {
    animation_sprite_draw(self->currentAnimation, &self->transform);
    shape_draw(collider_get_shape(self->physicsCollider), self->transform);
}

Transform* PlayerGetTransform(Player* self) {
    return &self->transform;
}

RigidBody* PlayerGetRigidBody(Player* self) {
    return self->rigidbody;
}

void PlayerOnCollision(Player* self, Collision collision) {}
void PlayerOnOverlap(Player* self, Collider* other) {}
