package net.minecraft.client.particle;

import net.minecraft.world.phys.AABB;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.util.Mth;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.CrashReport;
import java.util.Iterator;
import com.google.common.collect.EvictingQueue;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
import java.io.Reader;
import net.minecraft.server.packs.resources.Resource;
import java.io.IOException;
import java.util.stream.Collectors;
import net.minecraft.util.GsonHelper;
import java.io.InputStreamReader;
import com.google.common.base.Charsets;
import java.util.stream.Collector;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
import java.util.concurrent.CompletionStage;
import java.util.stream.Stream;
import java.util.function.Function;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.core.Registry;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleType;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.client.renderer.texture.AbstractTexture;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import com.google.common.collect.Queues;
import com.google.common.collect.Maps;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.resources.ResourceLocation;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.Random;
import net.minecraft.client.renderer.texture.TextureManager;
import java.util.Queue;
import java.util.Map;
import net.minecraft.client.multiplayer.ClientLevel;
import java.util.List;
import net.minecraft.server.packs.resources.PreparableReloadListener;

public class ParticleEngine implements PreparableReloadListener {
    private static final List<ParticleRenderType> RENDER_ORDER;
    protected ClientLevel level;
    private final Map<ParticleRenderType, Queue<Particle>> particles;
    private final Queue<TrackingEmitter> trackingEmitters;
    private final TextureManager textureManager;
    private final Random random;
    private final Int2ObjectMap<ParticleProvider<?>> providers;
    private final Queue<Particle> particlesToAdd;
    private final Map<ResourceLocation, MutableSpriteSet> spriteSets;
    private final TextureAtlas textureAtlas;
    
    public ParticleEngine(final ClientLevel dym, final TextureManager elw) {
        this.particles = Maps.newIdentityHashMap();
        this.trackingEmitters = Queues.newArrayDeque();
        this.random = new Random();
        this.providers = (Int2ObjectMap<ParticleProvider<?>>)new Int2ObjectOpenHashMap();
        this.particlesToAdd = Queues.newArrayDeque();
        this.spriteSets = Maps.newHashMap();
        this.textureAtlas = new TextureAtlas(TextureAtlas.LOCATION_PARTICLES);
        elw.register(this.textureAtlas.location(), this.textureAtlas);
        this.level = dym;
        this.textureManager = elw;
        this.registerProviders();
    }
    
    private void registerProviders() {
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.AMBIENT_ENTITY_EFFECT, SpellParticle.AmbientMobProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ANGRY_VILLAGER, HeartParticle.AngryVillagerProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.BARRIER, (ParticleProvider<ParticleOptions>)new BarrierParticle.Provider());
        this.<BlockParticleOption>register(ParticleTypes.BLOCK, new TerrainParticle.Provider());
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.BUBBLE, BubbleParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.BUBBLE_COLUMN_UP, BubbleColumnUpParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.BUBBLE_POP, BubblePopParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.CAMPFIRE_COSY_SMOKE, CampfireSmokeParticle.CosyProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.CAMPFIRE_SIGNAL_SMOKE, CampfireSmokeParticle.SignalProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.CLOUD, PlayerCloudParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.COMPOSTER, SuspendedTownParticle.ComposterFillProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.CRIT, CritParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.CURRENT_DOWN, WaterCurrentDownParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.DAMAGE_INDICATOR, CritParticle.DamageIndicatorProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.DRAGON_BREATH, DragonBreathParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.DOLPHIN, SuspendedTownParticle.DolphinSpeedProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.DRIPPING_LAVA, DripParticle.LavaHangProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FALLING_LAVA, DripParticle.LavaFallProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.LANDING_LAVA, DripParticle.LavaLandProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.DRIPPING_WATER, DripParticle.WaterHangProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FALLING_WATER, DripParticle.WaterFallProvider::new);
        this.<DustParticleOptions>register(ParticleTypes.DUST, DustParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.EFFECT, SpellParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ELDER_GUARDIAN, (ParticleProvider<ParticleOptions>)new MobAppearanceParticle.Provider());
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ENCHANTED_HIT, CritParticle.MagicProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ENCHANT, EnchantmentTableParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.END_ROD, EndRodParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ENTITY_EFFECT, SpellParticle.MobProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.EXPLOSION_EMITTER, (ParticleProvider<ParticleOptions>)new HugeExplosionSeedParticle.Provider());
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.EXPLOSION, HugeExplosionParticle.Provider::new);
        this.<BlockParticleOption>register(ParticleTypes.FALLING_DUST, FallingDustParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FIREWORK, FireworkParticles.SparkProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FISHING, WakeParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FLAME, FlameParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SOUL, SoulParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SOUL_FIRE_FLAME, FlameParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FLASH, FireworkParticles.FlashProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.HAPPY_VILLAGER, SuspendedTownParticle.HappyVillagerProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.HEART, HeartParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.INSTANT_EFFECT, SpellParticle.InstantProvider::new);
        this.<ItemParticleOption>register(ParticleTypes.ITEM, new BreakingItemParticle.Provider());
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ITEM_SLIME, (ParticleProvider<ParticleOptions>)new BreakingItemParticle.SlimeProvider());
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ITEM_SNOWBALL, (ParticleProvider<ParticleOptions>)new BreakingItemParticle.SnowballProvider());
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.LARGE_SMOKE, LargeSmokeParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.LAVA, LavaParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.MYCELIUM, SuspendedTownParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.NAUTILUS, EnchantmentTableParticle.NautilusProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.NOTE, NoteParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.POOF, ExplodeParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.PORTAL, PortalParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.RAIN, WaterDropParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SMOKE, SmokeParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SNEEZE, PlayerCloudParticle.SneezeProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SPIT, SpitParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SWEEP_ATTACK, AttackSweepParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.TOTEM_OF_UNDYING, TotemParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SQUID_INK, SquidInkParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.UNDERWATER, SuspendedParticle.UnderwaterProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.SPLASH, SplashParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.WITCH, SpellParticle.WitchProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.DRIPPING_HONEY, DripParticle.HoneyHangProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FALLING_HONEY, DripParticle.HoneyFallProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.LANDING_HONEY, DripParticle.HoneyLandProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FALLING_NECTAR, DripParticle.NectarFallProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.ASH, AshParticle.Provider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.CRIMSON_SPORE, SuspendedParticle.CrimsonSporeProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.WARPED_SPORE, SuspendedParticle.WarpedSporeProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.DRIPPING_OBSIDIAN_TEAR, DripParticle.ObsidianTearHangProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.FALLING_OBSIDIAN_TEAR, DripParticle.ObsidianTearFallProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.LANDING_OBSIDIAN_TEAR, DripParticle.ObsidianTearLandProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.REVERSE_PORTAL, ReversePortalParticle.ReversePortalProvider::new);
        this.<ParticleOptions>register((ParticleType<ParticleOptions>)ParticleTypes.WHITE_ASH, WhiteAshParticle.Provider::new);
    }
    
    private <T extends ParticleOptions> void register(final ParticleType<T> hg, final ParticleProvider<T> eac) {
        this.providers.put(Registry.PARTICLE_TYPE.getId(hg), eac);
    }
    
    private <T extends ParticleOptions> void register(final ParticleType<T> hg, final SpriteParticleRegistration<T> b) {
        final MutableSpriteSet a4 = new MutableSpriteSet();
        this.spriteSets.put(Registry.PARTICLE_TYPE.getKey(hg), a4);
        this.providers.put(Registry.PARTICLE_TYPE.getId(hg), b.create(a4));
    }
    
    @Override
    public CompletableFuture<Void> reload(final PreparationBarrier a, final ResourceManager abc, final ProfilerFiller ami3, final ProfilerFiller ami4, final Executor executor5, final Executor executor6) {
        final Map<ResourceLocation, List<ResourceLocation>> map8 = Maps.newConcurrentMap();
        final CompletableFuture<?>[] arr9 = Registry.PARTICLE_TYPE.keySet().stream().map(uh -> CompletableFuture.runAsync(() -> this.loadParticleDescription(abc, uh, map8), executor5)).<CompletableFuture<?>>toArray(CompletableFuture[]::new);
        final Map<K, Object> map9;
        final TextureAtlas.Preparations a2;
        final TextureAtlasSprite elv5;
        final Map map10;
        final TextureAtlasSprite element;
        final ImmutableList<TextureAtlasSprite> immutableList5;
        return CompletableFuture.allOf(arr9).thenApplyAsync(void4 -> {
            ami3.startTick();
            ami3.push("stitching");
            a2 = this.textureAtlas.prepareToStitch(abc, map9.values().stream().flatMap(Collection::stream), ami3, 0);
            ami3.pop();
            ami3.endTick();
            return a2;
        }, executor5).thenCompose(a::wait).thenAcceptAsync(a -> {
            this.particles.clear();
            ami4.startTick();
            ami4.push("upload");
            this.textureAtlas.reload(a);
            ami4.popPush("bindSpriteSets");
            elv5 = this.textureAtlas.getSprite(MissingTextureAtlasSprite.getLocation());
            map10.forEach((uh, list) -> {
                immutableList5 = (ImmutableList<TextureAtlasSprite>)(list.isEmpty() ? ImmutableList.<TextureAtlasSprite>of(element) : (list.stream().map(this.textureAtlas::getSprite).collect(ImmutableList.toImmutableList())));
                this.spriteSets.get(uh).rebind(immutableList5);
                return;
            });
            ami4.pop();
            ami4.endTick();
        }, executor6);
    }
    
    public void close() {
        this.textureAtlas.clearTextureData();
    }
    
    private void loadParticleDescription(final ResourceManager abc, final ResourceLocation uh, final Map<ResourceLocation, List<ResourceLocation>> map) {
        final ResourceLocation uh2 = new ResourceLocation(uh.getNamespace(), "particles/" + uh.getPath() + ".json");
        try (final Resource abb6 = abc.getResource(uh2);
             final Reader reader8 = new InputStreamReader(abb6.getInputStream(), Charsets.UTF_8)) {
            final ParticleDescription eaa10 = ParticleDescription.fromJson(GsonHelper.parse(reader8));
            final List<ResourceLocation> list11 = eaa10.getTextures();
            final boolean boolean12 = this.spriteSets.containsKey(uh);
            if (list11 == null) {
                if (boolean12) {
                    throw new IllegalStateException("Missing texture list for particle " + uh);
                }
            }
            else {
                if (!boolean12) {
                    throw new IllegalStateException("Redundant texture list for particle " + uh);
                }
                final ResourceLocation resourceLocation;
                map.put(uh, list11.stream().map(uh -> {
                    new ResourceLocation(uh.getNamespace(), "particle/" + uh.getPath());
                    return resourceLocation;
                }).collect(Collectors.toList()));
            }
        }
        catch (IOException iOException6) {
            throw new IllegalStateException("Failed to load description for particle " + uh, iOException6);
        }
    }
    
    public void createTrackingEmitter(final Entity aom, final ParticleOptions hf) {
        this.trackingEmitters.add(new TrackingEmitter(this.level, aom, hf));
    }
    
    public void createTrackingEmitter(final Entity aom, final ParticleOptions hf, final int integer) {
        this.trackingEmitters.add(new TrackingEmitter(this.level, aom, hf, integer));
    }
    
    @Nullable
    public Particle createParticle(final ParticleOptions hf, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
        final Particle dzz15 = this.<ParticleOptions>makeParticle(hf, double2, double3, double4, double5, double6, double7);
        if (dzz15 != null) {
            this.add(dzz15);
            return dzz15;
        }
        return null;
    }
    
    @Nullable
    private <T extends ParticleOptions> Particle makeParticle(final T hf, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
        final ParticleProvider<T> eac15 = (ParticleProvider<T>)this.providers.get(Registry.PARTICLE_TYPE.getId(hf.getType()));
        if (eac15 == null) {
            return null;
        }
        return eac15.createParticle(hf, this.level, double2, double3, double4, double5, double6, double7);
    }
    
    public void add(final Particle dzz) {
        this.particlesToAdd.add(dzz);
    }
    
    public void tick() {
        this.particles.forEach((ead, queue) -> {
            this.level.getProfiler().push(ead.toString());
            this.tickParticleList(queue);
            this.level.getProfiler().pop();
            return;
        });
        if (!this.trackingEmitters.isEmpty()) {
            final List<TrackingEmitter> list2 = Lists.newArrayList();
            for (final TrackingEmitter eaw4 : this.trackingEmitters) {
                eaw4.tick();
                if (!eaw4.isAlive()) {
                    list2.add(eaw4);
                }
            }
            this.trackingEmitters.removeAll(list2);
        }
        if (!this.particlesToAdd.isEmpty()) {
            Particle dzz2;
            while ((dzz2 = this.particlesToAdd.poll()) != null) {
                this.particles.computeIfAbsent(dzz2.getRenderType(), ead -> EvictingQueue.create(16384)).add(dzz2);
            }
        }
    }
    
    private void tickParticleList(final Collection<Particle> collection) {
        if (!collection.isEmpty()) {
            final Iterator<Particle> iterator3 = collection.iterator();
            while (iterator3.hasNext()) {
                final Particle dzz4 = iterator3.next();
                this.tickParticle(dzz4);
                if (!dzz4.isAlive()) {
                    iterator3.remove();
                }
            }
        }
    }
    
    private void tickParticle(final Particle dzz) {
        try {
            dzz.tick();
        }
        catch (Throwable throwable3) {
            final CrashReport j4 = CrashReport.forThrowable(throwable3, "Ticking Particle");
            final CrashReportCategory k5 = j4.addCategory("Particle being ticked");
            k5.setDetail("Particle", dzz::toString);
            k5.setDetail("Particle Type", dzz.getRenderType()::toString);
            throw new ReportedException(j4);
        }
    }
    
    public void render(final PoseStack dhl, final MultiBufferSource.BufferSource a, final LightTexture eby, final Camera dlj, final float float5) {
        eby.turnOnLightLayer();
        RenderSystem.enableAlphaTest();
        RenderSystem.defaultAlphaFunc();
        RenderSystem.enableDepthTest();
        RenderSystem.enableFog();
        RenderSystem.pushMatrix();
        RenderSystem.multMatrix(dhl.last().pose());
        for (final ParticleRenderType ead8 : ParticleEngine.RENDER_ORDER) {
            final Iterable<Particle> iterable9 = this.particles.get(ead8);
            if (iterable9 == null) {
                continue;
            }
            RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
            final Tesselator dhn10 = Tesselator.getInstance();
            final BufferBuilder dhg11 = dhn10.getBuilder();
            ead8.begin(dhg11, this.textureManager);
            for (final Particle dzz13 : iterable9) {
                try {
                    dzz13.render(dhg11, dlj, float5);
                }
                catch (Throwable throwable14) {
                    final CrashReport j15 = CrashReport.forThrowable(throwable14, "Rendering Particle");
                    final CrashReportCategory k16 = j15.addCategory("Particle being rendered");
                    k16.setDetail("Particle", dzz13::toString);
                    k16.setDetail("Particle Type", ead8::toString);
                    throw new ReportedException(j15);
                }
            }
            ead8.end(dhn10);
        }
        RenderSystem.popMatrix();
        RenderSystem.depthMask(true);
        RenderSystem.depthFunc(515);
        RenderSystem.disableBlend();
        RenderSystem.defaultAlphaFunc();
        eby.turnOffLightLayer();
        RenderSystem.disableFog();
    }
    
    public void setLevel(@Nullable final ClientLevel dym) {
        this.level = dym;
        this.particles.clear();
        this.trackingEmitters.clear();
    }
    
    public void destroy(final BlockPos fu, final BlockState cfj) {
        if (cfj.isAir()) {
            return;
        }
        final VoxelShape dfg4 = cfj.getShape(this.level, fu);
        final double double9 = 0.25;
        final double double10;
        final double double11;
        final double double12;
        final int integer22;
        final int integer23;
        final int integer24;
        int integer25;
        int integer26;
        int integer27;
        double double13;
        double double14;
        double double15;
        double double16;
        double double17;
        double double18;
        dfg4.forAllBoxes((double3, double4, double5, double6, double7, double8) -> {
            double10 = Math.min(1.0, double6 - double3);
            double11 = Math.min(1.0, double7 - double4);
            double12 = Math.min(1.0, double8 - double5);
            integer22 = Math.max(2, Mth.ceil(double10 / 0.25));
            integer23 = Math.max(2, Mth.ceil(double11 / 0.25));
            integer24 = Math.max(2, Mth.ceil(double12 / 0.25));
            for (integer25 = 0; integer25 < integer22; ++integer25) {
                for (integer26 = 0; integer26 < integer23; ++integer26) {
                    for (integer27 = 0; integer27 < integer24; ++integer27) {
                        double13 = (integer25 + 0.5) / integer22;
                        double14 = (integer26 + 0.5) / integer23;
                        double15 = (integer27 + 0.5) / integer24;
                        double16 = double13 * double10 + double3;
                        double17 = double14 * double11 + double4;
                        double18 = double15 * double12 + double5;
                        this.add(new TerrainParticle(this.level, fu.getX() + double16, fu.getY() + double17, fu.getZ() + double18, double13 - 0.5, double14 - 0.5, double15 - 0.5, cfj).init(fu));
                    }
                }
            }
        });
    }
    
    public void crack(final BlockPos fu, final Direction fz) {
        final BlockState cfj4 = this.level.getBlockState(fu);
        if (cfj4.getRenderShape() == RenderShape.INVISIBLE) {
            return;
        }
        final int integer5 = fu.getX();
        final int integer6 = fu.getY();
        final int integer7 = fu.getZ();
        final float float8 = 0.1f;
        final AABB deg9 = cfj4.getShape(this.level, fu).bounds();
        double double10 = integer5 + this.random.nextDouble() * (deg9.maxX - deg9.minX - 0.20000000298023224) + 0.10000000149011612 + deg9.minX;
        double double11 = integer6 + this.random.nextDouble() * (deg9.maxY - deg9.minY - 0.20000000298023224) + 0.10000000149011612 + deg9.minY;
        double double12 = integer7 + this.random.nextDouble() * (deg9.maxZ - deg9.minZ - 0.20000000298023224) + 0.10000000149011612 + deg9.minZ;
        if (fz == Direction.DOWN) {
            double11 = integer6 + deg9.minY - 0.10000000149011612;
        }
        if (fz == Direction.UP) {
            double11 = integer6 + deg9.maxY + 0.10000000149011612;
        }
        if (fz == Direction.NORTH) {
            double12 = integer7 + deg9.minZ - 0.10000000149011612;
        }
        if (fz == Direction.SOUTH) {
            double12 = integer7 + deg9.maxZ + 0.10000000149011612;
        }
        if (fz == Direction.WEST) {
            double10 = integer5 + deg9.minX - 0.10000000149011612;
        }
        if (fz == Direction.EAST) {
            double10 = integer5 + deg9.maxX + 0.10000000149011612;
        }
        this.add(new TerrainParticle(this.level, double10, double11, double12, 0.0, 0.0, 0.0, cfj4).init(fu).setPower(0.2f).scale(0.6f));
    }
    
    public String countParticles() {
        return String.valueOf(this.particles.values().stream().mapToInt(Collection::size).sum());
    }
    
    static {
        RENDER_ORDER = ImmutableList.<ParticleRenderType>of(ParticleRenderType.TERRAIN_SHEET, ParticleRenderType.PARTICLE_SHEET_OPAQUE, ParticleRenderType.PARTICLE_SHEET_LIT, ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT, ParticleRenderType.CUSTOM);
    }
    
    class MutableSpriteSet implements SpriteSet {
        private List<TextureAtlasSprite> sprites;
        
        private MutableSpriteSet() {
        }
        
        @Override
        public TextureAtlasSprite get(final int integer1, final int integer2) {
            return this.sprites.get(integer1 * (this.sprites.size() - 1) / integer2);
        }
        
        @Override
        public TextureAtlasSprite get(final Random random) {
            return this.sprites.get(random.nextInt(this.sprites.size()));
        }
        
        public void rebind(final List<TextureAtlasSprite> list) {
            this.sprites = ImmutableList.copyOf(list);
        }
    }
    
    @FunctionalInterface
    interface SpriteParticleRegistration<T extends ParticleOptions> {
        ParticleProvider<T> create(final SpriteSet eap);
    }
}
