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.world.level.Level;
import java.util.List;
import net.minecraft.server.packs.resources.PreparableReloadListener;

public class ParticleEngine implements PreparableReloadListener {
    private static final List<ParticleRenderType> RENDER_ORDER;
    protected Level 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 Level bjw, final TextureManager eak) {
        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);
        eak.register(this.textureAtlas.location(), this.textureAtlas);
        this.level = bjw;
        this.textureManager = eak;
        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.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.Provider::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);
    }
    
    private <T extends ParticleOptions> void register(final ParticleType<T> gu, final ParticleProvider<T> dpc) {
        this.providers.put(Registry.PARTICLE_TYPE.getId(gu), dpc);
    }
    
    private <T extends ParticleOptions> void register(final ParticleType<T> gu, final SpriteParticleRegistration<T> b) {
        final MutableSpriteSet a4 = new MutableSpriteSet();
        this.spriteSets.put(Registry.PARTICLE_TYPE.getKey(gu), a4);
        this.providers.put(Registry.PARTICLE_TYPE.getId(gu), b.create(a4));
    }
    
    @Override
    public CompletableFuture<Void> reload(final PreparationBarrier a, final ResourceManager zb, final ProfilerFiller aio3, final ProfilerFiller aio4, final Executor executor5, final Executor executor6) {
        final Map<ResourceLocation, List<ResourceLocation>> map8 = Maps.newConcurrentMap();
        final CompletableFuture<?>[] arr9 = Registry.PARTICLE_TYPE.keySet().stream().map(sm -> CompletableFuture.runAsync(() -> this.loadParticleDescription(zb, sm, map8), executor5)).<CompletableFuture<?>>toArray(CompletableFuture[]::new);
        final Map<K, Object> map9;
        final TextureAtlas.Preparations a2;
        final TextureAtlasSprite eaj5;
        final Map map10;
        final TextureAtlasSprite element;
        final ImmutableList<TextureAtlasSprite> immutableList5;
        return CompletableFuture.allOf(arr9).thenApplyAsync(void4 -> {
            aio3.startTick();
            aio3.push("stitching");
            a2 = this.textureAtlas.prepareToStitch(zb, map9.values().stream().flatMap(Collection::stream), aio3, 0);
            aio3.pop();
            aio3.endTick();
            return a2;
        }, executor5).thenCompose(a::wait).thenAcceptAsync(a -> {
            this.particles.clear();
            aio4.startTick();
            aio4.push("upload");
            this.textureAtlas.reload(a);
            aio4.popPush("bindSpriteSets");
            eaj5 = this.textureAtlas.getSprite(MissingTextureAtlasSprite.getLocation());
            map10.forEach((sm, list) -> {
                immutableList5 = (ImmutableList<TextureAtlasSprite>)(list.isEmpty() ? ImmutableList.<TextureAtlasSprite>of(element) : (list.stream().map(this.textureAtlas::getSprite).collect(ImmutableList.toImmutableList())));
                this.spriteSets.get(sm).rebind(immutableList5);
                return;
            });
            aio4.pop();
            aio4.endTick();
        }, executor6);
    }
    
    public void close() {
        this.textureAtlas.clearTextureData();
    }
    
    private void loadParticleDescription(final ResourceManager zb, final ResourceLocation sm, final Map<ResourceLocation, List<ResourceLocation>> map) {
        final ResourceLocation sm2 = new ResourceLocation(sm.getNamespace(), "particles/" + sm.getPath() + ".json");
        try (final Resource za6 = zb.getResource(sm2);
             final Reader reader8 = new InputStreamReader(za6.getInputStream(), Charsets.UTF_8)) {
            final ParticleDescription dpa10 = ParticleDescription.fromJson(GsonHelper.parse(reader8));
            final List<ResourceLocation> list11 = dpa10.getTextures();
            final boolean boolean12 = this.spriteSets.containsKey(sm);
            if (list11 == null) {
                if (boolean12) {
                    throw new IllegalStateException("Missing texture list for particle " + sm);
                }
            }
            else {
                if (!boolean12) {
                    throw new IllegalStateException("Redundant texture list for particle " + sm);
                }
                final ResourceLocation resourceLocation;
                map.put(sm, list11.stream().map(sm -> {
                    new ResourceLocation(sm.getNamespace(), "particle/" + sm.getPath());
                    return resourceLocation;
                }).collect(Collectors.toList()));
            }
        }
        catch (IOException iOException6) {
            throw new IllegalStateException("Failed to load description for particle " + sm, iOException6);
        }
    }
    
    public void createTrackingEmitter(final Entity akq, final ParticleOptions gt) {
        this.trackingEmitters.add(new TrackingEmitter(this.level, akq, gt));
    }
    
    public void createTrackingEmitter(final Entity akq, final ParticleOptions gt, final int integer) {
        this.trackingEmitters.add(new TrackingEmitter(this.level, akq, gt, integer));
    }
    
    @Nullable
    public Particle createParticle(final ParticleOptions gt, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
        final Particle doz15 = this.<ParticleOptions>makeParticle(gt, double2, double3, double4, double5, double6, double7);
        if (doz15 != null) {
            this.add(doz15);
            return doz15;
        }
        return null;
    }
    
    @Nullable
    private <T extends ParticleOptions> Particle makeParticle(final T gt, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
        final ParticleProvider<T> dpc15 = (ParticleProvider<T>)this.providers.get(Registry.PARTICLE_TYPE.getId(gt.getType()));
        if (dpc15 == null) {
            return null;
        }
        return dpc15.createParticle(gt, this.level, double2, double3, double4, double5, double6, double7);
    }
    
    public void add(final Particle doz) {
        this.particlesToAdd.add(doz);
    }
    
    public void tick() {
        this.particles.forEach((dpd, queue) -> {
            this.level.getProfiler().push(dpd.toString());
            this.tickParticleList(queue);
            this.level.getProfiler().pop();
            return;
        });
        if (!this.trackingEmitters.isEmpty()) {
            final List<TrackingEmitter> list2 = Lists.newArrayList();
            for (final TrackingEmitter dpt4 : this.trackingEmitters) {
                dpt4.tick();
                if (!dpt4.isAlive()) {
                    list2.add(dpt4);
                }
            }
            this.trackingEmitters.removeAll(list2);
        }
        if (!this.particlesToAdd.isEmpty()) {
            Particle doz2;
            while ((doz2 = this.particlesToAdd.poll()) != null) {
                this.particles.computeIfAbsent(doz2.getRenderType(), dpd -> EvictingQueue.create(16384)).add(doz2);
            }
        }
    }
    
    private void tickParticleList(final Collection<Particle> collection) {
        if (!collection.isEmpty()) {
            final Iterator<Particle> iterator3 = collection.iterator();
            while (iterator3.hasNext()) {
                final Particle doz4 = iterator3.next();
                this.tickParticle(doz4);
                if (!doz4.isAlive()) {
                    iterator3.remove();
                }
            }
        }
    }
    
    private void tickParticle(final Particle doz) {
        try {
            doz.tick();
        }
        catch (Throwable throwable3) {
            final CrashReport h4 = CrashReport.forThrowable(throwable3, "Ticking Particle");
            final CrashReportCategory i5 = h4.addCategory("Particle being ticked");
            i5.setDetail("Particle", doz::toString);
            i5.setDetail("Particle Type", doz.getRenderType()::toString);
            throw new ReportedException(h4);
        }
    }
    
    public void render(final PoseStack cyl, final MultiBufferSource.BufferSource a, final LightTexture dqs, final Camera dbb, final float float5) {
        dqs.turnOnLightLayer();
        RenderSystem.enableAlphaTest();
        RenderSystem.defaultAlphaFunc();
        RenderSystem.enableDepthTest();
        RenderSystem.enableFog();
        RenderSystem.pushMatrix();
        RenderSystem.multMatrix(cyl.last().pose());
        for (final ParticleRenderType dpd8 : ParticleEngine.RENDER_ORDER) {
            final Iterable<Particle> iterable9 = this.particles.get(dpd8);
            if (iterable9 == null) {
                continue;
            }
            RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
            final Tesselator cym10 = Tesselator.getInstance();
            final BufferBuilder cyg11 = cym10.getBuilder();
            dpd8.begin(cyg11, this.textureManager);
            for (final Particle doz13 : iterable9) {
                try {
                    doz13.render(cyg11, dbb, float5);
                }
                catch (Throwable throwable14) {
                    final CrashReport h15 = CrashReport.forThrowable(throwable14, "Rendering Particle");
                    final CrashReportCategory i16 = h15.addCategory("Particle being rendered");
                    i16.setDetail("Particle", doz13::toString);
                    i16.setDetail("Particle Type", dpd8::toString);
                    throw new ReportedException(h15);
                }
            }
            dpd8.end(cym10);
        }
        RenderSystem.popMatrix();
        RenderSystem.depthMask(true);
        RenderSystem.disableBlend();
        RenderSystem.defaultAlphaFunc();
        dqs.turnOffLightLayer();
        RenderSystem.disableFog();
    }
    
    public void setLevel(@Nullable final Level bjw) {
        this.level = bjw;
        this.particles.clear();
        this.trackingEmitters.clear();
    }
    
    public void destroy(final BlockPos fk, final BlockState byj) {
        if (byj.isAir()) {
            return;
        }
        final VoxelShape cwf4 = byj.getShape(this.level, fk);
        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;
        cwf4.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, fk.getX() + double16, fk.getY() + double17, fk.getZ() + double18, double13 - 0.5, double14 - 0.5, double15 - 0.5, byj).init(fk));
                    }
                }
            }
        });
    }
    
    public void crack(final BlockPos fk, final Direction fp) {
        final BlockState byj4 = this.level.getBlockState(fk);
        if (byj4.getRenderShape() == RenderShape.INVISIBLE) {
            return;
        }
        final int integer5 = fk.getX();
        final int integer6 = fk.getY();
        final int integer7 = fk.getZ();
        final float float8 = 0.1f;
        final AABB cvf9 = byj4.getShape(this.level, fk).bounds();
        double double10 = integer5 + this.random.nextDouble() * (cvf9.maxX - cvf9.minX - 0.20000000298023224) + 0.10000000149011612 + cvf9.minX;
        double double11 = integer6 + this.random.nextDouble() * (cvf9.maxY - cvf9.minY - 0.20000000298023224) + 0.10000000149011612 + cvf9.minY;
        double double12 = integer7 + this.random.nextDouble() * (cvf9.maxZ - cvf9.minZ - 0.20000000298023224) + 0.10000000149011612 + cvf9.minZ;
        if (fp == Direction.DOWN) {
            double11 = integer6 + cvf9.minY - 0.10000000149011612;
        }
        if (fp == Direction.UP) {
            double11 = integer6 + cvf9.maxY + 0.10000000149011612;
        }
        if (fp == Direction.NORTH) {
            double12 = integer7 + cvf9.minZ - 0.10000000149011612;
        }
        if (fp == Direction.SOUTH) {
            double12 = integer7 + cvf9.maxZ + 0.10000000149011612;
        }
        if (fp == Direction.WEST) {
            double10 = integer5 + cvf9.minX - 0.10000000149011612;
        }
        if (fp == Direction.EAST) {
            double10 = integer5 + cvf9.maxX + 0.10000000149011612;
        }
        this.add(new TerrainParticle(this.level, double10, double11, double12, 0.0, 0.0, 0.0, byj4).init(fk).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 dpm);
    }
}
