package net.minecraft.world.entity.item;

import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.Packet;
import javax.annotation.Nullable;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.stats.Stat;
import net.minecraft.stats.Stats;
import net.minecraft.world.entity.player.Player;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.Items;
import net.minecraft.world.damagesource.DamageSource;
import java.util.Objects;
import java.util.Iterator;
import java.util.List;
import net.minecraft.world.phys.Vec3;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.MoverType;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import java.util.UUID;
import net.minecraft.world.item.ItemStack;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.Entity;

public class ItemEntity extends Entity {
    private static final EntityDataAccessor<ItemStack> DATA_ITEM;
    private int age;
    private int pickupDelay;
    private int health;
    private UUID thrower;
    private UUID owner;
    public final float bobOffs;
    
    public ItemEntity(final EntityType<? extends ItemEntity> aku, final Level bjw) {
        super(aku, bjw);
        this.health = 5;
        this.bobOffs = (float)(Math.random() * 3.141592653589793 * 2.0);
    }
    
    public ItemEntity(final Level bjw, final double double2, final double double3, final double double4) {
        this(EntityType.ITEM, bjw);
        this.setPos(double2, double3, double4);
        this.yRot = this.random.nextFloat() * 360.0f;
        this.setDeltaMovement(this.random.nextDouble() * 0.2 - 0.1, 0.2, this.random.nextDouble() * 0.2 - 0.1);
    }
    
    public ItemEntity(final Level bjw, final double double2, final double double3, final double double4, final ItemStack ben) {
        this(bjw, double2, double3, double4);
        this.setItem(ben);
    }
    
    @Override
    protected boolean isMovementNoisy() {
        return false;
    }
    
    @Override
    protected void defineSynchedData() {
        this.getEntityData().<ItemStack>define(ItemEntity.DATA_ITEM, ItemStack.EMPTY);
    }
    
    @Override
    public void tick() {
        if (this.getItem().isEmpty()) {
            this.remove();
            return;
        }
        super.tick();
        if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
            --this.pickupDelay;
        }
        this.xo = this.getX();
        this.yo = this.getY();
        this.zo = this.getZ();
        final Vec3 cvl2 = this.getDeltaMovement();
        if (this.isUnderLiquid(FluidTags.WATER)) {
            this.setUnderwaterMovement();
        }
        else if (!this.isNoGravity()) {
            this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.04, 0.0));
        }
        if (this.level.isClientSide) {
            this.noPhysics = false;
        }
        else {
            this.noPhysics = !this.level.noCollision(this);
            if (this.noPhysics) {
                this.checkInBlock(this.getX(), (this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0, this.getZ());
            }
        }
        if (!this.onGround || Entity.getHorizontalDistanceSqr(this.getDeltaMovement()) > 9.999999747378752E-6 || (this.tickCount + this.getId()) % 4 == 0) {
            this.move(MoverType.SELF, this.getDeltaMovement());
            float float3 = 0.98f;
            if (this.onGround) {
                float3 = this.level.getBlockState(new BlockPos(this.getX(), this.getY() - 1.0, this.getZ())).getBlock().getFriction() * 0.98f;
            }
            this.setDeltaMovement(this.getDeltaMovement().multiply(float3, 0.98, float3));
            if (this.onGround) {
                this.setDeltaMovement(this.getDeltaMovement().multiply(1.0, -0.5, 1.0));
            }
        }
        final boolean boolean3 = Mth.floor(this.xo) != Mth.floor(this.getX()) || Mth.floor(this.yo) != Mth.floor(this.getY()) || Mth.floor(this.zo) != Mth.floor(this.getZ());
        final int integer4 = boolean3 ? 2 : 40;
        if (this.tickCount % integer4 == 0) {
            if (this.level.getFluidState(new BlockPos(this)).is(FluidTags.LAVA)) {
                this.setDeltaMovement((this.random.nextFloat() - this.random.nextFloat()) * 0.2f, 0.20000000298023224, (this.random.nextFloat() - this.random.nextFloat()) * 0.2f);
                this.playSound(SoundEvents.GENERIC_BURN, 0.4f, 2.0f + this.random.nextFloat() * 0.4f);
            }
            if (!this.level.isClientSide && this.isMergable()) {
                this.mergeWithNeighbours();
            }
        }
        if (this.age != -32768) {
            ++this.age;
        }
        this.hasImpulse |= this.updateInWaterState();
        if (!this.level.isClientSide) {
            final double double5 = this.getDeltaMovement().subtract(cvl2).lengthSqr();
            if (double5 > 0.01) {
                this.hasImpulse = true;
            }
        }
        if (!this.level.isClientSide && this.age >= 6000) {
            this.remove();
        }
    }
    
    private void setUnderwaterMovement() {
        final Vec3 cvl2 = this.getDeltaMovement();
        this.setDeltaMovement(cvl2.x * 0.9900000095367432, cvl2.y + ((cvl2.y < 0.05999999865889549) ? 5.0E-4f : 0.0f), cvl2.z * 0.9900000095367432);
    }
    
    private void mergeWithNeighbours() {
        if (!this.isMergable()) {
            return;
        }
        final List<ItemEntity> list2 = this.level.<ItemEntity>getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.5, 0.0, 0.5), awb -> awb != this && awb.isMergable());
        for (final ItemEntity awb2 : list2) {
            if (awb2.isMergable()) {
                this.tryToMerge(awb2);
                if (this.removed) {
                    break;
                }
                continue;
            }
        }
    }
    
    private boolean isMergable() {
        final ItemStack ben2 = this.getItem();
        return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < 6000 && ben2.getCount() < ben2.getMaxStackSize();
    }
    
    private void tryToMerge(final ItemEntity awb) {
        final ItemStack ben3 = this.getItem();
        final ItemStack ben4 = awb.getItem();
        if (!Objects.equals(this.getOwner(), awb.getOwner()) || !areMergable(ben3, ben4)) {
            return;
        }
        if (ben4.getCount() < ben3.getCount()) {
            merge(this, ben3, awb, ben4);
        }
        else {
            merge(awb, ben4, this, ben3);
        }
    }
    
    public static boolean areMergable(final ItemStack ben1, final ItemStack ben2) {
        return ben2.getItem() == ben1.getItem() && ben2.getCount() + ben1.getCount() <= ben2.getMaxStackSize() && !(ben2.hasTag() ^ ben1.hasTag()) && (!ben2.hasTag() || ben2.getTag().equals(ben1.getTag()));
    }
    
    public static ItemStack merge(final ItemStack ben1, final ItemStack ben2, final int integer) {
        final int integer2 = Math.min(Math.min(ben1.getMaxStackSize(), integer) - ben1.getCount(), ben2.getCount());
        final ItemStack ben3 = ben1.copy();
        ben3.grow(integer2);
        ben2.shrink(integer2);
        return ben3;
    }
    
    private static void merge(final ItemEntity awb, final ItemStack ben2, final ItemStack ben3) {
        final ItemStack ben4 = merge(ben2, ben3, 64);
        awb.setItem(ben4);
    }
    
    private static void merge(final ItemEntity awb1, final ItemStack ben2, final ItemEntity awb3, final ItemStack ben4) {
        merge(awb1, ben2, ben4);
        awb1.pickupDelay = Math.max(awb1.pickupDelay, awb3.pickupDelay);
        awb1.age = Math.min(awb1.age, awb3.age);
        if (ben4.isEmpty()) {
            awb3.remove();
        }
    }
    
    @Override
    protected void burn(final int integer) {
        this.hurt(DamageSource.IN_FIRE, (float)integer);
    }
    
    @Override
    public boolean hurt(final DamageSource ajz, final float float2) {
        if (this.isInvulnerableTo(ajz)) {
            return false;
        }
        if (!this.getItem().isEmpty() && this.getItem().getItem() == Items.NETHER_STAR && ajz.isExplosion()) {
            return false;
        }
        this.markHurt();
        this.health -= (int)float2;
        if (this.health <= 0) {
            this.remove();
        }
        return false;
    }
    
    public void addAdditionalSaveData(final CompoundTag jt) {
        jt.putShort("Health", (short)this.health);
        jt.putShort("Age", (short)this.age);
        jt.putShort("PickupDelay", (short)this.pickupDelay);
        if (this.getThrower() != null) {
            jt.put("Thrower", NbtUtils.createUUIDTag(this.getThrower()));
        }
        if (this.getOwner() != null) {
            jt.put("Owner", NbtUtils.createUUIDTag(this.getOwner()));
        }
        if (!this.getItem().isEmpty()) {
            jt.put("Item", this.getItem().save(new CompoundTag()));
        }
    }
    
    public void readAdditionalSaveData(final CompoundTag jt) {
        this.health = jt.getShort("Health");
        this.age = jt.getShort("Age");
        if (jt.contains("PickupDelay")) {
            this.pickupDelay = jt.getShort("PickupDelay");
        }
        if (jt.contains("Owner", 10)) {
            this.owner = NbtUtils.loadUUIDTag(jt.getCompound("Owner"));
        }
        if (jt.contains("Thrower", 10)) {
            this.thrower = NbtUtils.loadUUIDTag(jt.getCompound("Thrower"));
        }
        final CompoundTag jt2 = jt.getCompound("Item");
        this.setItem(ItemStack.of(jt2));
        if (this.getItem().isEmpty()) {
            this.remove();
        }
    }
    
    @Override
    public void playerTouch(final Player ayj) {
        if (this.level.isClientSide) {
            return;
        }
        final ItemStack ben3 = this.getItem();
        final Item bei4 = ben3.getItem();
        final int integer5 = ben3.getCount();
        if (this.pickupDelay == 0 && (this.owner == null || this.owner.equals(ayj.getUUID())) && ayj.inventory.add(ben3)) {
            ayj.take(this, integer5);
            if (ben3.isEmpty()) {
                this.remove();
                ben3.setCount(integer5);
            }
            ayj.awardStat(Stats.ITEM_PICKED_UP.get(bei4), integer5);
        }
    }
    
    @Override
    public Component getName() {
        final Component lf2 = this.getCustomName();
        if (lf2 != null) {
            return lf2;
        }
        return new TranslatableComponent(this.getItem().getDescriptionId(), new Object[0]);
    }
    
    @Override
    public boolean isAttackable() {
        return false;
    }
    
    @Nullable
    @Override
    public Entity changeDimension(final DimensionType cbi) {
        final Entity akq3 = super.changeDimension(cbi);
        if (!this.level.isClientSide && akq3 instanceof ItemEntity) {
            ((ItemEntity)akq3).mergeWithNeighbours();
        }
        return akq3;
    }
    
    public ItemStack getItem() {
        return this.getEntityData().<ItemStack>get(ItemEntity.DATA_ITEM);
    }
    
    public void setItem(final ItemStack ben) {
        this.getEntityData().<ItemStack>set(ItemEntity.DATA_ITEM, ben);
    }
    
    @Nullable
    public UUID getOwner() {
        return this.owner;
    }
    
    public void setOwner(@Nullable final UUID uUID) {
        this.owner = uUID;
    }
    
    @Nullable
    public UUID getThrower() {
        return this.thrower;
    }
    
    public void setThrower(@Nullable final UUID uUID) {
        this.thrower = uUID;
    }
    
    public int getAge() {
        return this.age;
    }
    
    public void setDefaultPickUpDelay() {
        this.pickupDelay = 10;
    }
    
    public void setNoPickUpDelay() {
        this.pickupDelay = 0;
    }
    
    public void setNeverPickUp() {
        this.pickupDelay = 32767;
    }
    
    public void setPickUpDelay(final int integer) {
        this.pickupDelay = integer;
    }
    
    public boolean hasPickUpDelay() {
        return this.pickupDelay > 0;
    }
    
    public void setExtendedLifetime() {
        this.age = -6000;
    }
    
    public void makeFakeItem() {
        this.setNeverPickUp();
        this.age = 5999;
    }
    
    @Override
    public Packet<?> getAddEntityPacket() {
        return new ClientboundAddEntityPacket(this);
    }
    
    static {
        DATA_ITEM = SynchedEntityData.<ItemStack>defineId(ItemEntity.class, EntityDataSerializers.ITEM_STACK);
    }
}
