/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.bausystem.utils;

import de.steamwar.Reflection;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.ItemContainerContents;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.entity.player.Abilities;
import net.minecraft.world.level.GameType;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import java.util.List;
import java.util.Optional;

public class NMSWrapper {
    public static final NMSWrapper impl = new NMSWrapper();

    private static final Reflection.Field<GameType> playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0);

    public void setInternalGameMode(Player player, GameMode gameMode) {
        playerGameMode.set(((CraftPlayer) player).getHandle().gameMode, GameType.byId(gameMode.getValue()));
    }

    public void setSlotToItemStack(Player player, Object o) {
        ClientboundContainerSetSlotPacket packetPlayInSetCreativeSlot = (ClientboundContainerSetSlotPacket) o;
        int index = packetPlayInSetCreativeSlot.getSlot();
        if (index >= 36 && index <= 44) {
            index -= 36;
        } else if (index > 44) {
            index -= 5;
        } else if (index <= 8) {
            index = index - 8 + 36;
        }
        player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.getItem()));
        if (index < 9) player.getInventory().setHeldItemSlot(index);
        player.updateInventory();
    }

    public void setPlayerBuildAbilities(Player player) {
        Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities();
        abilities.mayBuild = true;
        abilities.mayfly = true;
    }

    public Material pathMaterial() {
        return Material.DIRT_PATH;
    }

    private static final int threshold = 2048;

    public boolean checkItemStack(ItemStack item) {
        ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
        if (data == null) {
            return false;
        }

        return drillDown(data.contents(), 0, 0) > threshold;
    }

    private int drillDown(List<ItemStack> items, int layer, int start) {
        if (layer > 2) return start + threshold;
        int invalid = start;
        for (int i = start; i < items.size(); i++) {
            ItemStack item = items.get(i);
            if (item.isEmpty()) continue;

            invalid += item.getAmount();

            ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
            if (data == null) {
                continue;
            }

            List<ItemStack> subItems = data.contents();
            if (subItems.size() > 1) {
                invalid = drillDown(subItems, layer + 1, invalid);
            }
        }
        return invalid;
    }

    public Object resetExplosionKnockback(Object packet) {
        ClientboundExplodePacket explosion = (ClientboundExplodePacket) packet;

        return new ClientboundExplodePacket(
                explosion.center(),
                Optional.empty(),
                explosion.explosionParticle(),
                explosion.explosionSound()
        );
    }
}
