// IWYU pragma: no_include <bits/std_abs.h>
#include "tabe.h"
#include <SDL3/SDL_surface.h> // Para SDL_FlipMode
#include <stdlib.h>           // Para rand, abs
#include <algorithm>          // Para max
#include "audio.h"            // Para JA_PlaySound
#include "param.h"            // Para Param, ParamGame, param
#include "resource.h"         // Para Resource
#include "utils.h"            // Para Zone

// Constructor
Tabe::Tabe()
    : sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("tabe.png"), Resource::get()->getAnimation("tabe.ani"))),
      timer_(TabeTimer(2.5f, 4.0f)) {}

// Actualiza la lógica
void Tabe::update()
{
    if (enabled_)
    {
        sprite_->update();
        move();
        updateState();
    }
    timer_.update();
    if (timer_.should_spawn())
    {
        enable();
    }
}

// Dibuja el objeto
void Tabe::render()
{
    if (enabled_)
    {
        sprite_->render();
    }
}

// Mueve el objeto
void Tabe::move()
{
    const int x = static_cast<int>(x_);
    speed_ += accel_;
    x_ += speed_;
    fly_distance_ -= std::abs(x - static_cast<int>(x_));

    // Comprueba si sale por los bordes
    const float min_x = param.game.game_area.rect.x - WIDTH_;
    const float max_x = param.game.game_area.rect.x + param.game.game_area.rect.w;
    switch (destiny_)
    {
    case TabeDirection::TO_THE_LEFT:
    {
        if (x_ < min_x)
        {
            disable();
        }
        if (x_ > param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_ && direction_ == TabeDirection::TO_THE_RIGHT)
        {
            setRandomFlyPath(TabeDirection::TO_THE_LEFT, 80);
            x_ = param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_;
        }
        break;
    }

    case TabeDirection::TO_THE_RIGHT:
    {
        if (x_ > max_x)
        {
            disable();
        }
        if (x_ < param.game.game_area.rect.x && direction_ == TabeDirection::TO_THE_LEFT)
        {
            setRandomFlyPath(TabeDirection::TO_THE_RIGHT, 80);
            x_ = param.game.game_area.rect.x;
        }
        break;
    }
    default:
        break;
    }

    if (fly_distance_ <= 0)
    {
        if (waiting_counter_ > 0)
        {
            accel_ = speed_ = 0.0f;
            --waiting_counter_;
        }
        else
        {
            constexpr int CHOICES = 4;
            const TabeDirection left[CHOICES] = {TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_RIGHT};
            const TabeDirection right[CHOICES] = {TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_RIGHT, TabeDirection::TO_THE_RIGHT, TabeDirection::TO_THE_RIGHT};
            const TabeDirection direction = destiny_ == TabeDirection::TO_THE_LEFT ? left[rand() % CHOICES] : right[rand() % CHOICES];
            setRandomFlyPath(direction, 20 + rand() % 40);
        }
    }

    shiftSprite();
}

// Habilita el objeto
void Tabe::enable()
{
    if (!enabled_)
    {
        enabled_ = true;
        has_bonus_ = true;
        hit_counter_ = 0;
        number_of_hits_ = 0;
        y_ = param.game.game_area.rect.y + 20.0f;

        // Establece una dirección aleatoria
        destiny_ = direction_ = rand() % 2 == 0 ? TabeDirection::TO_THE_LEFT : TabeDirection::TO_THE_RIGHT;

        // Establece la posición inicial
        x_ = (direction_ == TabeDirection::TO_THE_LEFT) ? param.game.game_area.rect.x + param.game.game_area.rect.w : param.game.game_area.rect.x - WIDTH_;

        // Crea una ruta de vuelo
        setRandomFlyPath(direction_, 60);
        shiftSprite();
    }
}

// Establece un vuelo aleatorio
void Tabe::setRandomFlyPath(TabeDirection direction, int lenght)
{
    direction_ = direction;
    fly_distance_ = lenght;
    waiting_counter_ = 5 + rand() % 15;
    Audio::get().playSound("tabe.wav");

    constexpr float SPEED = 2.0f;

    switch (direction)
    {
    case TabeDirection::TO_THE_LEFT:
    {
        speed_ = -1.0f * SPEED;
        accel_ = -1.0f * (1 + rand() % 10) / 30.0f;
        sprite_->setFlip(SDL_FLIP_NONE);
        break;
    }

    case TabeDirection::TO_THE_RIGHT:
    {
        speed_ = SPEED;
        accel_ = (1 + rand() % 10) / 30.0f;
        sprite_->setFlip(SDL_FLIP_HORIZONTAL);
        break;
    }

    default:
        break;
    }
}

// Establece el estado
void Tabe::setState(TabeState state)
{
    if (enabled_)
    {
        state_ = state;

        switch (state)
        {
        case TabeState::FLY:
            sprite_->setCurrentAnimation("fly");
            break;

        case TabeState::HIT:
            sprite_->setCurrentAnimation("hit");
            hit_counter_ = 5;
            ++number_of_hits_;
            break;

        default:
            break;
        }
    }
}

// Actualiza el estado
void Tabe::updateState()
{
    if (state_ == TabeState::HIT)
    {
        --hit_counter_;
        if (hit_counter_ == 0)
        {
            setState(TabeState::FLY);
        }
    }
}

// Intenta obtener el bonus
bool Tabe::tryToGetBonus()
{
    if (has_bonus_ && rand() % std::max(1, 15 - number_of_hits_) == 0)
    {
        has_bonus_ = false;
        return true;
    }
    return false;
}

// Actualiza el temporizador
void Tabe::updateTimer()
{
    timer_.current_time = SDL_GetTicks();
    timer_.delta_time = timer_.current_time - timer_.last_time;
    timer_.last_time = timer_.current_time;
}

// Deshabilita el objeto
void Tabe::disable()
{
    enabled_ = false;
    timer_.reset();
}