#ifndef ENEMY_WORLD_STATE_HPP
#define ENEMY_WORLD_STATE_HPP

#include "unit_world_state.hpp"
#include "goap/goal.hpp"
#include <godot_cpp/classes/area3d.hpp>
#include <godot_cpp/templates/vector.hpp>
#include <godot_cpp/variant/array.hpp>

namespace gd = godot;

class EntityHealth;

class EnemyWorldState : public UnitWorldState {
    GDCLASS(EnemyWorldState, UnitWorldState);
    static void _bind_methods();
public:
    virtual void _ready() override;
    virtual void _process(double) override;
    void on_awareness_entered(gd::Node3D *node);
    void on_awareness_exited(gd::Node3D *node);
    void on_damaged(EntityHealth *, int, Unit *source);
    /*! Select the target with the highest priority and return it.
     * Assigns the priority of found target to out_priority.
     * \param out_priority Assigned to highest found priority, cannot be nullptr.
    */
    Unit *select_target_from_known_with_priority(float *out_priority);
    //! Shorthand for select_target_from_known_with_priority(nullptr)
    Unit *select_target_from_known();
private:
    void queue_select_and_set_target();
    void add_aware_unit(Unit *unit);
    void remove_aware_unit(Unit *unit);
    void select_and_set_target();
    void on_aware_unit_death(Unit *, Unit *dead_entity);
    float calculate_priority(Unit *target);
    gd::Ref<goap::Goal> get_goal_for_target(Unit *unit);
    void try_set_target(Unit *target);
    void set_editor_available_goals(gd::Array array);
    gd::Array get_editor_available_goals() const;
private:
    gd::Callable aware_unit_death{callable_mp(this, &EnemyWorldState::on_aware_unit_death)};
    gd::Vector<gd::Ref<goap::Goal>> available_goals{};
    gd::Vector<Unit *> known_enemies{};
    bool select_and_set_target_on_process{false};

    gd::Ref<goap::Goal> last_goal{};

    gd::Area3D *awareness_area{nullptr};
    EntityHealth *health{nullptr};
};

#endif // !ENEMY_WORLD_STATE_HPP
