/*
 * Decompiled with CFR 0.152.
 * 
 * Could not load the following classes:
 *  meteordevelopment.orbit.EventHandler
 *  net.minecraft.class_1109
 *  net.minecraft.class_1113
 *  net.minecraft.class_1802
 *  net.minecraft.class_1923
 *  net.minecraft.class_2246
 *  net.minecraft.class_2248
 *  net.minecraft.class_2338
 *  net.minecraft.class_2680
 *  net.minecraft.class_2791
 *  net.minecraft.class_2818
 *  net.minecraft.class_3414
 *  net.minecraft.class_3417
 *  net.minecraft.class_368
 */
package meteordevelopment.meteorclient.systems.modules.donut;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import meteordevelopment.meteorclient.events.render.Render3DEvent;
import meteordevelopment.meteorclient.events.world.BlockUpdateEvent;
import meteordevelopment.meteorclient.events.world.ChunkDataEvent;
import meteordevelopment.meteorclient.events.world.TickEvent;
import meteordevelopment.meteorclient.renderer.ShapeMode;
import meteordevelopment.meteorclient.settings.BoolSetting;
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.render.MeteorToast;
import meteordevelopment.meteorclient.utils.render.RenderUtils;
import meteordevelopment.meteorclient.utils.render.color.Color;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.class_1109;
import net.minecraft.class_1113;
import net.minecraft.class_1802;
import net.minecraft.class_1923;
import net.minecraft.class_2246;
import net.minecraft.class_2248;
import net.minecraft.class_2338;
import net.minecraft.class_2680;
import net.minecraft.class_2791;
import net.minecraft.class_2818;
import net.minecraft.class_3414;
import net.minecraft.class_3417;
import net.minecraft.class_368;

public class AmethystEsp
extends Module {
    private final SettingGroup sgGeneral;
    private final Setting<Boolean> tracers;
    private final Setting<Boolean> highlightChunks;
    private final Setting<Integer> clusterThreshold;
    private final Setting<Integer> alpha;
    private final Setting<Boolean> chatFeedback;
    private final int rangeVal = 8;
    private final int minY = -64;
    private final int maxY = 320;
    private final Map<class_1923, ChunkData> chunkData;
    private final Set<class_2338> clusterPositions;
    private final Set<class_1923> flaggedChunks;
    private final Map<class_1923, Long> notificationCooldowns;
    private ExecutorService threadPool;
    private long lastCleanup;
    private long lastReFlag;
    private long lastRescan;
    private volatile boolean active;

    public AmethystEsp() {
        super(Categories.Donut, "Amethyst esp", "Highlights amethyst buds and clusters.");
        this.sgGeneral = this.settings.getDefaultGroup();
        this.tracers = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("tracers")).defaultValue(false)).build());
        this.highlightChunks = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("highlight-chunks")).defaultValue(true)).build());
        this.clusterThreshold = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("threshold")).defaultValue(15)).range(1, 40).build());
        this.alpha = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("alpha")).defaultValue(140)).range(0, 255).build());
        this.chatFeedback = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("chat-feedback")).defaultValue(false)).build());
        this.rangeVal = 8;
        this.minY = -64;
        this.maxY = 320;
        this.chunkData = new ConcurrentHashMap<class_1923, ChunkData>();
        this.clusterPositions = ConcurrentHashMap.newKeySet();
        this.flaggedChunks = ConcurrentHashMap.newKeySet();
        this.notificationCooldowns = new ConcurrentHashMap<class_1923, Long>();
    }

    @Override
    public void onActivate() {
        if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
            return;
        }
        this.active = true;
        this.chunkData.clear();
        this.clusterPositions.clear();
        this.flaggedChunks.clear();
        this.notificationCooldowns.clear();
        this.lastCleanup = System.currentTimeMillis();
        this.lastReFlag = 0L;
        this.lastRescan = 0L;
        this.threadPool = Executors.newFixedThreadPool(3);
        this.threadPool.submit(() -> {
            try {
                if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
                    return;
                }
                class_1923 playerChunk = new class_1923(this.mc.field_1724.method_24515());
                for (int x = -8; x <= 8; ++x) {
                    for (int z = -8; z <= 8; ++z) {
                        class_2818 chunk;
                        if (!this.isActive() || this.mc.field_1687 == null) {
                            return;
                        }
                        int cx = playerChunk.field_9181 + x;
                        int cz = playerChunk.field_9180 + z;
                        if (!this.mc.field_1687.method_2935().method_12123(cx, cz) || !((chunk = this.mc.field_1687.method_8497(cx, cz)) instanceof class_2818)) continue;
                        class_2818 worldChunk = chunk;
                        this.scanChunk(worldChunk);
                    }
                }
            }
            catch (Exception exception) {
                // empty catch block
            }
        });
    }

    @Override
    public void onDeactivate() {
        this.active = false;
        if (this.threadPool != null && !this.threadPool.isShutdown()) {
            this.threadPool.shutdownNow();
            this.threadPool = null;
        }
        this.chunkData.clear();
        this.clusterPositions.clear();
        this.flaggedChunks.clear();
        this.notificationCooldowns.clear();
    }

    @EventHandler
    private void onChunkLoad(ChunkDataEvent event) {
        if (!this.active || this.mc.field_1687 == null) {
            return;
        }
        if (this.threadPool == null || this.threadPool.isShutdown()) {
            return;
        }
        this.threadPool.submit(() -> this.scanChunk(event.chunk()));
    }

    @EventHandler
    private void onBlockUpdate(BlockUpdateEvent event) {
        if (!this.active || this.mc.field_1687 == null) {
            return;
        }
        class_2248 newBlock = event.newState.method_26204();
        class_2248 oldBlock = event.oldState.method_26204();
        if (!this.isAmethyst(newBlock) && !this.isAmethyst(oldBlock)) {
            return;
        }
        Runnable updateTask = () -> {
            class_2791 chunk = this.mc.field_1687.method_22350(event.pos);
            if (chunk instanceof class_2818) {
                class_2818 worldChunk = (class_2818)chunk;
                this.scanChunk(worldChunk);
            }
        };
        if (this.threadPool != null && !this.threadPool.isShutdown()) {
            this.threadPool.submit(updateTask);
        } else {
            updateTask.run();
        }
    }

    @EventHandler
    private void onTick(TickEvent.Post event) {
        if (!this.active || this.mc.field_1724 == null) {
            return;
        }
        long now = System.currentTimeMillis();
        if (now - this.lastCleanup > 30000L) {
            this.cleanup();
            this.lastCleanup = now;
        }
        if (now - this.lastReFlag > 500L) {
            for (Map.Entry<class_1923, ChunkData> entry : this.chunkData.entrySet()) {
                this.evaluateChunk(entry.getKey(), entry.getValue());
            }
            this.lastReFlag = now;
        }
        if (now - this.lastRescan > 100L && this.threadPool != null && !this.threadPool.isShutdown()) {
            this.lastRescan = now;
            this.threadPool.submit(() -> {
                try {
                    if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
                        return;
                    }
                    class_1923 playerChunk = new class_1923(this.mc.field_1724.method_24515());
                    for (int x = -8; x <= 8; ++x) {
                        for (int z = -8; z <= 8; ++z) {
                            class_2818 chunk;
                            if (!this.isActive() || this.mc.field_1687 == null) {
                                return;
                            }
                            int cx = playerChunk.field_9181 + x;
                            int cz = playerChunk.field_9180 + z;
                            if (!this.mc.field_1687.method_2935().method_12123(cx, cz) || !((chunk = this.mc.field_1687.method_8497(cx, cz)) instanceof class_2818)) continue;
                            class_2818 worldChunk = chunk;
                            this.scanChunk(worldChunk);
                        }
                    }
                }
                catch (Exception exception) {
                    // empty catch block
                }
            });
        }
    }

    @EventHandler
    private void onRender3D(Render3DEvent event) {
        if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
            return;
        }
        Color clusterColor = new Color(147, 0, 211, this.alpha.get());
        Color highlightColor = new Color(180, 50, 230, 140);
        Color lineColor = new Color(147, 0, 211, 255);
        if (this.highlightChunks.get().booleanValue()) {
            int renderedChunks = 0;
            for (class_1923 cPos : this.flaggedChunks) {
                if (renderedChunks++ > 20) break;
                event.renderer.box(cPos.method_8326(), 45.0, cPos.method_8328(), (double)cPos.method_8326() + 16.0, 45.5, (double)cPos.method_8328() + 16.0, highlightColor, lineColor, ShapeMode.Both, 0);
            }
        }
        int renderedClusters = 0;
        for (class_2338 pos : this.clusterPositions) {
            if (renderedClusters++ >= 200) break;
            double distanceSquared = this.mc.field_1724.method_5649((double)pos.method_10263() + 0.5, (double)pos.method_10264() + 0.5, (double)pos.method_10260() + 0.5);
            if (distanceSquared > 65536.0) continue;
            event.renderer.box(pos, clusterColor, lineColor, ShapeMode.Both, 0);
            if (!this.tracers.get().booleanValue()) continue;
            event.renderer.line(RenderUtils.center.field_1352, RenderUtils.center.field_1351, RenderUtils.center.field_1350, (double)pos.method_10263() + 0.5, (double)pos.method_10264() + 0.5, (double)pos.method_10260() + 0.5, lineColor);
        }
    }

    private void cleanup() {
        if (this.mc.field_1724 == null) {
            return;
        }
        class_1923 currentChunkPos = this.mc.field_1724.method_31476();
        this.chunkData.keySet().removeIf(pos -> Math.abs(pos.field_9181 - currentChunkPos.field_9181) > 8 || Math.abs(pos.field_9180 - currentChunkPos.field_9180) > 8);
        this.flaggedChunks.removeIf(pos -> Math.abs(pos.field_9181 - currentChunkPos.field_9181) > 8 || Math.abs(pos.field_9180 - currentChunkPos.field_9180) > 8);
        this.clusterPositions.removeIf(pos -> {
            class_1923 cp = new class_1923(pos);
            return Math.abs(cp.field_9181 - currentChunkPos.field_9181) > 8 || Math.abs(cp.field_9180 - currentChunkPos.field_9180) > 8;
        });
    }

    private void scanChunk(class_2818 chunk) {
        if (!this.active || chunk == null) {
            return;
        }
        class_1923 cPos = chunk.method_12004();
        ChunkData data = new ChunkData();
        int minScanY = Math.max(chunk.method_31607(), -64);
        int maxScanY = Math.min(chunk.method_31600(), 320);
        for (int x = 0; x < 16; ++x) {
            for (int z = 0; z < 16; ++z) {
                for (int y = minScanY; y <= maxScanY; ++y) {
                    class_2338 pos = new class_2338(cPos.method_8326() + x, y, cPos.method_8328() + z);
                    class_2680 state = chunk.method_8320(pos);
                    if (!this.isFullyGrownAmethyst(state, y)) continue;
                    data.clusters.add(pos.method_10062());
                    ++data.clusterCount;
                }
            }
        }
        if (data.clusterCount > 0) {
            this.chunkData.put(cPos, data);
        } else {
            this.chunkData.remove(cPos);
        }
        this.rebuildClusterPositions();
        this.evaluateChunk(cPos, data);
    }

    private boolean isAmethyst(class_2248 block) {
        return block == class_2246.field_27161 || block == class_2246.field_27162;
    }

    private boolean isFullyGrownAmethyst(class_2680 state, int y) {
        if (y < -64 || y > 320) {
            return false;
        }
        return this.isAmethyst(state.method_26204());
    }

    private void evaluateChunk(class_1923 chunkPos, ChunkData data) {
        boolean shouldFlag;
        boolean bl = shouldFlag = data.clusterCount >= this.clusterThreshold.get();
        if (shouldFlag) {
            long last;
            boolean justFlagged = this.flaggedChunks.add(chunkPos);
            if (justFlagged && (last = this.notificationCooldowns.getOrDefault(chunkPos, 0L).longValue()) == 0L) {
                this.notificationCooldowns.put(chunkPos, System.currentTimeMillis());
                this.mc.execute(() -> {
                    this.mc.method_1566().method_1999((class_368)new MeteorToast.Builder("Amethyst ESP").icon(class_1802.field_27063).text("Found " + data.clusterCount + " clusters!").build());
                    this.mc.method_1483().method_4873((class_1113)class_1109.method_4757((class_3414)class_3417.field_14627, (float)0.7f, (float)0.8f));
                });
                if (this.chatFeedback.get().booleanValue()) {
                    this.info("Rich Amethyst Chunk found! (%d buds)", data.clusterCount);
                }
            }
        } else {
            this.flaggedChunks.remove(chunkPos);
        }
    }

    private void rebuildClusterPositions() {
        this.clusterPositions.clear();
        for (ChunkData data : this.chunkData.values()) {
            this.clusterPositions.addAll(data.clusters);
        }
    }

    private static class ChunkData {
        final Set<class_2338> clusters = ConcurrentHashMap.newKeySet();
        int clusterCount = 0;

        private ChunkData() {
        }
    }
}

