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

import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
import net.minecraft.Util;
import java.util.function.Function;
import com.mojang.datafixers.Dynamic;
import com.mojang.datafixers.types.DynamicOps;
import java.util.function.Predicate;
import java.util.function.BiFunction;
import java.util.Set;
import java.util.Arrays;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.List;
import java.util.Collection;
import java.util.Random;
import java.util.stream.Stream;
import java.util.Iterator;
import com.google.common.collect.Maps;
import java.util.UUID;
import java.util.Map;

public class GossipContainer {
    private final Map<UUID, EntityGossips> gossips;
    
    public GossipContainer() {
        this.gossips = Maps.newHashMap();
    }
    
    public void decay() {
        final Iterator<EntityGossips> iterator2 = this.gossips.values().iterator();
        while (iterator2.hasNext()) {
            final EntityGossips a3 = iterator2.next();
            a3.decay();
            if (a3.isEmpty()) {
                iterator2.remove();
            }
        }
    }
    
    private Stream<GossipEntry> unpack() {
        return this.gossips.entrySet().stream().<GossipEntry>flatMap(entry -> entry.getValue().unpack((UUID)entry.getKey()));
    }
    
    private Collection<GossipEntry> selectGossipsForTransfer(final Random random, final int integer) {
        final List<GossipEntry> list4 = this.unpack().collect(Collectors.toList());
        if (list4.isEmpty()) {
            return Collections.emptyList();
        }
        final int[] arr5 = new int[list4.size()];
        int integer2 = 0;
        for (int integer3 = 0; integer3 < list4.size(); ++integer3) {
            final GossipEntry b8 = list4.get(integer3);
            integer2 += Math.abs(b8.weightedValue());
            arr5[integer3] = integer2 - 1;
        }
        final Set<GossipEntry> set7 = Sets.<GossipEntry>newIdentityHashSet();
        for (int integer4 = 0; integer4 < integer; ++integer4) {
            final int integer5 = random.nextInt(integer2);
            final int integer6 = Arrays.binarySearch(arr5, integer5);
            set7.add(list4.get((integer6 < 0) ? (-integer6 - 1) : integer6));
        }
        return set7;
    }
    
    private EntityGossips getOrCreate(final UUID uUID) {
        return this.gossips.computeIfAbsent(uUID, uUID -> new EntityGossips());
    }
    
    public void transferFrom(final GossipContainer ari, final Random random, final int integer) {
        final Collection<GossipEntry> collection5 = ari.selectGossipsForTransfer(random, integer);
        final int integer2;
        collection5.forEach(b -> {
            integer2 = b.value - b.type.decayPerTransfer;
            if (integer2 >= 2) {
                this.getOrCreate(b.target).entries.mergeInt(b.type, integer2, (BiFunction)GossipContainer::mergeValuesForTransfer);
            }
        });
    }
    
    public int getReputation(final UUID uUID, final Predicate<GossipType> predicate) {
        final EntityGossips a4 = this.gossips.get(uUID);
        return (a4 != null) ? a4.weightedValue(predicate) : 0;
    }
    
    public void add(final UUID uUID, final GossipType arj, final int integer) {
        final EntityGossips a5 = this.getOrCreate(uUID);
        a5.entries.mergeInt(arj, integer, (integer2, integer3) -> this.mergeValuesForAddition(arj, integer2, integer3));
        a5.makeSureValueIsntTooLowOrTooHigh(arj);
        if (a5.isEmpty()) {
            this.gossips.remove(uUID);
        }
    }
    
    public <T> Dynamic<T> store(final DynamicOps<T> dynamicOps) {
        return (Dynamic<T>)new Dynamic((DynamicOps)dynamicOps, dynamicOps.createList((Stream)this.unpack().map(b -> b.<T>store(dynamicOps)).map(Dynamic::getValue)));
    }
    
    public void update(final Dynamic<?> dynamic) {
        dynamic.asStream().map(GossipEntry::load).flatMap(Util::toStream).forEach(b -> this.getOrCreate(b.target).entries.put(b.type, b.value));
    }
    
    private static int mergeValuesForTransfer(final int integer1, final int integer2) {
        return Math.max(integer1, integer2);
    }
    
    private int mergeValuesForAddition(final GossipType arj, final int integer2, final int integer3) {
        final int integer4 = integer2 + integer3;
        return (integer4 > arj.max) ? Math.max(arj.max, integer2) : integer4;
    }
    
    static class GossipEntry {
        public final UUID target;
        public final GossipType type;
        public final int value;
        
        public GossipEntry(final UUID uUID, final GossipType arj, final int integer) {
            this.target = uUID;
            this.type = arj;
            this.value = integer;
        }
        
        public int weightedValue() {
            return this.value * this.type.weight;
        }
        
        @Override
        public String toString() {
            return "GossipEntry{target=" + this.target + ", type=" + this.type + ", value=" + this.value + '}';
        }
        
        public <T> Dynamic<T> store(final DynamicOps<T> dynamicOps) {
            return Util.<T>writeUUID("Target", this.target, (com.mojang.datafixers.Dynamic<T>)new Dynamic((DynamicOps)dynamicOps, dynamicOps.createMap((Map)ImmutableMap.of(dynamicOps.createString("Type"), dynamicOps.createString(this.type.id), dynamicOps.createString("Value"), dynamicOps.createInt(this.value)))));
        }
        
        public static Optional<GossipEntry> load(final Dynamic<?> dynamic) {
            return dynamic.get("Type").asString().map(GossipType::byId).<GossipEntry>flatMap(arj -> Util.readUUID("Target", dynamic).flatMap(uUID -> dynamic.get("Value").asNumber().map(number -> new GossipEntry(uUID, arj, number.intValue()))));
        }
    }
    
    static class EntityGossips {
        private final Object2IntMap<GossipType> entries;
        
        private EntityGossips() {
            this.entries = (Object2IntMap<GossipType>)new Object2IntOpenHashMap();
        }
        
        public int weightedValue(final Predicate<GossipType> predicate) {
            return this.entries.object2IntEntrySet().stream().filter(entry -> predicate.test((GossipType)entry.getKey())).mapToInt(entry -> entry.getIntValue() * ((GossipType)entry.getKey()).weight).sum();
        }
        
        public Stream<GossipEntry> unpack(final UUID uUID) {
            return this.entries.object2IntEntrySet().stream().<GossipEntry>map(entry -> new GossipEntry(uUID, (GossipType)entry.getKey(), entry.getIntValue()));
        }
        
        public void decay() {
            final ObjectIterator<Object2IntMap.Entry<GossipType>> objectIterator2 = (ObjectIterator<Object2IntMap.Entry<GossipType>>)this.entries.object2IntEntrySet().iterator();
            while (objectIterator2.hasNext()) {
                final Object2IntMap.Entry<GossipType> entry3 = (Object2IntMap.Entry<GossipType>)objectIterator2.next();
                final int integer4 = entry3.getIntValue() - ((GossipType)entry3.getKey()).decayPerDay;
                if (integer4 < 2) {
                    objectIterator2.remove();
                }
                else {
                    entry3.setValue(integer4);
                }
            }
        }
        
        public boolean isEmpty() {
            return this.entries.isEmpty();
        }
        
        public void makeSureValueIsntTooLowOrTooHigh(final GossipType arj) {
            final int integer3 = this.entries.getInt(arj);
            if (integer3 > arj.max) {
                this.entries.put(arj, arj.max);
            }
            if (integer3 < 2) {
                this.remove(arj);
            }
        }
        
        public void remove(final GossipType arj) {
            this.entries.removeInt(arj);
        }
    }
}
