package net.minecraft.client.renderer.debug;

import java.util.function.Predicate;
import javax.annotation.Nullable;
import net.minecraft.world.level.pathfinder.Path;
import org.apache.logging.log4j.LogManager;
import net.minecraft.world.entity.Entity;
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.DebugMobNameGenerator;
import net.minecraft.core.Position;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Iterator;
import net.minecraft.core.Vec3i;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.MultiBufferSource;
import com.mojang.blaze3d.vertex.PoseStack;
import com.google.common.collect.Sets;
import com.google.common.collect.Maps;
import java.util.UUID;
import net.minecraft.core.SectionPos;
import java.util.Set;
import net.minecraft.core.BlockPos;
import java.util.Map;
import net.minecraft.client.Minecraft;
import org.apache.logging.log4j.Logger;

public class VillageDebugRenderer implements DebugRenderer.SimpleDebugRenderer {
    private static final Logger LOGGER;
    private final Minecraft minecraft;
    private final Map<BlockPos, PoiInfo> pois;
    private final Set<SectionPos> villageSections;
    private final Map<UUID, BrainDump> brainDumpsPerEntity;
    private UUID lastLookedAtUuid;
    
    public VillageDebugRenderer(final Minecraft dbn) {
        this.pois = Maps.newHashMap();
        this.villageSections = Sets.newHashSet();
        this.brainDumpsPerEntity = Maps.newHashMap();
        this.minecraft = dbn;
    }
    
    @Override
    public void clear() {
        this.pois.clear();
        this.villageSections.clear();
        this.brainDumpsPerEntity.clear();
        this.lastLookedAtUuid = null;
    }
    
    public void addPoi(final PoiInfo b) {
        this.pois.put(b.pos, b);
    }
    
    public void removePoi(final BlockPos fk) {
        this.pois.remove(fk);
    }
    
    public void setFreeTicketCount(final BlockPos fk, final int integer) {
        final PoiInfo b4 = this.pois.get(fk);
        if (b4 == null) {
            VillageDebugRenderer.LOGGER.warn("Strange, setFreeTicketCount was called for an unknown POI: " + fk);
            return;
        }
        b4.freeTicketCount = integer;
    }
    
    public void setVillageSection(final SectionPos gd) {
        this.villageSections.add(gd);
    }
    
    public void setNotVillageSection(final SectionPos gd) {
        this.villageSections.remove(gd);
    }
    
    public void addOrUpdateBrainDump(final BrainDump a) {
        this.brainDumpsPerEntity.put(a.uuid, a);
    }
    
    @Override
    public void render(final PoseStack cyl, final MultiBufferSource dqt, final double double3, final double double4, final double double5) {
        RenderSystem.pushMatrix();
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        RenderSystem.disableTexture();
        this.doRender(double3, double4, double5);
        RenderSystem.enableTexture();
        RenderSystem.disableBlend();
        RenderSystem.popMatrix();
        if (!this.minecraft.player.isSpectator()) {
            this.updateLastLookedAtUuid();
        }
    }
    
    private void doRender(final double double1, final double double2, final double double3) {
        final BlockPos fk3 = new BlockPos(double1, double2, double3);
        this.villageSections.forEach(gd -> {
            if (fk3.closerThan(gd.center(), 60.0)) {
                highlightVillageSection(gd);
            }
            return;
        });
        this.brainDumpsPerEntity.values().forEach(a -> {
            if (this.isPlayerCloseEnoughToMob(a)) {
                this.renderVillagerInfo(a, double1, double2, double3);
            }
            return;
        });
        for (final BlockPos fk4 : this.pois.keySet()) {
            if (fk3.closerThan(fk4, 30.0)) {
                highlightPoi(fk4);
            }
        }
        this.pois.values().forEach(b -> {
            if (fk3.closerThan(b.pos, 30.0)) {
                this.renderPoiInfo(b);
            }
            return;
        });
        this.getGhostPois().forEach((fk2, list) -> {
            if (fk3.closerThan(fk2, 30.0)) {
                this.renderGhostPoi(fk2, list);
            }
        });
    }
    
    private static void highlightVillageSection(final SectionPos gd) {
        final float float2 = 1.0f;
        final BlockPos fk3 = gd.center();
        final BlockPos fk4 = fk3.offset(-1.0, -1.0, -1.0);
        final BlockPos fk5 = fk3.offset(1.0, 1.0, 1.0);
        DebugRenderer.renderFilledBox(fk4, fk5, 0.2f, 1.0f, 0.2f, 0.15f);
    }
    
    private static void highlightPoi(final BlockPos fk) {
        final float float2 = 0.05f;
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        DebugRenderer.renderFilledBox(fk, 0.05f, 0.2f, 0.2f, 1.0f, 0.3f);
    }
    
    private void renderGhostPoi(final BlockPos fk, final List<String> list) {
        final float float4 = 0.05f;
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        DebugRenderer.renderFilledBox(fk, 0.05f, 0.2f, 0.2f, 1.0f, 0.3f);
        renderTextOverPos("" + list, fk, 0, -256);
        renderTextOverPos("Ghost POI", fk, 1, -65536);
    }
    
    private void renderPoiInfo(final PoiInfo b) {
        int integer3 = 0;
        if (this.getTicketHolderNames(b).size() < 4) {
            renderTextOverPoi("" + this.getTicketHolderNames(b), b, integer3, -256);
        }
        else {
            renderTextOverPoi("" + this.getTicketHolderNames(b).size() + " ticket holders", b, integer3, -256);
        }
        ++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 renderVillagerInfo(final BrainDump a, final double double2, final double double3, final double double4) {
        final boolean boolean9 = this.isVillagerSelected(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 && !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 fk5 = b.pos;
        renderTextOverPos(string, fk5, integer3, integer4);
    }
    
    private static void renderTextOverPos(final String string, final BlockPos fk, final int integer3, final int integer4) {
        final double double5 = 1.3;
        final double double6 = 0.2;
        final double double7 = fk.getX() + 0.5;
        final double double8 = fk.getY() + 1.3 + integer3 * 0.2;
        final double double9 = fk.getZ() + 0.5;
        DebugRenderer.renderFloatingText(string, double7, double8, double9, integer4, 0.02f, true, 0.0f, true);
    }
    
    private static void renderTextOverMob(final Position fz, final int integer2, final String string, final int integer4, final float float5) {
        final double double6 = 2.4;
        final double double7 = 0.25;
        final BlockPos fk10 = new BlockPos(fz);
        final double double8 = fk10.getX() + 0.5;
        final double double9 = fz.y() + 2.4 + integer2 * 0.25;
        final double double10 = fk10.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(DebugMobNameGenerator::getMobName).collect(Collectors.toSet());
    }
    
    private boolean isVillagerSelected(final BrainDump a) {
        return Objects.equals(this.lastLookedAtUuid, a.uuid);
    }
    
    private boolean isPlayerCloseEnoughToMob(final BrainDump a) {
        final Player ayj3 = this.minecraft.player;
        final BlockPos fk4 = new BlockPos(ayj3.getX(), a.pos.y(), ayj3.getZ());
        final BlockPos fk5 = new BlockPos(a.pos);
        return fk4.closerThan(fk5, 30.0);
    }
    
    private Collection<UUID> getTicketHolders(final BlockPos fk) {
        return this.brainDumpsPerEntity.values().stream().filter(a -> a.hasPoi(fk)).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 fk6 : a4.pois) {
                if (!this.pois.containsKey(fk6)) {
                    List<String> list7 = map2.get(fk6);
                    if (list7 == null) {
                        list7 = Lists.newArrayList();
                        map2.put(fk6, list7);
                    }
                    list7.add(a4.name);
                }
            }
        }
        return map2;
    }
    
    private void updateLastLookedAtUuid() {
        DebugRenderer.getTargetedEntity(this.minecraft.getCameraEntity(), 8).ifPresent(akq -> this.lastLookedAtUuid = akq.getUUID());
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
    
    public static class PoiInfo {
        public final BlockPos pos;
        public String type;
        public int freeTicketCount;
        
        public PoiInfo(final BlockPos fk, final String string, final int integer) {
            this.pos = fk;
            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 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 BrainDump(final UUID uUID, final int integer2, final String string3, final String string4, final int integer5, final Position fz, final String string7, @Nullable final Path cqq, final boolean boolean9) {
            this.activities = Lists.newArrayList();
            this.behaviors = Lists.newArrayList();
            this.memories = Lists.newArrayList();
            this.gossips = Lists.newArrayList();
            this.pois = Sets.newHashSet();
            this.uuid = uUID;
            this.id = integer2;
            this.name = string3;
            this.profession = string4;
            this.xp = integer5;
            this.pos = fz;
            this.inventory = string7;
            this.path = cqq;
            this.wantsGolem = boolean9;
        }
        
        private boolean hasPoi(final BlockPos fk) {
            return this.pois.stream().anyMatch(fk::equals);
        }
        
        public UUID getUuid() {
            return this.uuid;
        }
    }
}
