#include "adapters/eft/player/PlayerVitalsService.h"

#include "adapters/eft/bindings/PlayerAccess.h"

namespace ta::adapters::eft::player::runtime
{
    void PlayerVitalsService::apply(ta::adapters::il2cpp::Il2CppObjectInstance& main_player,
                                    const PlayerSettings& settings)
    {
        if (settings.infinite_stamina)
            refill_stamina(main_player);

        Il2CppObject* health_controller_ptr = PlayerAccess::get_active_health_controller(main_player);

        apply_god_mode(health_controller_ptr, settings.god_mode);

        if (settings.no_hunger_thirst)
            refill_hunger_and_thirst(health_controller_ptr);
    }

    void PlayerVitalsService::refill_stamina(ta::adapters::il2cpp::Il2CppObjectInstance& main_player)
    {
        Il2CppObject* physical_ptr = PlayerAccess::get_physical(main_player);
        if (!physical_ptr) return;

        ta::adapters::il2cpp::Il2CppObjectInstance physical(physical_ptr);
        set_resource_current(physical, "Stamina", 100.0f);
        set_resource_current(physical, "HandsStamina", 100.0f);
        set_resource_current(physical, "Oxygen", 100.0f);
    }

    void PlayerVitalsService::set_resource_current(ta::adapters::il2cpp::Il2CppObjectInstance& owner,
                                                   const char* field_name,
                                                   const float value)
    {
        Il2CppObject* resource_ptr = nullptr;
        if (!owner.try_get_field(field_name, resource_ptr) || !resource_ptr)
            return;

        ta::adapters::il2cpp::Il2CppObjectInstance resource(resource_ptr);
        resource.set_field("Current", value);
    }

    void PlayerVitalsService::apply_god_mode(Il2CppObject* health_controller_ptr, const bool enabled)
    {
        if (!health_controller_ptr) return;

        ta::adapters::il2cpp::Il2CppObjectInstance health_controller(health_controller_ptr);

        if (enabled)
        {
            if (!god_mode_state_.fall_height_saved)
            {
                float fall_safe_height = 0.0f;
                if (!health_controller.try_get_field("_fallSafeHeight", fall_safe_height))
                    return;
                god_mode_state_.saved_fall_height = fall_safe_height;
                god_mode_state_.fall_height_saved = true;
            }

            const auto set_damage_coeff =
                health_controller.get_method<void(*)(Il2CppObject*, float)>("SetDamageCoeff", 1);
            const auto remove_negative_effects =
                health_controller.get_method<void(*)(Il2CppObject*, int)>("RemoveNegativeEffects", 1);
            const auto restore_full_health =
                health_controller.get_method<void(*)(Il2CppObject*)>("RestoreFullHealth", 0);

            if (set_damage_coeff)
                set_damage_coeff(health_controller.get_instance(), -1.0f);
            if (remove_negative_effects)
                remove_negative_effects(health_controller.get_instance(), 7); // 7 = Common
            if (restore_full_health)
                restore_full_health(health_controller.get_instance());

            health_controller.set_field("_fallSafeHeight", 99999999.0f);
            god_mode_state_.was_enabled = true;
        }
        else if (god_mode_state_.was_enabled)
        {
            const auto set_damage_coeff =
                health_controller.get_method<void(*)(Il2CppObject*, float)>("SetDamageCoeff", 1);
            if (set_damage_coeff)
                set_damage_coeff(health_controller.get_instance(), 1.0f);

            if (god_mode_state_.fall_height_saved)
                health_controller.set_field("_fallSafeHeight", god_mode_state_.saved_fall_height);

            god_mode_state_.was_enabled = false;
        }
    }

    void PlayerVitalsService::refill_hunger_and_thirst(Il2CppObject* health_controller_ptr)
    {
        if (!health_controller_ptr) return;

        ta::adapters::il2cpp::Il2CppObjectInstance health_controller(health_controller_ptr);
        const auto change_energy =
            health_controller.get_method<void(*)(Il2CppObject*, float)>("ChangeEnergy", 1);
        const auto change_hydration =
            health_controller.get_method<void(*)(Il2CppObject*, float)>("ChangeHydration", 1);

        if (change_energy)
            change_energy(health_controller.get_instance(), 100.0f);
        if (change_hydration)
            change_hydration(health_controller.get_instance(), 100.0f);
    }
}
