package net.minecraft.client.renderer.debug;

import java.util.function.Predicate;
import com.google.common.collect.Sets;
import net.minecraft.world.level.pathfinder.Path;
import org.apache.logging.log4j.LogManager;
import com.google.common.collect.Iterables;
import java.util.Collection;
import net.minecraft.world.entity.player.Player;
import java.util.Objects;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.function.Function;
import net.minecraft.network.protocol.game.DebugEntityNameGenerator;
import net.minecraft.core.Position;
import com.google.common.collect.Lists;
import java.util.Set;
import java.util.List;
import java.util.Iterator;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.Entity;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.MultiBufferSource;
import com.mojang.blaze3d.vertex.PoseStack;
import com.google.common.collect.Maps;
import javax.annotation.Nullable;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import java.util.Map;
import net.minecraft.client.Minecraft;
import org.apache.logging.log4j.Logger;

public class BrainDebugRenderer implements DebugRenderer.SimpleDebugRenderer {
    private static final Logger LOGGER;
    private final Minecraft minecraft;
    private final Map<BlockPos, PoiInfo> pois;
    private final Map<UUID, BrainDump> brainDumpsPerEntity;
    @Nullable
    private UUID lastLookedAtUuid;
    
    public BrainDebugRenderer(final Minecraft dlx) {
        this.pois = Maps.newHashMap();
        this.brainDumpsPerEntity = Maps.newHashMap();
        this.minecraft = dlx;
    }
    
    @Override
    public void clear() {
        this.pois.clear();
        this.brainDumpsPerEntity.clear();
        this.lastLookedAtUuid = null;
    }
    
    public void addPoi(final PoiInfo b) {
        this.pois.put(b.pos, b);
    }
    
    public void removePoi(final BlockPos fu) {
        this.pois.remove(fu);
    }
    
    public void setFreeTicketCount(final BlockPos fu, final int integer) {
        final PoiInfo b4 = this.pois.get(fu);
        if (b4 == null) {
            BrainDebugRenderer.LOGGER.warn("Strange, setFreeTicketCount was called for an unknown POI: " + fu);
            return;
        }
        b4.freeTicketCount = integer;
    }
    
    public void addOrUpdateBrainDump(final BrainDump a) {
        this.brainDumpsPerEntity.put(a.uuid, a);
    }
    
    @Override
    public void render(final PoseStack dhl, final MultiBufferSource ebz, final double double3, final double double4, final double double5) {
        RenderSystem.pushMatrix();
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        RenderSystem.disableTexture();
        this.clearRemovedEntities();
        this.doRender(double3, double4, double5);
        RenderSystem.enableTexture();
        RenderSystem.disableBlend();
        RenderSystem.popMatrix();
        if (!this.minecraft.player.isSpectator()) {
            this.updateLastLookedAtUuid();
        }
    }
    
    private void clearRemovedEntities() {
        final Entity aom3;
        this.brainDumpsPerEntity.entrySet().removeIf(entry -> {
            aom3 = this.minecraft.level.getEntity(entry.getValue().id);
            return aom3 == null || aom3.removed;
        });
    }
    
    private void doRender(final double double1, final double double2, final double double3) {
        final BlockPos fu3 = new BlockPos(double1, double2, double3);
        this.brainDumpsPerEntity.values().forEach(a -> {
            if (this.isPlayerCloseEnoughToMob(a)) {
                this.renderBrainInfo(a, double1, double2, double3);
            }
            return;
        });
        for (final BlockPos fu4 : this.pois.keySet()) {
            if (fu3.closerThan(fu4, 30.0)) {
                highlightPoi(fu4);
            }
        }
        this.pois.values().forEach(b -> {
            if (fu3.closerThan(b.pos, 30.0)) {
                this.renderPoiInfo(b);
            }
            return;
        });
        this.getGhostPois().forEach((fu2, list) -> {
            if (fu3.closerThan(fu2, 30.0)) {
                this.renderGhostPoi(fu2, list);
            }
        });
    }
    
    private static void highlightPoi(final BlockPos fu) {
        final float float2 = 0.05f;
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        DebugRenderer.renderFilledBox(fu, 0.05f, 0.2f, 0.2f, 1.0f, 0.3f);
    }
    
    private void renderGhostPoi(final BlockPos fu, final List<String> list) {
        final float float4 = 0.05f;
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        DebugRenderer.renderFilledBox(fu, 0.05f, 0.2f, 0.2f, 1.0f, 0.3f);
        renderTextOverPos("" + list, fu, 0, -256);
        renderTextOverPos("Ghost POI", fu, 1, -65536);
    }
    
    private void renderPoiInfo(final PoiInfo b) {
        int integer3 = 0;
        final Set<String> set4 = this.getTicketHolderNames(b);
        if (set4.size() < 4) {
            renderTextOverPoi("Owners: " + set4, b, integer3, -256);
        }
        else {
            renderTextOverPoi("" + set4.size() + " ticket holders", b, integer3, -256);
        }
        ++integer3;
        final Set<String> set5 = this.getPotentialTicketHolderNames(b);
        if (set5.size() < 4) {
            renderTextOverPoi("Candidates: " + set5, b, integer3, -23296);
        }
        else {
            renderTextOverPoi("" + set5.size() + " potential owners", b, integer3, -23296);
        }
        ++integer3;
        renderTextOverPoi("Free tickets: " + b.freeTicketCount, b, integer3, -256);
        ++integer3;
        renderTextOverPoi(b.type, b, integer3, -1);
    }
    
    private void renderPath(final BrainDump a, final double double2, final double double3, final double double4) {
        if (a.path != null) {
            PathfindingRenderer.renderPath(a.path, 0.5f, false, false, double2, double3, double4);
        }
    }
    
    private void renderBrainInfo(final BrainDump a, final double double2, final double double3, final double double4) {
        final boolean boolean9 = this.isMobSelected(a);
        int integer10 = 0;
        renderTextOverMob(a.pos, integer10, a.name, -1, 0.03f);
        ++integer10;
        if (boolean9) {
            renderTextOverMob(a.pos, integer10, a.profession + " " + a.xp + " xp", -1, 0.02f);
            ++integer10;
        }
        if (boolean9) {
            final int integer11 = (a.health < a.maxHealth) ? -23296 : -1;
            renderTextOverMob(a.pos, integer10, "health: " + String.format("%.1f", a.health) + " / " + String.format("%.1f", a.maxHealth), integer11, 0.02f);
            ++integer10;
        }
        if (boolean9 && !a.inventory.equals("")) {
            renderTextOverMob(a.pos, integer10, a.inventory, -98404, 0.02f);
            ++integer10;
        }
        if (boolean9) {
            for (final String string12 : a.behaviors) {
                renderTextOverMob(a.pos, integer10, string12, -16711681, 0.02f);
                ++integer10;
            }
        }
        if (boolean9) {
            for (final String string12 : a.activities) {
                renderTextOverMob(a.pos, integer10, string12, -16711936, 0.02f);
                ++integer10;
            }
        }
        if (a.wantsGolem) {
            renderTextOverMob(a.pos, integer10, "Wants Golem", -23296, 0.02f);
            ++integer10;
        }
        if (boolean9) {
            for (final String string12 : a.gossips) {
                if (string12.startsWith(a.name)) {
                    renderTextOverMob(a.pos, integer10, string12, -1, 0.02f);
                }
                else {
                    renderTextOverMob(a.pos, integer10, string12, -23296, 0.02f);
                }
                ++integer10;
            }
        }
        if (boolean9) {
            for (final String string12 : Lists.<String>reverse(a.memories)) {
                renderTextOverMob(a.pos, integer10, string12, -3355444, 0.02f);
                ++integer10;
            }
        }
        if (boolean9) {
            this.renderPath(a, double2, double3, double4);
        }
    }
    
    private static void renderTextOverPoi(final String string, final PoiInfo b, final int integer3, final int integer4) {
        final BlockPos fu5 = b.pos;
        renderTextOverPos(string, fu5, integer3, integer4);
    }
    
    private static void renderTextOverPos(final String string, final BlockPos fu, final int integer3, final int integer4) {
        final double double5 = 1.3;
        final double double6 = 0.2;
        final double double7 = fu.getX() + 0.5;
        final double double8 = fu.getY() + 1.3 + integer3 * 0.2;
        final double double9 = fu.getZ() + 0.5;
        DebugRenderer.renderFloatingText(string, double7, double8, double9, integer4, 0.02f, true, 0.0f, true);
    }
    
    private static void renderTextOverMob(final Position gj, final int integer2, final String string, final int integer4, final float float5) {
        final double double6 = 2.4;
        final double double7 = 0.25;
        final BlockPos fu10 = new BlockPos(gj);
        final double double8 = fu10.getX() + 0.5;
        final double double9 = gj.y() + 2.4 + integer2 * 0.25;
        final double double10 = fu10.getZ() + 0.5;
        final float float6 = 0.5f;
        DebugRenderer.renderFloatingText(string, double8, double9, double10, integer4, float5, false, 0.5f, true);
    }
    
    private Set<String> getTicketHolderNames(final PoiInfo b) {
        return this.getTicketHolders(b.pos).stream().map(DebugEntityNameGenerator::getEntityName).collect(Collectors.toSet());
    }
    
    private Set<String> getPotentialTicketHolderNames(final PoiInfo b) {
        return this.getPotentialTicketHolders(b.pos).stream().map(DebugEntityNameGenerator::getEntityName).collect(Collectors.toSet());
    }
    
    private boolean isMobSelected(final BrainDump a) {
        return Objects.equals(this.lastLookedAtUuid, a.uuid);
    }
    
    private boolean isPlayerCloseEnoughToMob(final BrainDump a) {
        final Player bec3 = this.minecraft.player;
        final BlockPos fu4 = new BlockPos(bec3.getX(), a.pos.y(), bec3.getZ());
        final BlockPos fu5 = new BlockPos(a.pos);
        return fu4.closerThan(fu5, 30.0);
    }
    
    private Collection<UUID> getTicketHolders(final BlockPos fu) {
        return this.brainDumpsPerEntity.values().stream().filter(a -> a.hasPoi(fu)).map(BrainDump::getUuid).collect(Collectors.toSet());
    }
    
    private Collection<UUID> getPotentialTicketHolders(final BlockPos fu) {
        return this.brainDumpsPerEntity.values().stream().filter(a -> a.hasPotentialPoi(fu)).map(BrainDump::getUuid).collect(Collectors.toSet());
    }
    
    private Map<BlockPos, List<String>> getGhostPois() {
        final Map<BlockPos, List<String>> map2 = Maps.newHashMap();
        for (final BrainDump a4 : this.brainDumpsPerEntity.values()) {
            for (final BlockPos fu6 : Iterables.<BlockPos>concat(a4.pois, a4.potentialPois)) {
                if (!this.pois.containsKey(fu6)) {
                    map2.computeIfAbsent(fu6, fu -> Lists.newArrayList()).add(a4.name);
                }
            }
        }
        return map2;
    }
    
    private void updateLastLookedAtUuid() {
        DebugRenderer.getTargetedEntity(this.minecraft.getCameraEntity(), 8).ifPresent(aom -> this.lastLookedAtUuid = aom.getUUID());
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
    
    public static class PoiInfo {
        public final BlockPos pos;
        public String type;
        public int freeTicketCount;
        
        public PoiInfo(final BlockPos fu, final String string, final int integer) {
            this.pos = fu;
            this.type = string;
            this.freeTicketCount = integer;
        }
    }
    
    public static class BrainDump {
        public final UUID uuid;
        public final int id;
        public final String name;
        public final String profession;
        public final int xp;
        public final float health;
        public final float maxHealth;
        public final Position pos;
        public final String inventory;
        public final Path path;
        public final boolean wantsGolem;
        public final List<String> activities;
        public final List<String> behaviors;
        public final List<String> memories;
        public final List<String> gossips;
        public final Set<BlockPos> pois;
        public final Set<BlockPos> potentialPois;
        
        public BrainDump(final UUID uUID, final int integer2, final String string3, final String string4, final int integer5, final float float6, final float float7, final Position gj, final String string9, @Nullable final Path czf, final boolean boolean11) {
            this.activities = Lists.newArrayList();
            this.behaviors = Lists.newArrayList();
            this.memories = Lists.newArrayList();
            this.gossips = Lists.newArrayList();
            this.pois = Sets.newHashSet();
            this.potentialPois = Sets.newHashSet();
            this.uuid = uUID;
            this.id = integer2;
            this.name = string3;
            this.profession = string4;
            this.xp = integer5;
            this.health = float6;
            this.maxHealth = float7;
            this.pos = gj;
            this.inventory = string9;
            this.path = czf;
            this.wantsGolem = boolean11;
        }
        
        private boolean hasPoi(final BlockPos fu) {
            return this.pois.stream().anyMatch(fu::equals);
        }
        
        private boolean hasPotentialPoi(final BlockPos fu) {
            return this.potentialPois.contains(fu);
        }
        
        public UUID getUuid() {
            return this.uuid;
        }
    }
}
