package net.minecraft.world.entity.ai.attributes;

import java.util.AbstractList;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.ListTag;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import java.util.Collections;
import java.util.Iterator;
import javax.annotation.Nullable;
import java.util.Collection;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import com.google.common.collect.Maps;
import java.util.function.Consumer;
import java.util.UUID;
import java.util.Set;
import java.util.Map;

public class AttributeInstance {
    private final Attribute attribute;
    private final Map<AttributeModifier.Operation, Set<AttributeModifier>> modifiersByOperation;
    private final Map<UUID, AttributeModifier> modifierById;
    private final Set<AttributeModifier> permanentModifiers;
    private double baseValue;
    private boolean dirty;
    private double cachedValue;
    private final Consumer<AttributeInstance> onDirty;
    
    public AttributeInstance(final Attribute aps, final Consumer<AttributeInstance> consumer) {
        this.modifiersByOperation = Maps.newEnumMap(AttributeModifier.Operation.class);
        this.modifierById = (Map<UUID, AttributeModifier>)new Object2ObjectArrayMap();
        this.permanentModifiers = (Set<AttributeModifier>)new ObjectArraySet();
        this.dirty = true;
        this.attribute = aps;
        this.onDirty = consumer;
        this.baseValue = aps.getDefaultValue();
    }
    
    public Attribute getAttribute() {
        return this.attribute;
    }
    
    public double getBaseValue() {
        return this.baseValue;
    }
    
    public void setBaseValue(final double double1) {
        if (double1 == this.baseValue) {
            return;
        }
        this.baseValue = double1;
        this.setDirty();
    }
    
    public Set<AttributeModifier> getModifiers(final AttributeModifier.Operation a) {
        return this.modifiersByOperation.computeIfAbsent(a, a -> Sets.newHashSet());
    }
    
    public Set<AttributeModifier> getModifiers() {
        return ImmutableSet.copyOf(this.modifierById.values());
    }
    
    @Nullable
    public AttributeModifier getModifier(final UUID uUID) {
        return this.modifierById.get(uUID);
    }
    
    public boolean hasModifier(final AttributeModifier apv) {
        return this.modifierById.get(apv.getId()) != null;
    }
    
    private void addModifier(final AttributeModifier apv) {
        final AttributeModifier apv2 = this.modifierById.putIfAbsent(apv.getId(), apv);
        if (apv2 != null) {
            throw new IllegalArgumentException("Modifier is already applied on this attribute!");
        }
        this.getModifiers(apv.getOperation()).add(apv);
        this.setDirty();
    }
    
    public void addTransientModifier(final AttributeModifier apv) {
        this.addModifier(apv);
    }
    
    public void addPermanentModifier(final AttributeModifier apv) {
        this.addModifier(apv);
        this.permanentModifiers.add(apv);
    }
    
    protected void setDirty() {
        this.dirty = true;
        this.onDirty.accept(this);
    }
    
    public void removeModifier(final AttributeModifier apv) {
        this.getModifiers(apv.getOperation()).remove(apv);
        this.modifierById.remove(apv.getId());
        this.permanentModifiers.remove(apv);
        this.setDirty();
    }
    
    public void removeModifier(final UUID uUID) {
        final AttributeModifier apv3 = this.getModifier(uUID);
        if (apv3 != null) {
            this.removeModifier(apv3);
        }
    }
    
    public boolean removePermanentModifier(final UUID uUID) {
        final AttributeModifier apv3 = this.getModifier(uUID);
        if (apv3 != null && this.permanentModifiers.contains(apv3)) {
            this.removeModifier(apv3);
            return true;
        }
        return false;
    }
    
    public void removeModifiers() {
        for (final AttributeModifier apv3 : this.getModifiers()) {
            this.removeModifier(apv3);
        }
    }
    
    public double getValue() {
        if (this.dirty) {
            this.cachedValue = this.calculateValue();
            this.dirty = false;
        }
        return this.cachedValue;
    }
    
    private double calculateValue() {
        double double2 = this.getBaseValue();
        for (final AttributeModifier apv5 : this.getModifiersOrEmpty(AttributeModifier.Operation.ADDITION)) {
            double2 += apv5.getAmount();
        }
        double double3 = double2;
        for (final AttributeModifier apv6 : this.getModifiersOrEmpty(AttributeModifier.Operation.MULTIPLY_BASE)) {
            double3 += double2 * apv6.getAmount();
        }
        for (final AttributeModifier apv6 : this.getModifiersOrEmpty(AttributeModifier.Operation.MULTIPLY_TOTAL)) {
            double3 *= 1.0 + apv6.getAmount();
        }
        return this.attribute.sanitizeValue(double3);
    }
    
    private Collection<AttributeModifier> getModifiersOrEmpty(final AttributeModifier.Operation a) {
        return this.modifiersByOperation.getOrDefault(a, Collections.<AttributeModifier>emptySet());
    }
    
    public void replaceFrom(final AttributeInstance apt) {
        this.baseValue = apt.baseValue;
        this.modifierById.clear();
        this.modifierById.putAll(apt.modifierById);
        this.permanentModifiers.clear();
        this.permanentModifiers.addAll(apt.permanentModifiers);
        this.modifiersByOperation.clear();
        apt.modifiersByOperation.forEach((a, set) -> this.getModifiers(a).addAll(set));
        this.setDirty();
    }
    
    public CompoundTag save() {
        final CompoundTag le2 = new CompoundTag();
        le2.putString("Name", Registry.ATTRIBUTE.getKey(this.attribute).toString());
        le2.putDouble("Base", this.baseValue);
        if (!this.permanentModifiers.isEmpty()) {
            final ListTag lk3 = new ListTag();
            for (final AttributeModifier apv5 : this.permanentModifiers) {
                ((AbstractList<CompoundTag>)lk3).add(apv5.save());
            }
            le2.put("Modifiers", lk3);
        }
        return le2;
    }
    
    public void load(final CompoundTag le) {
        this.baseValue = le.getDouble("Base");
        if (le.contains("Modifiers", 9)) {
            final ListTag lk3 = le.getList("Modifiers", 10);
            for (int integer4 = 0; integer4 < lk3.size(); ++integer4) {
                final AttributeModifier apv5 = AttributeModifier.load(lk3.getCompound(integer4));
                if (apv5 != null) {
                    this.modifierById.put(apv5.getId(), apv5);
                    this.getModifiers(apv5.getOperation()).add(apv5);
                    this.permanentModifiers.add(apv5);
                }
            }
        }
        this.setDirty();
    }
}
