/*
 * Decompiled with CFR 0.152.
 * 
 * Could not load the following classes:
 *  meteordevelopment.orbit.EventHandler
 *  net.minecraft.class_1297
 *  net.minecraft.class_1439
 *  net.minecraft.class_1451
 *  net.minecraft.class_1646
 *  net.minecraft.class_1799
 *  net.minecraft.class_1802
 *  net.minecraft.class_1923
 *  net.minecraft.class_1935
 *  net.minecraft.class_2246
 *  net.minecraft.class_2338
 *  net.minecraft.class_2350$class_2351
 *  net.minecraft.class_2561
 *  net.minecraft.class_2596
 *  net.minecraft.class_2680
 *  net.minecraft.class_2741
 *  net.minecraft.class_2743
 *  net.minecraft.class_2769
 *  net.minecraft.class_2791
 *  net.minecraft.class_2818
 *  net.minecraft.class_327
 *  net.minecraft.class_332
 *  net.minecraft.class_3414
 *  net.minecraft.class_3417
 *  net.minecraft.class_3419
 *  net.minecraft.class_368
 *  net.minecraft.class_4019
 */
package meteordevelopment.meteorclient.systems.modules.donut;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import meteordevelopment.meteorclient.events.packets.PacketEvent;
import meteordevelopment.meteorclient.events.render.Render3DEvent;
import meteordevelopment.meteorclient.events.world.BlockUpdateEvent;
import meteordevelopment.meteorclient.events.world.ChunkDataEvent;
import meteordevelopment.meteorclient.renderer.ShapeMode;
import meteordevelopment.meteorclient.settings.BoolSetting;
import meteordevelopment.meteorclient.settings.ColorSetting;
import meteordevelopment.meteorclient.settings.EnumSetting;
import meteordevelopment.meteorclient.settings.IntSetting;
import meteordevelopment.meteorclient.settings.Setting;
import meteordevelopment.meteorclient.settings.SettingGroup;
import meteordevelopment.meteorclient.systems.modules.Categories;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.meteorclient.utils.Utils;
import meteordevelopment.meteorclient.utils.player.PlayerUtils;
import meteordevelopment.meteorclient.utils.render.MeteorToast;
import meteordevelopment.meteorclient.utils.render.RenderUtils;
import meteordevelopment.meteorclient.utils.render.color.Color;
import meteordevelopment.meteorclient.utils.render.color.SettingColor;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.class_1297;
import net.minecraft.class_1439;
import net.minecraft.class_1451;
import net.minecraft.class_1646;
import net.minecraft.class_1799;
import net.minecraft.class_1802;
import net.minecraft.class_1923;
import net.minecraft.class_1935;
import net.minecraft.class_2246;
import net.minecraft.class_2338;
import net.minecraft.class_2350;
import net.minecraft.class_2561;
import net.minecraft.class_2596;
import net.minecraft.class_2680;
import net.minecraft.class_2741;
import net.minecraft.class_2743;
import net.minecraft.class_2769;
import net.minecraft.class_2791;
import net.minecraft.class_2818;
import net.minecraft.class_327;
import net.minecraft.class_332;
import net.minecraft.class_3414;
import net.minecraft.class_3417;
import net.minecraft.class_3419;
import net.minecraft.class_368;
import net.minecraft.class_4019;

public class ChunkFinderV3
extends Module {
    private final SettingGroup sgGeneral;
    private final SettingGroup sgDetection;
    private final Setting<Boolean> showCoords;
    private final Setting<Integer> renderDistance;
    private final Setting<SettingColor> chunkColor;
    private final Setting<Boolean> showTracers;
    private final Setting<Boolean> velocityDetection;
    private final Setting<Boolean> suspiciousEntities;
    private final Setting<HoleMode> holeMode;
    private final Setting<Integer> holeCheckRadius;
    private static final int MIN_VINE_LENGTH = 20;
    private static final int MIN_KELP_LENGTH = 20;
    private static final int ROTATED_DEEPSLATE_THRESHOLD = 5;
    private static final int ENTITY_MAX_DISTANCE = 256;
    private static final int MIN_Y_LEVEL = 16;
    private final Map<class_1923, DetectionInfo> detectedChunks;
    private final Set<class_1923> chunksWithBrokenBlocks;
    private final Set<Integer> detectedEntities;
    private final Set<class_1923> scannedChunks;

    public ChunkFinderV3() {
        super(Categories.Donut, "chunk-finder-v3", "Advanced suspicious chunk detector using EntityVelocity packets.");
        this.sgGeneral = this.settings.getDefaultGroup();
        this.sgDetection = this.settings.createGroup("Detection");
        this.showCoords = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("show-coordinates")).description("Show coordinates in chat and notifications")).defaultValue(true)).build());
        this.renderDistance = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("render-distance")).description("Hide detections further than this distance.")).defaultValue(512)).min(32).sliderMax(2048).build());
        this.chunkColor = this.sgGeneral.add(((ColorSetting.Builder)((ColorSetting.Builder)new ColorSetting.Builder().name("chunk-color")).description("Color of detected chunks")).defaultValue(new SettingColor(255, 0, 0, 100)).build());
        this.showTracers = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("show-tracers")).description("Draw tracers to detected chunks")).defaultValue(false)).build());
        this.velocityDetection = this.sgDetection.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("velocity-detection")).description("Detects suspicious chunks when entity velocity packets are received.")).defaultValue(true)).build());
        this.suspiciousEntities = this.sgDetection.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("suspicious-entities")).description("Detects villager, iron golem, fox, and cat entities.")).defaultValue(true)).build());
        this.holeMode = this.sgDetection.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("hole-detection")).description("Detect chunks with or without holes")).defaultValue(HoleMode.Both)).build());
        this.holeCheckRadius = this.sgDetection.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("hole-check-radius")).description("Blocks radius to check for holes")).defaultValue(8)).min(0).max(64).visible(() -> this.holeMode.get() == HoleMode.Without_Holes)).build());
        this.detectedChunks = new ConcurrentHashMap<class_1923, DetectionInfo>();
        this.chunksWithBrokenBlocks = ConcurrentHashMap.newKeySet();
        this.detectedEntities = ConcurrentHashMap.newKeySet();
        this.scannedChunks = ConcurrentHashMap.newKeySet();
    }

    @Override
    public void onActivate() {
        this.clearData();
        if (this.mc.field_1687 != null) {
            for (class_2791 chunk : Utils.chunks()) {
                if (!(chunk instanceof class_2818)) continue;
                class_2818 worldChunk = (class_2818)chunk;
                this.scanChunk(worldChunk);
            }
        }
    }

    @Override
    public void onDeactivate() {
        this.clearData();
    }

    private void clearData() {
        this.detectedChunks.clear();
        this.chunksWithBrokenBlocks.clear();
        this.detectedEntities.clear();
        this.scannedChunks.clear();
    }

    @EventHandler
    private void onPacketReceive(PacketEvent.Receive event) {
        class_2743 packet;
        class_1297 entity;
        if (!this.velocityDetection.get().booleanValue() || this.mc.field_1687 == null) {
            return;
        }
        class_2596<?> class_25962 = event.packet;
        if (class_25962 instanceof class_2743 && (entity = this.mc.field_1687.method_8469((packet = (class_2743)class_25962).method_11818())) != null && (packet.method_73085().method_10216() != 0.0 || packet.method_73085().method_10214() != 0.0 || packet.method_73085().method_10215() != 0.0)) {
            this.addDetection(entity.method_24515(), "Velocity Update");
        }
    }

    @EventHandler
    private void onChunkLoad(ChunkDataEvent event) {
        class_2818 chunk = event.chunk();
        this.mc.execute(() -> this.scanChunk(chunk));
    }

    @EventHandler
    private void onBlockUpdate(BlockUpdateEvent event) {
        if (event.pos.method_10264() > 16) {
            class_1923 cp;
            if (event.newState.method_26215() && this.detectedChunks.containsKey(cp = new class_1923(event.pos))) {
                this.chunksWithBrokenBlocks.add(cp);
            }
            this.mc.execute(() -> {
                class_2680 state = event.newState;
                if (state.method_27852(class_2246.field_10597)) {
                    this.checkVine(event.pos);
                }
                if (state.method_27852(class_2246.field_9993) || state.method_27852(class_2246.field_10463)) {
                    this.checkKelp(event.pos);
                }
                if (state.method_27852(class_2246.field_28888)) {
                    this.checkRotatedDeepslate(event.pos);
                }
            });
        }
    }

    @EventHandler
    private void onRender(Render3DEvent event) {
        if (this.mc.field_1724 == null || this.mc.field_1687 == null) {
            return;
        }
        if (this.suspiciousEntities.get().booleanValue()) {
            this.checkEntities();
        }
        Color sideColor = this.chunkColor.get();
        Color lineColor = new Color(sideColor).a(255);
        for (Map.Entry<class_1923, DetectionInfo> entry : this.detectedChunks.entrySet()) {
            class_1923 cp = entry.getKey();
            class_2338 pos = entry.getValue().position;
            double distSq = PlayerUtils.squaredDistanceTo((double)pos.method_10263() + 0.5, (double)pos.method_10264() + 0.5, (double)pos.method_10260() + 0.5);
            if (distSq > Math.pow(this.renderDistance.get().intValue(), 2.0)) continue;
            double x1 = cp.method_8326();
            double z1 = cp.method_8328();
            double y = pos.method_10264();
            event.renderer.box(x1, y, z1, x1 + 16.0, y + 0.3, z1 + 16.0, sideColor, lineColor, ShapeMode.Both, 0);
            if (!this.showTracers.get().booleanValue() || this.chunksWithBrokenBlocks.contains(cp)) continue;
            event.renderer.line(RenderUtils.center.field_1352, RenderUtils.center.field_1351, RenderUtils.center.field_1350, x1 + 8.0, y + 0.15, z1 + 8.0, lineColor);
        }
    }

    private void addDetection(class_2338 pos, String reason) {
        if (pos.method_10264() > 16) {
            class_1923 chunkPos = new class_1923(pos);
            if (this.holeMode.get() != HoleMode.Without_Holes || !this.chunkHasHoles(chunkPos)) {
                for (class_1923 existing : this.detectedChunks.keySet()) {
                    if (Math.abs(existing.field_9181 - chunkPos.field_9181) > 2 || Math.abs(existing.field_9180 - chunkPos.field_9180) > 2) continue;
                    return;
                }
                this.detectedChunks.put(chunkPos, new DetectionInfo(pos.method_10062(), System.currentTimeMillis()));
                this.mc.execute(() -> {
                    if (this.mc.field_1687 != null && this.mc.field_1724 != null) {
                        this.mc.field_1687.method_8396((class_1297)this.mc.field_1724, pos, (class_3414)class_3417.field_14725.comp_349(), class_3419.field_15248, 1.0f, 1.0f);
                    }
                });
                if (this.showCoords.get().booleanValue()) {
                    this.info("Detected suspicious chunk (%s) at X: %d, Z: %d", reason, pos.method_10263(), pos.method_10260());
                    this.mc.method_1566().method_1999((class_368)new CustomChunkToast(new class_1799((class_1935)class_1802.field_8449), (class_2561)class_2561.method_43470((String)"ChunkFinderV3"), (class_2561)class_2561.method_43470((String)("X: " + pos.method_10263() + " Z: " + pos.method_10260() + " (" + reason + ")"))));
                }
            }
        }
    }

    private void checkEntities() {
        if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
            return;
        }
        for (class_1297 entity : this.mc.field_1687.method_18112()) {
            boolean isSuspicious;
            if (entity.method_24515().method_10264() <= 16 || !(isSuspicious = entity instanceof class_1646 || entity instanceof class_1439 || entity instanceof class_4019 || entity instanceof class_1451) || !(this.mc.field_1724.method_5739(entity) <= 256.0f) || !this.detectedEntities.add(entity.method_5628())) continue;
            this.addDetection(entity.method_24515(), "Suspicious Entity");
        }
    }

    private void scanChunk(class_2818 chunk) {
        class_1923 cpos = chunk.method_12004();
        if (this.scannedChunks.add(cpos)) {
            this.scanChunkForVines(chunk);
            this.scanChunkForKelp(chunk);
            this.scanChunkForRotatedDeepslate(chunk);
        }
    }

    private void scanChunkForRotatedDeepslate(class_2818 chunk) {
        int rotatedCount = 0;
        class_2338 firstFound = null;
        class_1923 cPos = chunk.method_12004();
        for (int x = cPos.method_8326(); x < cPos.method_8327(); ++x) {
            for (int z = cPos.method_8328(); z < cPos.method_8329(); ++z) {
                for (int y = 17; y < 319; ++y) {
                    class_2350.class_2351 axis;
                    class_2338 pos = new class_2338(x, y, z);
                    class_2680 state = chunk.method_8320(pos);
                    if (!state.method_27852(class_2246.field_28888) || !state.method_28498((class_2769)class_2741.field_12496) || (axis = (class_2350.class_2351)state.method_11654((class_2769)class_2741.field_12496)) != class_2350.class_2351.field_11048 && axis != class_2350.class_2351.field_11051) continue;
                    ++rotatedCount;
                    if (firstFound == null) {
                        firstFound = pos;
                    }
                    if (rotatedCount < 5) continue;
                    this.addDetection(firstFound, "Rotated Deepslate");
                    return;
                }
            }
        }
    }

    private void scanChunkForKelp(class_2818 chunk) {
        class_1923 cPos = chunk.method_12004();
        for (int x = cPos.method_8326(); x < cPos.method_8327(); ++x) {
            for (int z = cPos.method_8328(); z < cPos.method_8329(); ++z) {
                for (int y = 17; y < 64; ++y) {
                    class_2338 pos = new class_2338(x, y, z);
                    if (!chunk.method_8320(pos).method_27852(class_2246.field_9993) || this.getKelpLength(pos) < 20) continue;
                    this.addDetection(pos, "Long Kelp");
                }
            }
        }
    }

    private void scanChunkForVines(class_2818 chunk) {
        class_1923 cPos = chunk.method_12004();
        for (int x = cPos.method_8326(); x < cPos.method_8327(); ++x) {
            for (int z = cPos.method_8328(); z < cPos.method_8329(); ++z) {
                for (int y = 318; y > 16; --y) {
                    class_2338 pos = new class_2338(x, y, z);
                    if (!chunk.method_8320(pos).method_27852(class_2246.field_10597) || !this.vineExtendsToY16(pos) || this.getVineLength(pos) < 20) continue;
                    this.addDetection(pos, "Long Vine");
                }
            }
        }
    }

    private void checkRotatedDeepslate(class_2338 pos) {
        class_2818 chunk;
        if (this.mc.field_1687 != null && pos.method_10264() > 16 && (chunk = this.mc.field_1687.method_8497(pos.method_10263() >> 4, pos.method_10260() >> 4)) != null) {
            this.scanChunkForRotatedDeepslate(chunk);
        }
    }

    private void checkVine(class_2338 pos) {
        if (pos.method_10264() > 16 && this.vineExtendsToY16(pos) && this.getVineLength(pos) >= 20) {
            this.addDetection(pos, "Long Vine");
        }
    }

    private void checkKelp(class_2338 pos) {
        if (pos.method_10264() > 16 && this.getKelpLength(pos) >= 20) {
            this.addDetection(pos, "Long Kelp");
        }
    }

    private boolean chunkHasHoles(class_1923 cp) {
        if (this.mc.field_1687 == null) {
            return false;
        }
        int air = 0;
        int total = 0;
        int radius = this.holeCheckRadius.get();
        int cx = cp.method_33940();
        int cz = cp.method_33942();
        for (int x = cx - radius; x <= cx + radius; ++x) {
            for (int z = cz - radius; z <= cz + radius; ++z) {
                for (int y = 60; y <= 70; ++y) {
                    if (this.mc.field_1687.method_8320(new class_2338(x, y, z)).method_26215()) {
                        ++air;
                    }
                    ++total;
                }
            }
        }
        return total > 0 && (double)air / (double)total > 0.1;
    }

    private boolean vineExtendsToY16(class_2338 start) {
        if (this.mc.field_1687 == null) {
            return false;
        }
        class_2338 p = start;
        while (p.method_10264() >= 16) {
            if (!this.mc.field_1687.method_8320(p).method_27852(class_2246.field_10597)) {
                return false;
            }
            if (p.method_10264() == 16) {
                return true;
            }
            p = p.method_10074();
        }
        return false;
    }

    private int getVineLength(class_2338 start) {
        if (this.mc.field_1687 == null) {
            return 0;
        }
        int l = 0;
        class_2338 p = start;
        while (this.mc.field_1687.method_8320(p).method_27852(class_2246.field_10597)) {
            ++l;
            p = p.method_10084();
        }
        p = start.method_10074();
        while (p.method_10264() >= 16 && this.mc.field_1687.method_8320(p).method_27852(class_2246.field_10597)) {
            ++l;
            p = p.method_10074();
        }
        return l;
    }

    private int getKelpLength(class_2338 start) {
        if (this.mc.field_1687 == null) {
            return 0;
        }
        int l = 0;
        class_2338 p = start;
        while (this.mc.field_1687.method_8320(p).method_27852(class_2246.field_9993) || this.mc.field_1687.method_8320(p).method_27852(class_2246.field_10463)) {
            ++l;
            p = p.method_10084();
        }
        p = start.method_10074();
        while (p.method_10264() >= 16 && (this.mc.field_1687.method_8320(p).method_27852(class_2246.field_9993) || this.mc.field_1687.method_8320(p).method_27852(class_2246.field_10463))) {
            ++l;
            p = p.method_10074();
        }
        return l;
    }

    @Override
    public String getInfoString() {
        return this.detectedChunks.isEmpty() ? null : String.valueOf(this.detectedChunks.size());
    }

    public static enum HoleMode {
        Without_Holes,
        Both;

    }

    private record DetectionInfo(class_2338 position, long timestamp) {
    }

    private static class CustomChunkToast
    extends MeteorToast {
        private final class_1799 icon;
        private final class_2561 title;
        private final class_2561 description;

        public CustomChunkToast(class_1799 icon, class_2561 title, class_2561 description) {
            super(new MeteorToast.Builder(title.getString()).icon(icon.method_7909()).text(description.getString()));
            this.icon = icon;
            this.title = title;
            this.description = description;
        }

        @Override
        public void method_1986(class_332 context, class_327 textRenderer, long startTime) {
            context.method_25294(0, 0, 160, 32, -872415232);
            int red = -65536;
            context.method_25294(0, 0, 160, 1, red);
            context.method_25294(0, 31, 160, 32, red);
            context.method_25294(0, 1, 1, 31, red);
            context.method_25294(159, 1, 160, 31, red);
            if (this.icon != null && !this.icon.method_7960()) {
                context.method_51427(this.icon, 8, 8);
            }
            if (this.title != null) {
                context.method_51439(textRenderer, this.title, 32, 7, 0xFFFFFF, false);
            }
            if (this.description != null) {
                context.method_51439(textRenderer, this.description, 32, 18, red, false);
            }
        }
    }
}

