/*
 * Decompiled with CFR 0.152.
 * 
 * Could not load the following classes:
 *  meteordevelopment.orbit.EventHandler
 *  net.minecraft.class_1297
 *  net.minecraft.class_1799
 *  net.minecraft.class_1802
 *  net.minecraft.class_1923
 *  net.minecraft.class_1935
 *  net.minecraft.class_1937
 *  net.minecraft.class_2246
 *  net.minecraft.class_2248
 *  net.minecraft.class_2338
 *  net.minecraft.class_2561
 *  net.minecraft.class_2586
 *  net.minecraft.class_2636
 *  net.minecraft.class_2661
 *  net.minecraft.class_368
 */
package meteordevelopment.meteorclient.systems.modules.misc;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import meteordevelopment.meteorclient.events.render.Render3DEvent;
import meteordevelopment.meteorclient.events.world.ChunkDataEvent;
import meteordevelopment.meteorclient.renderer.ShapeMode;
import meteordevelopment.meteorclient.settings.BlockListSetting;
import meteordevelopment.meteorclient.settings.BoolSetting;
import meteordevelopment.meteorclient.settings.ColorSetting;
import meteordevelopment.meteorclient.settings.DoubleSetting;
import meteordevelopment.meteorclient.settings.EnumSetting;
import meteordevelopment.meteorclient.settings.IntSetting;
import meteordevelopment.meteorclient.settings.Setting;
import meteordevelopment.meteorclient.settings.SettingGroup;
import meteordevelopment.meteorclient.settings.StringSetting;
import meteordevelopment.meteorclient.systems.modules.Categories;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.meteorclient.utils.network.MeteorExecutor;
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_1799;
import net.minecraft.class_1802;
import net.minecraft.class_1923;
import net.minecraft.class_1935;
import net.minecraft.class_1937;
import net.minecraft.class_2246;
import net.minecraft.class_2248;
import net.minecraft.class_2338;
import net.minecraft.class_2561;
import net.minecraft.class_2586;
import net.minecraft.class_2636;
import net.minecraft.class_2661;
import net.minecraft.class_368;

public class BlockNotifier
extends Module {
    private final SettingGroup sgGeneral;
    private final SettingGroup sgNotifications;
    private final SettingGroup sgWebhook;
    private final SettingGroup sgRender;
    private final Setting<List<class_2248>> blocksToFind;
    private final Setting<Boolean> showSpawnerType;
    private final Setting<Integer> minY;
    private final Setting<Integer> maxY;
    private final Setting<Mode> notificationMode;
    private final Setting<Boolean> disconnectOnFind;
    private final Setting<Boolean> webhookEnabled;
    private final Setting<String> webhookUrl;
    private final Setting<Boolean> selfPing;
    private final Setting<String> discordId;
    private final Setting<Boolean> showEsp;
    private final Setting<SettingColor> espColor;
    private final Setting<ShapeMode> shapeMode;
    private final Setting<Boolean> tracers;
    private final Setting<SettingColor> tracerColor;
    private final Setting<Boolean> onlyRenderNew;
    private final Setting<Double> renderDistance;
    private final Set<class_1923> processedChunks;
    private final Set<class_2338> foundBlockPositions;
    private final Set<class_2338> newFoundBlocks;
    private final HttpClient httpClient;

    public BlockNotifier() {
        super(Categories.Misc, "block-notifier", "Notifies when specific blocks are detected with visual ESP and Discord logging.");
        this.sgGeneral = this.settings.getDefaultGroup();
        this.sgNotifications = this.settings.createGroup("Notifications");
        this.sgWebhook = this.settings.createGroup("Webhook");
        this.sgRender = this.settings.createGroup("Render");
        this.blocksToFind = this.sgGeneral.add(((BlockListSetting.Builder)((BlockListSetting.Builder)((BlockListSetting.Builder)new BlockListSetting.Builder().name("blocks-to-find")).description("Blocks to notify when found.")).defaultValue(Collections.emptyList())).build());
        this.showSpawnerType = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("show-spawner-type")).description("Shows entity type (e.g. Zombie Spawner).")).defaultValue(true)).build());
        this.minY = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("min-y")).description("Minimum Y level to search.")).defaultValue(-64)).min(-64).max(320).build());
        this.maxY = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("max-y")).description("Maximum Y level to search.")).defaultValue(320)).min(-64).max(320).build());
        this.notificationMode = this.sgNotifications.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("notification-mode")).description("How to notify you.")).defaultValue(Mode.Both)).build());
        this.disconnectOnFind = this.sgNotifications.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("disconnect-on-find")).description("Disconnects from the server when a target block is found.")).defaultValue(false)).build());
        this.webhookEnabled = this.sgWebhook.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("webhook-enabled")).description("Send a Discord webhook when blocks are found.")).defaultValue(false)).build());
        this.webhookUrl = this.sgWebhook.add(((StringSetting.Builder)((StringSetting.Builder)((StringSetting.Builder)((StringSetting.Builder)new StringSetting.Builder().name("webhook-url")).description("The Discord webhook URL.")).defaultValue("")).visible(this.webhookEnabled::get)).build());
        this.selfPing = this.sgWebhook.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("self-ping")).description("Ping yourself in the Discord message.")).defaultValue(false)).visible(this.webhookEnabled::get)).build());
        this.discordId = this.sgWebhook.add(((StringSetting.Builder)((StringSetting.Builder)((StringSetting.Builder)((StringSetting.Builder)new StringSetting.Builder().name("discord-id")).description("Your Discord ID for pinging.")).defaultValue("")).visible(() -> this.webhookEnabled.get() != false && this.selfPing.get() != false)).build());
        this.showEsp = this.sgRender.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("show-esp")).description("Render a box around found blocks.")).defaultValue(true)).build());
        this.espColor = this.sgRender.add(((ColorSetting.Builder)((ColorSetting.Builder)((ColorSetting.Builder)new ColorSetting.Builder().name("esp-color")).description("The color of the ESP.")).defaultValue(new SettingColor(255, 165, 0, 100)).visible(this.showEsp::get)).build());
        this.shapeMode = this.sgRender.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("shape-mode")).description("How the shape for the ESP is rendered.")).defaultValue(ShapeMode.Both)).visible(this.showEsp::get)).build());
        this.tracers = this.sgRender.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("tracers")).description("Render tracers to found blocks.")).defaultValue(true)).build());
        this.tracerColor = this.sgRender.add(((ColorSetting.Builder)((ColorSetting.Builder)((ColorSetting.Builder)new ColorSetting.Builder().name("tracer-color")).description("The color of the tracers.")).defaultValue(new SettingColor(255, 165, 0, 200)).visible(this.tracers::get)).build());
        this.onlyRenderNew = this.sgRender.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("only-render-new")).description("Only render blocks found in the last batch.")).defaultValue(false)).build());
        this.renderDistance = this.sgRender.add(((DoubleSetting.Builder)((DoubleSetting.Builder)new DoubleSetting.Builder().name("render-distance")).description("Maximum distance to render ESP/tracers. 0 for no limit.")).defaultValue(0.0).min(0.0).max(500.0).build());
        this.processedChunks = Collections.synchronizedSet(new HashSet());
        this.foundBlockPositions = Collections.synchronizedSet(new HashSet());
        this.newFoundBlocks = Collections.synchronizedSet(new HashSet());
        this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10L)).build();
    }

    @Override
    public void onActivate() {
        this.processedChunks.clear();
        this.foundBlockPositions.clear();
        this.newFoundBlocks.clear();
    }

    @EventHandler
    private void onChunkData(ChunkDataEvent event) {
        if (this.mc.field_1724 == null || this.mc.field_1687 == null) {
            return;
        }
        class_1923 chunkPos = event.chunk().method_12004();
        if (!this.processedChunks.contains(chunkPos)) {
            MeteorExecutor.execute(() -> {
                if (this.mc.field_1687 == null) {
                    return;
                }
                HashMap<class_2248, String> foundBlocksMap = new HashMap<class_2248, String>();
                boolean hasTargetBlocks = false;
                int startY = Math.max(this.minY.get(), this.mc.field_1687.method_31607());
                int endY = Math.min(this.maxY.get(), this.mc.field_1687.method_31600());
                for (int x = 0; x < 16; ++x) {
                    for (int z = 0; z < 16; ++z) {
                        for (int y = startY; y <= endY; ++y) {
                            class_2636 spawner;
                            class_1297 entity;
                            class_2586 be;
                            class_2338 pos = new class_2338(chunkPos.method_8326() + x, y, chunkPos.method_8328() + z);
                            if (this.mc.field_1687 == null) {
                                return;
                            }
                            class_2248 block = this.mc.field_1687.method_8320(pos).method_26204();
                            if (!this.blocksToFind.get().contains(block)) continue;
                            Object blockName = block.method_9518().getString();
                            if (this.showSpawnerType.get().booleanValue() && block == class_2246.field_10260 && (be = this.mc.field_1687.method_8321(pos)) instanceof class_2636 && (entity = (spawner = (class_2636)be).method_11390().method_8283((class_1937)this.mc.field_1687, pos)) != null) {
                                blockName = entity.method_5864().method_5897().getString() + " Spawner";
                            }
                            foundBlocksMap.put(block, (String)blockName);
                            this.foundBlockPositions.add(pos);
                            this.newFoundBlocks.add(pos);
                            hasTargetBlocks = true;
                        }
                    }
                }
                if (hasTargetBlocks) {
                    this.processedChunks.add(chunkPos);
                    this.mc.execute(() -> this.handleBlocksFound(chunkPos, foundBlocksMap));
                }
            });
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @EventHandler
    private void onRender(Render3DEvent event) {
        if (this.mc.field_1724 == null) {
            return;
        }
        if (!this.showEsp.get().booleanValue() && !this.tracers.get().booleanValue()) {
            return;
        }
        Color color = this.espColor.get();
        Color tracerColorValue = this.tracerColor.get();
        double maxDistanceSq = Math.pow(this.renderDistance.get(), 2.0);
        boolean limitDistance = this.renderDistance.get() > 0.0;
        double s = 0.15;
        Set<class_2338> set = this.foundBlockPositions;
        synchronized (set) {
            for (class_2338 pos : this.foundBlockPositions) {
                double dz;
                double dy;
                double dx;
                if (this.onlyRenderNew.get().booleanValue() && !this.newFoundBlocks.contains(pos) || limitDistance && (dx = this.mc.field_1724.method_23317() - ((double)pos.method_10263() + 0.5)) * dx + (dy = this.mc.field_1724.method_23318() - ((double)pos.method_10264() + 0.5)) * dy + (dz = this.mc.field_1724.method_23321() - ((double)pos.method_10260() + 0.5)) * dz > maxDistanceSq) continue;
                if (this.showEsp.get().booleanValue()) {
                    event.renderer.box((double)pos.method_10263() + s, (double)pos.method_10264() + s, (double)pos.method_10260() + s, (double)(pos.method_10263() + 1) - s, (double)(pos.method_10264() + 1) - s, (double)(pos.method_10260() + 1) - s, color, color, this.shapeMode.get(), 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, tracerColorValue);
            }
        }
        this.newFoundBlocks.clear();
    }

    private void handleBlocksFound(class_1923 chunkPos, Map<class_2248, String> foundBlocks) {
        String message = this.buildNotificationMessage(chunkPos, foundBlocks);
        switch (this.notificationMode.get().ordinal()) {
            case 0: {
                this.info(message, new Object[0]);
                break;
            }
            case 1: {
                this.showToastNotification(foundBlocks);
                break;
            }
            case 2: {
                this.info(message, new Object[0]);
                this.showToastNotification(foundBlocks);
            }
        }
        this.sendWebhookNotification(chunkPos, foundBlocks);
        if (this.disconnectOnFind.get().booleanValue()) {
            this.handleDisconnection(foundBlocks);
        }
    }

    private String buildNotificationMessage(class_1923 chunkPos, Map<class_2248, String> foundBlocks) {
        String names = String.join((CharSequence)", ", foundBlocks.values());
        return "Target blocks found: " + names + " at " + (chunkPos.field_9181 * 16 + 8) + ", " + (chunkPos.field_9180 * 16 + 8);
    }

    private void showToastNotification(Map<class_2248, String> foundBlocks) {
        Map.Entry<class_2248, String> firstEntry = foundBlocks.entrySet().iterator().next();
        class_1799 iconStack = new class_1799((class_1935)firstEntry.getKey());
        if (iconStack.method_7960()) {
            iconStack = new class_1799((class_1935)class_1802.field_8077);
        }
        this.mc.method_1566().method_1999((class_368)new MeteorToast.Builder("Block Found").text(firstEntry.getValue()).icon(iconStack.method_7909()).build());
    }

    private void sendWebhookNotification(class_1923 chunkPos, Map<class_2248, String> foundBlocks) {
        if (!this.webhookEnabled.get().booleanValue() || this.webhookUrl.get().trim().isEmpty()) {
            return;
        }
        CompletableFuture.runAsync(() -> {
            try {
                String serverInfo = this.mc.method_1558() != null ? this.mc.method_1558().field_3761 : "Singleplayer";
                String pingContent = this.selfPing.get() != false && !this.discordId.get().trim().isEmpty() ? String.format("<@%s>", this.discordId.get().trim()) : "";
                StringBuilder itemsBreakdown = new StringBuilder();
                for (String name : foundBlocks.values()) {
                    itemsBreakdown.append(String.format("\u2022 %s\\n", name));
                }
                String jsonPayload = String.format("{\"content\":\"%s\",\"username\":\"Block Notifier\",\"embeds\":[{\"title\":\"Block Notifier Alert\",\"description\":\"Detected at **%d, %d**\",\"color\":3447003,\"fields\":[{\"name\":\"Total Types\",\"value\":\"%d\",\"inline\":false},{\"name\":\"Breakdown\",\"value\":\"%s\",\"inline\":false},{\"name\":\"Server\",\"value\":\"%s\",\"inline\":true},{\"name\":\"Time\",\"value\":\"<t:%d:R>\",\"inline\":true}],\"footer\":{\"text\":\"Divine Client\"}}]}", pingContent, chunkPos.field_9181 * 16 + 8, chunkPos.field_9180 * 16 + 8, foundBlocks.size(), itemsBreakdown, serverInfo, System.currentTimeMillis() / 1000L);
                HttpRequest request = HttpRequest.newBuilder().uri(URI.create(this.webhookUrl.get().trim())).header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonPayload)).build();
                this.httpClient.send(request, HttpResponse.BodyHandlers.discarding());
            }
            catch (Exception exception) {
                // empty catch block
            }
        });
    }

    private void handleDisconnection(Map<class_2248, String> foundBlocks) {
        if (this.mc.method_1562() != null) {
            this.mc.method_1562().method_52781(new class_2661((class_2561)class_2561.method_43470((String)("Target found: " + foundBlocks.values().iterator().next()))));
            this.toggle();
        }
    }

    public static enum Mode {
        Chat,
        Toast,
        Both;

    }
}

