package net.minecraft.client.renderer.item;

import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceKey;
import java.util.Optional;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.item.CompassItem;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.CrossbowItem;
import net.minecraft.world.item.ElytraItem;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.FishingRodItem;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
import com.google.common.collect.Maps;
import net.minecraft.world.item.Item;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;

public class ItemProperties {
    private static final Map<ResourceLocation, ItemPropertyFunction> GENERIC_PROPERTIES;
    private static final ResourceLocation DAMAGED;
    private static final ResourceLocation DAMAGE;
    private static final ItemPropertyFunction PROPERTY_DAMAGED;
    private static final ItemPropertyFunction PROPERTY_DAMAGE;
    private static final Map<Item, Map<ResourceLocation, ItemPropertyFunction>> PROPERTIES;
    
    private static ItemPropertyFunction registerGeneric(final ResourceLocation uh, final ItemPropertyFunction elg) {
        ItemProperties.GENERIC_PROPERTIES.put(uh, elg);
        return elg;
    }
    
    private static void register(final Item bke, final ResourceLocation uh, final ItemPropertyFunction elg) {
        ItemProperties.PROPERTIES.computeIfAbsent(bke, bke -> Maps.newHashMap()).put(uh, elg);
    }
    
    @Nullable
    public static ItemPropertyFunction getProperty(final Item bke, final ResourceLocation uh) {
        if (bke.getMaxDamage() > 0) {
            if (ItemProperties.DAMAGE.equals(uh)) {
                return ItemProperties.PROPERTY_DAMAGE;
            }
            if (ItemProperties.DAMAGED.equals(uh)) {
                return ItemProperties.PROPERTY_DAMAGED;
            }
        }
        final ItemPropertyFunction elg3 = ItemProperties.GENERIC_PROPERTIES.get(uh);
        if (elg3 != null) {
            return elg3;
        }
        final Map<ResourceLocation, ItemPropertyFunction> map4 = ItemProperties.PROPERTIES.get(bke);
        if (map4 == null) {
            return null;
        }
        return map4.get(uh);
    }
    
    static {
        GENERIC_PROPERTIES = Maps.newHashMap();
        DAMAGED = new ResourceLocation("damaged");
        DAMAGE = new ResourceLocation("damage");
        PROPERTY_DAMAGED = ((bki, dym, aoy) -> bki.isDamaged() ? 1.0f : 0.0f);
        PROPERTY_DAMAGE = ((bki, dym, aoy) -> Mth.clamp(bki.getDamageValue() / (float)bki.getMaxDamage(), 0.0f, 1.0f));
        PROPERTIES = Maps.newHashMap();
        registerGeneric(new ResourceLocation("lefthanded"), (bki, dym, aoy) -> (aoy == null || aoy.getMainArm() == HumanoidArm.RIGHT) ? 0.0f : 1.0f);
        registerGeneric(new ResourceLocation("cooldown"), (bki, dym, aoy) -> (aoy instanceof Player) ? aoy.getCooldowns().getCooldownPercent(bki.getItem(), 0.0f) : 0.0f);
        registerGeneric(new ResourceLocation("custom_model_data"), (bki, dym, aoy) -> bki.hasTag() ? ((float)bki.getTag().getInt("CustomModelData")) : 0.0f);
        register(Items.BOW, new ResourceLocation("pull"), (bki, dym, aoy) -> {
            if (aoy == null) {
                return 0.0f;
            }
            else if (aoy.getUseItem() != bki) {
                return 0.0f;
            }
            else {
                return (bki.getUseDuration() - aoy.getUseItemRemainingTicks()) / 20.0f;
            }
        });
        register(Items.BOW, new ResourceLocation("pulling"), (bki, dym, aoy) -> (aoy != null && aoy.isUsingItem() && aoy.getUseItem() == bki) ? 1.0f : 0.0f);
        register(Items.CLOCK, new ResourceLocation("time"), new ItemPropertyFunction() {
            private double rotation;
            private double rota;
            private long lastUpdateTick;
            
            @Override
            public float call(final ItemStack bki, @Nullable ClientLevel dym, @Nullable final LivingEntity aoy) {
                final Entity aom5 = (aoy != null) ? aoy : bki.getEntityRepresentation();
                if (aom5 == null) {
                    return 0.0f;
                }
                if (dym == null && aom5.level instanceof ClientLevel) {
                    dym = (ClientLevel)aom5.level;
                }
                if (dym == null) {
                    return 0.0f;
                }
                double double6;
                if (dym.dimensionType().natural()) {
                    double6 = dym.getTimeOfDay(1.0f);
                }
                else {
                    double6 = Math.random();
                }
                double6 = this.wobble(dym, double6);
                return (float)double6;
            }
            
            private double wobble(final Level bqb, final double double2) {
                if (bqb.getGameTime() != this.lastUpdateTick) {
                    this.lastUpdateTick = bqb.getGameTime();
                    double double3 = double2 - this.rotation;
                    double3 = Mth.positiveModulo(double3 + 0.5, 1.0) - 0.5;
                    this.rota += double3 * 0.1;
                    this.rota *= 0.9;
                    this.rotation = Mth.positiveModulo(this.rotation + this.rota, 1.0);
                }
                return this.rotation;
            }
        });
        register(Items.COMPASS, new ResourceLocation("angle"), new ItemPropertyFunction() {
            private final CompassWobble wobble = new CompassWobble();
            private final CompassWobble wobbleRandom = new CompassWobble();
            
            @Override
            public float call(final ItemStack bki, @Nullable ClientLevel dym, @Nullable final LivingEntity aoy) {
                final Entity aom5 = (aoy != null) ? aoy : bki.getEntityRepresentation();
                if (aom5 == null) {
                    return 0.0f;
                }
                if (dym == null && aom5.level instanceof ClientLevel) {
                    dym = (ClientLevel)aom5.level;
                }
                final BlockPos fu6 = CompassItem.isLodestoneCompass(bki) ? this.getLodestonePosition(dym, bki.getOrCreateTag()) : this.getSpawnPosition(dym);
                final long long7 = dym.getGameTime();
                if (fu6 == null || aom5.position().distanceToSqr(fu6.getX() + 0.5, aom5.position().y(), fu6.getZ() + 0.5) < 9.999999747378752E-6) {
                    if (this.wobbleRandom.shouldUpdate(long7)) {
                        this.wobbleRandom.update(long7, Math.random());
                    }
                    final double double9 = this.wobbleRandom.rotation + bki.hashCode() / 2.14748365E9f;
                    return Mth.positiveModulo((float)double9, 1.0f);
                }
                final boolean boolean9 = aoy instanceof Player && ((Player)aoy).isLocalPlayer();
                double double10 = 0.0;
                if (boolean9) {
                    double10 = aoy.yRot;
                }
                else if (aom5 instanceof ItemFrame) {
                    double10 = this.getFrameRotation((ItemFrame)aom5);
                }
                else if (aom5 instanceof ItemEntity) {
                    double10 = 180.0f - ((ItemEntity)aom5).getSpin(0.5f) / 6.2831855f * 360.0f;
                }
                else if (aoy != null) {
                    double10 = aoy.yBodyRot;
                }
                double10 = Mth.positiveModulo(double10 / 360.0, 1.0);
                final double double11 = this.getAngleTo(Vec3.atCenterOf(fu6), aom5) / 6.2831854820251465;
                double double12;
                if (boolean9) {
                    if (this.wobble.shouldUpdate(long7)) {
                        this.wobble.update(long7, 0.5 - (double10 - 0.25));
                    }
                    double12 = double11 + this.wobble.rotation;
                }
                else {
                    double12 = 0.5 - (double10 - 0.25 - double11);
                }
                return Mth.positiveModulo((float)double12, 1.0f);
            }
            
            @Nullable
            private BlockPos getSpawnPosition(final ClientLevel dym) {
                return dym.dimensionType().natural() ? dym.getSharedSpawnPos() : null;
            }
            
            @Nullable
            private BlockPos getLodestonePosition(final Level bqb, final CompoundTag le) {
                final boolean boolean4 = le.contains("LodestonePos");
                final boolean boolean5 = le.contains("LodestoneDimension");
                if (boolean4 && boolean5) {
                    final Optional<ResourceKey<Level>> optional6 = CompassItem.getLodestoneDimension(le);
                    if (optional6.isPresent() && bqb.dimension() == optional6.get()) {
                        return NbtUtils.readBlockPos(le.getCompound("LodestonePos"));
                    }
                }
                return null;
            }
            
            private double getFrameRotation(final ItemFrame bba) {
                final Direction fz3 = bba.getDirection();
                final int integer4 = fz3.getAxis().isVertical() ? (90 * fz3.getAxisDirection().getStep()) : 0;
                return Mth.wrapDegrees(180 + fz3.get2DDataValue() * 90 + bba.getRotation() * 45 + integer4);
            }
            
            private double getAngleTo(final Vec3 dem, final Entity aom) {
                return Math.atan2(dem.z() - aom.getZ(), dem.x() - aom.getX());
            }
        });
        register(Items.CROSSBOW, new ResourceLocation("pull"), (bki, dym, aoy) -> {
            if (aoy == null) {
                return 0.0f;
            }
            else if (CrossbowItem.isCharged(bki)) {
                return 0.0f;
            }
            else {
                return (bki.getUseDuration() - aoy.getUseItemRemainingTicks()) / (float)CrossbowItem.getChargeDuration(bki);
            }
        });
        register(Items.CROSSBOW, new ResourceLocation("pulling"), (bki, dym, aoy) -> (aoy != null && aoy.isUsingItem() && aoy.getUseItem() == bki && !CrossbowItem.isCharged(bki)) ? 1.0f : 0.0f);
        register(Items.CROSSBOW, new ResourceLocation("charged"), (bki, dym, aoy) -> (aoy != null && CrossbowItem.isCharged(bki)) ? 1.0f : 0.0f);
        register(Items.CROSSBOW, new ResourceLocation("firework"), (bki, dym, aoy) -> (aoy != null && CrossbowItem.isCharged(bki) && CrossbowItem.containsChargedProjectile(bki, Items.FIREWORK_ROCKET)) ? 1.0f : 0.0f);
        register(Items.ELYTRA, new ResourceLocation("broken"), (bki, dym, aoy) -> ElytraItem.isFlyEnabled(bki) ? 0.0f : 1.0f);
        boolean boolean4;
        boolean boolean5;
        register(Items.FISHING_ROD, new ResourceLocation("cast"), (bki, dym, aoy) -> {
            if (aoy == null) {
                return 0.0f;
            }
            else {
                boolean4 = (aoy.getMainHandItem() == bki);
                boolean5 = (aoy.getOffhandItem() == bki);
                if (aoy.getMainHandItem().getItem() instanceof FishingRodItem) {
                    boolean5 = false;
                }
                return ((boolean4 || boolean5) && aoy instanceof Player && aoy.fishing != null) ? 1.0f : 0.0f;
            }
        });
        register(Items.SHIELD, new ResourceLocation("blocking"), (bki, dym, aoy) -> (aoy != null && aoy.isUsingItem() && aoy.getUseItem() == bki) ? 1.0f : 0.0f);
        register(Items.TRIDENT, new ResourceLocation("throwing"), (bki, dym, aoy) -> (aoy != null && aoy.isUsingItem() && aoy.getUseItem() == bki) ? 1.0f : 0.0f);
    }
    
    static class CompassWobble {
        private double rotation;
        private double deltaRotation;
        private long lastUpdateTick;
        
        private CompassWobble() {
        }
        
        private boolean shouldUpdate(final long long1) {
            return this.lastUpdateTick != long1;
        }
        
        private void update(final long long1, final double double2) {
            this.lastUpdateTick = long1;
            double double3 = double2 - this.rotation;
            double3 = Mth.positiveModulo(double3 + 0.5, 1.0) - 0.5;
            this.deltaRotation += double3 * 0.1;
            this.deltaRotation *= 0.8;
            this.rotation = Mth.positiveModulo(this.rotation + this.deltaRotation, 1.0);
        }
    }
}
