#include "adapters/hooks/items/ItemHooks.h"
#include "adapters/hooks/Il2CppMethodHook.h"
#include "application/lifecycle/CallbackBarrier.h"

#include "adapters/hooks/items/ItemHookBindings.h"

#include "adapters/il2cpp/Il2CppObjectInstance.h"
#include "adapters/il2cpp/Il2CppResolver.h"
#include "adapters/il2cpp/Il2CppObjectAccess.h"
#include "adapters/il2cpp/Il2CppString.h"
#include "foundation/diagnostics/Logger.h"
#include "adapters/hooks/items/ItemHookState.h"
#include "adapters/hooks/items/ItemHookDiagnostics.h"
#include "application/lifecycle/RuntimeState.h"

namespace ta::adapters::hooks::items
{
    bool no_limit_enabled()
    {
        return ta::adapters::hooks::items::no_slot_limits_enabled();
    }

    bool big_grid_enabled()
    {
        return ta::adapters::hooks::items::increased_transfer_size_enabled();
    }

    bool install()
    {
        ta::adapters::hooks::items::set_ensure_factory_hook(&install_factory_hook);
        const Il2CppImage* assembly = ta::adapters::il2cpp::resolve_image("Assembly-CSharp.dll");
        if (!assembly)
        {
            ta::foundation::logger::warn("Item hooks install failed: Assembly-CSharp.dll was not found");
            return false;
        }

        bool ok = install_factory_hook();

        const Il2CppClass* item_controller_class = ta::adapters::il2cpp::resolve_class(
            assembly, "EFT.InventoryLogic", "ItemController");
        const Il2CppClass* inventory_controller_class = ta::adapters::il2cpp::resolve_class(
            assembly, "EFT.InventoryLogic", "InventoryController");
        const Il2CppClass* backend_inventory_controller_class = ta::adapters::il2cpp::resolve_class(
            assembly, "EFT.InventoryLogic", "BackEndInventoryController");

        ok = ta::adapters::il2cpp::hook_method(item_controller_class, "IsLimitedAtAddress", 3, hk_limited_at_address_item,
                              &o_limited_at_address_item) && ok;
        ok = ta::adapters::il2cpp::hook_method(inventory_controller_class, "IsLimitedAtAddress", 3,
                              hk_limited_at_address_inventory, &o_limited_at_address_inventory) && ok;
        ok = ta::adapters::il2cpp::hook_method(backend_inventory_controller_class, "IsLimitedAtAddress", 3,
                              hk_limited_at_address_backend, &o_limited_at_address_backend) && ok;

        const Il2CppClass* stack_slot_class = ta::adapters::il2cpp::resolve_class(assembly, "EFT.InventoryLogic", "StackSlot");
        ok = ta::adapters::il2cpp::hook_method(stack_slot_class, "CheckCompatibility", 1, hk_check_compat_stack_slot,
                              &o_check_compat_stack_slot) && ok;

        ok = ta::adapters::il2cpp::hook_method(item_controller_class, "CheckCompatibility", 1,
                              hk_check_compat_item_controller, &o_check_compat_item_controller) && ok;
        ok = ta::adapters::il2cpp::hook_method(item_controller_class, "CheckRestrictions", 2, hk_check_restrictions,
                              &o_check_restrictions) && ok;
        ok = ta::adapters::il2cpp::hook_method(item_controller_class, "CheckForOnlySpecialSlotsItem", 2,
                              hk_check_special_slot, &o_check_special_slot) && ok;
        ok = ta::adapters::il2cpp::hook_method(item_controller_class, "CheckSlotRestrictions", 2,
                              hk_check_slot_restrictions, &o_check_slot_restrictions) && ok;

        const Il2CppClass* magazine_class = ta::adapters::il2cpp::resolve_class(assembly, "EFT.InventoryLogic", "Magazine");
        ok = ta::adapters::il2cpp::hook_method(magazine_class, "CheckCompatibility", 1, hk_check_compat_magazine,
                              &o_check_compat_magazine) && ok;
        ok = ta::adapters::il2cpp::hook_method(magazine_class, "IsAmmoCompatible", 1, hk_is_ammo_compatible,
                              &o_is_ammo_compatible) && ok;

        const Il2CppClass* virtual_container_class = ta::adapters::il2cpp::resolve_class(
            assembly, "EFT.InventoryLogic", "VirtualContainer");
        ok = ta::adapters::il2cpp::hook_method(virtual_container_class, "CheckCompatibility", 1,
                              hk_check_compat_virtual_container,
                              &o_check_compat_virtual_container) && ok;

        const Il2CppClass* grid_class = ta::adapters::il2cpp::resolve_class(assembly, "EFT.InventoryLogic", "Grid");
        ok = ta::adapters::il2cpp::hook_method(grid_class, "CheckCompatibility", 1, hk_check_compat_grid,
                              &o_check_compat_grid) && ok;

        const Il2CppClass* slot_class = ta::adapters::il2cpp::resolve_class(assembly, "EFT.InventoryLogic", "Slot");
        ok = ta::adapters::il2cpp::hook_method(slot_class, "CheckCompatibility", 1, hk_check_compat_slot,
                              &o_check_compat_slot) && ok;
        ok = ta::adapters::il2cpp::hook_method(slot_class, "CheckConditions", 3, hk_slot_check_conditions,
                              &o_slot_check_conditions) && ok;

        const Il2CppClass* weapon_class = ta::adapters::il2cpp::resolve_class(assembly, "EFT.InventoryLogic", "Weapon");
        ok = ta::adapters::il2cpp::hook_method(weapon_class, "IsModSuitable", 1, hk_is_mod_suitable,
                              &o_is_mod_suitable) && ok;

        const Il2CppClass* mod_class = ta::adapters::il2cpp::resolve_class(assembly, "EFT.InventoryLogic", "Mod");
        ok = ta::adapters::il2cpp::hook_method(mod_class, "get_RaidModdable", 0, hk_get_mod_raid_moddable,
                              &o_get_mod_raid_moddable) && ok;
        ok = ta::adapters::il2cpp::hook_method(mod_class, "CanBeMoved", 1, hk_mod_can_be_moved,
                              &o_mod_can_be_moved) && ok;

        const Il2CppClass* transfer_items_controller = ta::adapters::il2cpp::resolve_class(
            assembly, "EFT", "TransferItemsController");
        ok = ta::adapters::il2cpp::hook_method(transfer_items_controller, "GetGridSizeForProfileId", 1, hk_get_grid_size,
                              &o_get_grid_size) && ok;

        const Il2CppClass* eft_client_backend_session = ta::adapters::il2cpp::resolve_class(
            assembly, "EFT", "EftClientBackendSession");
        ok = ta::adapters::il2cpp::hook_method(eft_client_backend_session, "LocalRaidEnded", 4, hk_local_raid_ended,
                              &o_local_raid_ended) && ok;

        return ok;
    }

    void reset()
    {
        o_init_level = nullptr;
        o_mod_can_be_moved = nullptr;
        o_get_mod_raid_moddable = nullptr;
        o_slot_check_conditions = nullptr;
        o_is_mod_suitable = nullptr;
        o_is_ammo_compatible = nullptr;
        o_limited_at_address_inventory = nullptr;
        o_limited_at_address_item = nullptr;
        o_limited_at_address_backend = nullptr;
        o_check_compat_stack_slot = nullptr;
        o_check_compat_item_controller = nullptr;
        o_check_compat_magazine = nullptr;
        o_check_compat_virtual_container = nullptr;
        o_check_compat_slot = nullptr;
        o_check_compat_grid = nullptr;
        o_check_restrictions = nullptr;
        o_check_special_slot = nullptr;
        o_check_slot_restrictions = nullptr;
        o_get_grid_size = nullptr;
        o_local_raid_ended = nullptr;
        ta::adapters::hooks::items::reset_state();
        ta::adapters::hooks::items::diagnostics::reset();
    }

}
