package org.allaymc.server.item.component.weapon;

import lombok.extern.slf4j.Slf4j;
import org.allaymc.api.container.Container;
import org.allaymc.api.container.ContainerTypes;
import org.allaymc.api.entity.EntityInitInfo;
import org.allaymc.api.entity.interfaces.EntityPlayer;
import org.allaymc.api.entity.type.EntityTypes;
import org.allaymc.api.eventbus.event.entity.EntityShootBowEvent;
import org.allaymc.api.item.ItemStackInitInfo;
import org.allaymc.api.item.data.PotionType;
import org.allaymc.api.item.enchantment.EnchantmentTypes;
import org.allaymc.api.item.interfaces.ItemArrowStack;
import org.allaymc.api.math.MathUtils;
import org.allaymc.api.player.GameMode;
import org.allaymc.api.world.sound.SimpleSound;
import org.allaymc.server.container.impl.OffhandContainerImpl;
import org.allaymc.server.item.component.ItemBaseComponentImpl;
import org.joml.Vector3d;

/**
 * @author harryxi | daoge_cmd
 */
@Slf4j
public class ItemBowBaseComponentImpl extends ItemBaseComponentImpl {

    public ItemBowBaseComponentImpl(ItemStackInitInfo initInfo) {
        super(initInfo);
    }

    @Override
    public boolean canUseItemInAir(EntityPlayer player) {
        return player.getGameMode() == GameMode.CREATIVE || hasArrow(player);
    }

    @Override
    public boolean useItemInAir(EntityPlayer player, long usedTime) {
        if (usedTime < 3) {
            return false;
        }

        var creative = player.getGameMode() == GameMode.CREATIVE;
        var force = Math.min(usedTime * (usedTime + 40.0) / 1200.0, 1.0);
        var speed = force * 5;

        var infinity = getEnchantmentLevel(EnchantmentTypes.INFINITY) != 0;
        PotionType potionType = null;
        if (!creative) {
            var arrow = findArrow(player, infinity);
            if (arrow == null) {
                return false;
            }

            potionType = arrow.getPotionType();
        }

        var powerLevel = getEnchantmentLevel(EnchantmentTypes.POWER);
        var punchLevel = getEnchantmentLevel(EnchantmentTypes.PUNCH);
        var flameLevel = getEnchantmentLevel(EnchantmentTypes.FLAME);

        var dimension = player.getDimension();
        var location = player.getLocation();
        var shootPos = new Vector3d(location.x(), location.y() + player.getEyeHeight() - 0.1, location.z());
        var playerMotion = new Vector3d(player.getMotion());
        if (player.isOnGround()) {
            playerMotion.setComponent(1, 0);
        }
        var arrow = EntityTypes.ARROW.createEntity(
                EntityInitInfo.builder()
                        .dimension(dimension)
                        .pos(shootPos)
                        .rot(-location.yaw(), -location.pitch())
                        .motion(MathUtils.getDirectionVector(location).mul(speed).add(playerMotion))
                        .build()
        );
        arrow.setShooter(player);
        arrow.setPotionType(potionType);
        if (creative || (infinity && potionType == null)) {
            arrow.setInfinite(true);
        }
        arrow.setPowerLevel(powerLevel);
        arrow.setPunchLevel(punchLevel);
        if (flameLevel != 0) {
            arrow.setOnFireTicks(20 * 5);
        }
        arrow.setCritical(force >= 1.0);

        var event = new EntityShootBowEvent(player, thisItemStack, arrow);
        if (!event.call()) {
            return false;
        }

        dimension.getEntityManager().addEntity(arrow);
        if (!creative) {
            tryIncreaseDamage(1);
        }
        player.getDimension().addSound(shootPos, SimpleSound.BOW_SHOOT);

        return true;
    }

    protected boolean hasArrow(EntityPlayer player) {
        // Find offhand arrow first
        Container container = player.getContainer(ContainerTypes.OFFHAND);
        if (container.getItemStack(OffhandContainerImpl.OFFHAND_SLOT) instanceof ItemArrowStack) {
            return true;
        }

        // Arrow is not in offhand, search in inventory again
        container = player.getContainer(ContainerTypes.INVENTORY);
        for (var itemStack : container.getItemStacks()) {
            if (itemStack instanceof ItemArrowStack) {
                return true;
            }
        }

        return false;
    }

    protected ItemArrowStack findArrow(EntityPlayer player, boolean infinity) {
        Container container;
        int slot;
        ItemArrowStack arrow = null;

        // Find offhand arrow first
        container = player.getContainer(ContainerTypes.OFFHAND);
        slot = OffhandContainerImpl.OFFHAND_SLOT;
        if (container.getItemStack(slot) instanceof ItemArrowStack a) {
            arrow = a;
        }

        if (arrow == null) {
            // Arrow is not in offhand, search in inventory again
            container = player.getContainer(ContainerTypes.INVENTORY);
            var itemStacks = container.getItemStacks();
            for (slot = 0; slot < itemStacks.size(); slot++) {
                var item = itemStacks.get(slot);
                if (item instanceof ItemArrowStack a) {
                    arrow = a;
                    break;
                }
            }
        }

        if (arrow == null) {
            // No arrow found
            return null;
        }

        // Bows with infinity enchantment do not consume normal types of arrows when shooting
        if (!infinity || arrow.getPotionType() != null) {
            if (arrow.getCount() == 1) {
                container.clearSlot(slot);
            } else {
                arrow.reduceCount(1);
                container.notifySlotChange(slot);
            }
        }

        return arrow;
    }

}
