/*
 * Decompiled with CFR 0.152.
 * 
 * Could not load the following classes:
 *  meteordevelopment.orbit.EventHandler
 *  net.minecraft.class_1923
 *  net.minecraft.class_1944
 *  net.minecraft.class_2338
 *  net.minecraft.class_2350
 *  net.minecraft.class_2818
 */
package meteordevelopment.meteorclient.systems.modules.donut;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import meteordevelopment.meteorclient.events.render.Render3DEvent;
import meteordevelopment.meteorclient.events.world.TickEvent;
import meteordevelopment.meteorclient.renderer.ShapeMode;
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.color.Color;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.class_1923;
import net.minecraft.class_1944;
import net.minecraft.class_2338;
import net.minecraft.class_2350;
import net.minecraft.class_2818;

public class LightEsp
extends Module {
    private static final int CHUNK_RADIUS = 10;
    private static final int MIN_LIGHT_LEVEL = 5;
    private static final long SCAN_COOLDOWN = 1000L;
    private static final long OPTIMIZATION_COOLDOWN = 1750L;
    private static final Set<class_2338> blocksToSkip = ConcurrentHashMap.newKeySet();
    private static long lastOptimizationTime = 0L;
    private final SettingGroup sgGeneral;
    private final Setting<Integer> red;
    private final Setting<Integer> green;
    private final Setting<Integer> blue;
    private final Setting<Integer> black;
    private final Setting<Integer> alpha;
    private final Setting<Integer> minY;
    private final Setting<Integer> maxY;
    private final ExecutorService executor;
    private final AtomicBoolean isScanning;
    private volatile Map<Integer, List<class_2338>> groupedLights;
    private volatile Set<class_2338> allLightPositions;
    private long lastScanTime;

    public LightEsp() {
        super(Categories.Donut, "Light esp", "Shows block light sources in low Y range.");
        this.sgGeneral = this.settings.getDefaultGroup();
        this.red = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("red")).defaultValue(255)).range(0, 255).sliderRange(0, 255).build());
        this.green = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("green")).defaultValue(255)).range(0, 255).sliderRange(0, 255).build());
        this.blue = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("blue")).defaultValue(0)).range(0, 255).sliderRange(0, 255).build());
        this.black = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("black")).defaultValue(0)).range(0, 255).sliderRange(0, 255).build());
        this.alpha = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("alpha")).defaultValue(100)).range(0, 255).sliderRange(0, 255).build());
        this.minY = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("min-y")).description("Minimum Y level to scan.")).defaultValue(-64)).range(-64, 320).sliderRange(-64, 320).build());
        this.maxY = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("max-y")).description("Maximum Y level to scan.")).defaultValue(-35)).range(-64, 320).sliderRange(-64, 320).build());
        this.executor = Executors.newSingleThreadExecutor();
        this.isScanning = new AtomicBoolean(false);
        this.groupedLights = new HashMap<Integer, List<class_2338>>();
        this.allLightPositions = new HashSet<class_2338>();
        this.lastScanTime = 0L;
    }

    @Override
    public void onActivate() {
        blocksToSkip.clear();
        this.groupedLights = new HashMap<Integer, List<class_2338>>();
        this.allLightPositions = new HashSet<class_2338>();
        this.lastScanTime = 0L;
        if (this.isScanning.compareAndSet(false, true)) {
            this.executor.submit(this::updateLights);
        }
    }

    @Override
    public void onDeactivate() {
        this.groupedLights = new HashMap<Integer, List<class_2338>>();
        this.allLightPositions = new HashSet<class_2338>();
    }

    @EventHandler
    private void onTick(TickEvent.Post event) {
        if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
            return;
        }
        long now = System.currentTimeMillis();
        if (now - this.lastScanTime > 1000L && this.isScanning.compareAndSet(false, true)) {
            this.lastScanTime = now;
            this.executor.submit(this::updateLights);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private void updateLights() {
        try {
            if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
                return;
            }
            long now = System.currentTimeMillis();
            if (now - lastOptimizationTime >= 1750L) {
                this.optimizeWeakLights();
                lastOptimizationTime = now;
            }
            ConcurrentHashMap<Integer, List<class_2338>> newGrouped = new ConcurrentHashMap<Integer, List<class_2338>>();
            ConcurrentHashMap.KeySetView<class_2338, Boolean> newAllPositions = ConcurrentHashMap.newKeySet();
            class_1923 playerChunk = this.mc.field_1724.method_31476();
            for (int chunkX = playerChunk.field_9181 - 10; chunkX <= playerChunk.field_9181 + 10; ++chunkX) {
                for (int chunkZ = playerChunk.field_9180 - 10; chunkZ <= playerChunk.field_9180 + 10; ++chunkZ) {
                    class_2818 chunk;
                    if (!this.mc.field_1687.method_2935().method_12123(chunkX, chunkZ) || (chunk = this.mc.field_1687.method_8497(chunkX, chunkZ)) == null) continue;
                    int startX = chunk.method_12004().method_8326();
                    int startZ = chunk.method_12004().method_8328();
                    for (int x = 0; x < 16; ++x) {
                        for (int z = 0; z < 16; ++z) {
                            if (this.mc.field_1687 == null) {
                                return;
                            }
                            int worldX = startX + x;
                            int worldZ = startZ + z;
                            for (int y = this.minY.get().intValue(); y <= this.maxY.get(); ++y) {
                                int skyLight;
                                int blockLight;
                                class_2338 pos = new class_2338(worldX, y, worldZ);
                                if (blocksToSkip.contains(pos) || (blockLight = this.mc.field_1687.method_8314(class_1944.field_9282, pos)) < 5 || blockLight <= (skyLight = this.mc.field_1687.method_8314(class_1944.field_9284, pos))) continue;
                                newAllPositions.add(pos);
                                newGrouped.computeIfAbsent(blockLight, k -> Collections.synchronizedList(new ArrayList())).add(pos);
                            }
                        }
                    }
                }
            }
            this.groupedLights = newGrouped;
            this.allLightPositions = newAllPositions;
        }
        catch (Exception exception) {
        }
        finally {
            this.isScanning.set(false);
        }
    }

    @EventHandler
    private void onRender3D(Render3DEvent event) {
        if (this.mc.field_1724 == null || this.mc.field_1687 == null) {
            return;
        }
        if (this.groupedLights.isEmpty()) {
            return;
        }
        float baseR = (float)this.red.get().intValue() / 255.0f;
        float baseG = (float)this.green.get().intValue() / 255.0f;
        float baseB = (float)this.blue.get().intValue() / 255.0f;
        float baseK = (float)this.black.get().intValue() / 255.0f;
        float baseA = (float)this.alpha.get().intValue() / 255.0f;
        for (Map.Entry<Integer, List<class_2338>> entry : this.groupedLights.entrySet()) {
            int lightLevel = entry.getKey();
            float intensity = (float)lightLevel / 15.0f;
            int r = Math.max(0, Math.min(255, (int)(255.0f * baseR * intensity * (1.0f - baseK))));
            int g = Math.max(0, Math.min(255, (int)(255.0f * baseG * intensity * (1.0f - baseK))));
            int b = Math.max(0, Math.min(255, (int)(255.0f * baseB * intensity * (1.0f - baseK))));
            int a = Math.max(0, Math.min(255, (int)(255.0f * baseA * (0.3f + 0.7f * intensity))));
            Color side = new Color(r, g, b, a);
            Color line = new Color(r, g, b, 255);
            for (class_2338 pos : entry.getValue()) {
                if (lightLevel < 15 && !LightEsp.shouldRenderBlock(pos, this.allLightPositions)) continue;
                event.renderer.box(pos, side, line, ShapeMode.Both, 0);
            }
        }
    }

    private static boolean shouldRenderBlock(class_2338 pos, Set<class_2338> allLightPositions) {
        for (class_2350 dir : class_2350.values()) {
            if (allLightPositions.contains(pos.method_10093(dir))) continue;
            return true;
        }
        return false;
    }

    private void optimizeWeakLights() {
        if (this.mc.field_1687 == null || this.mc.field_1724 == null) {
            return;
        }
        this.clearOldCacheEntries();
        class_1923 playerChunk = this.mc.field_1724.method_31476();
        HashSet<class_2338> weakSources = new HashSet<class_2338>();
        HashSet<class_2338> visited = new HashSet<class_2338>();
        for (int chunkX = playerChunk.field_9181 - 10; chunkX <= playerChunk.field_9181 + 10; ++chunkX) {
            for (int chunkZ = playerChunk.field_9180 - 10; chunkZ <= playerChunk.field_9180 + 10; ++chunkZ) {
                class_2818 chunk;
                if (!this.mc.field_1687.method_2935().method_12123(chunkX, chunkZ) || (chunk = this.mc.field_1687.method_8497(chunkX, chunkZ)) == null) continue;
                int startX = chunkX * 16;
                int startZ = chunkZ * 16;
                for (int x = 0; x < 16; ++x) {
                    for (int z = 0; z < 16; ++z) {
                        for (int y = this.minY.get().intValue(); y <= this.maxY.get(); ++y) {
                            class_2338 pos = new class_2338(startX + x, y, startZ + z);
                            int blockLight = this.mc.field_1687.method_8314(class_1944.field_9282, pos);
                            int skyLight = this.mc.field_1687.method_8314(class_1944.field_9284, pos);
                            if (blockLight != 6 && blockLight != 7 || blockLight <= skyLight) continue;
                            weakSources.add(pos);
                        }
                    }
                }
            }
        }
        for (class_2338 weakSource : weakSources) {
            int sourceLevel;
            int requiredLevel;
            if (visited.contains(weakSource) || this.hasHigherLevelLightIn3x3Area(weakSource, requiredLevel = (sourceLevel = this.mc.field_1687.method_8314(class_1944.field_9282, weakSource)) + 1)) continue;
            this.propagateDeletionWithLevelFilter(weakSource, visited, sourceLevel);
        }
    }

    private boolean hasHigherLevelLightIn3x3Area(class_2338 centerPos, int requiredLevel) {
        for (int x = centerPos.method_10263() - 1; x <= centerPos.method_10263() + 1; ++x) {
            for (int y = Math.max(this.minY.get(), centerPos.method_10264() - 1); y <= Math.min(this.maxY.get(), centerPos.method_10264() + 1); ++y) {
                for (int z = centerPos.method_10260() - 1; z <= centerPos.method_10260() + 1; ++z) {
                    class_2338 checkPos;
                    if (x == centerPos.method_10263() && y == centerPos.method_10264() && z == centerPos.method_10260() || !this.isWithinScanArea(checkPos = new class_2338(x, y, z))) continue;
                    int blockLight = this.mc.field_1687.method_8314(class_1944.field_9282, checkPos);
                    int skyLight = this.mc.field_1687.method_8314(class_1944.field_9284, checkPos);
                    if (blockLight < requiredLevel || blockLight <= skyLight) continue;
                    return true;
                }
            }
        }
        return false;
    }

    private void propagateDeletionWithLevelFilter(class_2338 startPos, Set<class_2338> visited, int maxLevel) {
        LinkedList<class_2338> queue = new LinkedList<class_2338>();
        queue.add(startPos);
        visited.add(startPos);
        while (!queue.isEmpty()) {
            class_2338 current = (class_2338)queue.poll();
            blocksToSkip.add(current);
            for (class_2350 dir : class_2350.values()) {
                class_2338 neighbor = current.method_10093(dir);
                if (!this.isWithinScanArea(neighbor) || visited.contains(neighbor) || blocksToSkip.contains(neighbor)) continue;
                int neighborLight = this.mc.field_1687.method_8314(class_1944.field_9282, neighbor);
                int neighborSky = this.mc.field_1687.method_8314(class_1944.field_9284, neighbor);
                if (neighborLight < 5 || neighborLight <= neighborSky || neighborLight > maxLevel) continue;
                visited.add(neighbor);
                queue.add(neighbor);
            }
        }
    }

    private void clearOldCacheEntries() {
        if (this.mc.field_1724 == null) {
            blocksToSkip.clear();
            return;
        }
        class_1923 playerChunk = this.mc.field_1724.method_31476();
        int limit = 11;
        blocksToSkip.removeIf(pos -> {
            class_1923 posChunk = new class_1923(pos);
            return Math.abs(posChunk.field_9181 - playerChunk.field_9181) > limit || Math.abs(posChunk.field_9180 - playerChunk.field_9180) > limit;
        });
    }

    private boolean isWithinScanArea(class_2338 pos) {
        if (this.mc.field_1724 == null) {
            return false;
        }
        class_1923 playerChunk = this.mc.field_1724.method_31476();
        int minX = (playerChunk.field_9181 - 10) * 16;
        int maxX = (playerChunk.field_9181 + 10 + 1) * 16;
        int minZ = (playerChunk.field_9180 - 10) * 16;
        int maxZ = (playerChunk.field_9180 + 10 + 1) * 16;
        return pos.method_10263() >= minX && pos.method_10263() < maxX && pos.method_10260() >= minZ && pos.method_10260() < maxZ && pos.method_10264() >= this.minY.get() && pos.method_10264() <= this.maxY.get();
    }
}

