package net.minecraft.client.renderer.texture;

import net.minecraft.world.inventory.InventoryMenu;
import org.apache.logging.log4j.LogManager;
import javax.annotation.Nullable;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.datafixers.util.Pair;
import net.minecraft.server.packs.resources.Resource;
import java.util.concurrent.CompletableFuture;
import net.minecraft.Util;
import net.minecraft.server.packs.metadata.MetadataSectionSerializer;
import net.minecraft.client.resources.metadata.animation.AnimationMetadataSection;
import com.mojang.blaze3d.platform.PngInfo;
import java.util.concurrent.ConcurrentLinkedQueue;
import net.minecraft.util.Mth;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import net.minecraft.util.profiling.ProfilerFiller;
import java.util.stream.Stream;
import net.minecraft.CrashReportCategory;
import java.util.Iterator;
import net.minecraft.ReportedException;
import net.minecraft.CrashReport;
import com.mojang.blaze3d.platform.TextureUtil;
import java.util.Collection;
import java.io.IOException;
import net.minecraft.server.packs.resources.ResourceManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Lists;
import java.util.Map;
import java.util.Set;
import java.util.List;
import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.Logger;

public class TextureAtlas extends AbstractTexture implements Tickable {
    private static final Logger LOGGER;
    @Deprecated
    public static final ResourceLocation LOCATION_BLOCKS;
    @Deprecated
    public static final ResourceLocation LOCATION_PARTICLES;
    private final List<TextureAtlasSprite> animatedTextures;
    private final Set<ResourceLocation> sprites;
    private final Map<ResourceLocation, TextureAtlasSprite> texturesByName;
    private final ResourceLocation location;
    private final int maxSupportedTextureSize;
    
    public TextureAtlas(final ResourceLocation sm) {
        this.animatedTextures = Lists.newArrayList();
        this.sprites = Sets.newHashSet();
        this.texturesByName = Maps.newHashMap();
        this.location = sm;
        this.maxSupportedTextureSize = RenderSystem.maxSupportedTextureSize();
    }
    
    @Override
    public void load(final ResourceManager zb) throws IOException {
    }
    
    public void reload(final Preparations a) {
        this.sprites.clear();
        this.sprites.addAll(a.sprites);
        TextureAtlas.LOGGER.info("Created: {}x{}x{} {}-atlas", a.width, a.height, a.mipLevel, this.location);
        TextureUtil.prepareImage(this.getId(), a.mipLevel, a.width, a.height);
        this.clearTextureData();
        for (final TextureAtlasSprite eaj4 : a.regions) {
            this.texturesByName.put(eaj4.getName(), eaj4);
            try {
                eaj4.uploadFirstFrame();
            }
            catch (Throwable throwable5) {
                final CrashReport h6 = CrashReport.forThrowable(throwable5, "Stitching texture atlas");
                final CrashReportCategory i7 = h6.addCategory("Texture being stitched together");
                i7.setDetail("Atlas path", this.location);
                i7.setDetail("Sprite", eaj4);
                throw new ReportedException(h6);
            }
            if (eaj4.isAnimation()) {
                this.animatedTextures.add(eaj4);
            }
        }
    }
    
    public Preparations prepareToStitch(final ResourceManager zb, final Stream<ResourceLocation> stream, final ProfilerFiller aio, final int integer) {
        aio.push("preparing");
        final Set<ResourceLocation> set6 = stream.peek(sm -> {
            if (sm == null) {
                throw new IllegalArgumentException("Location cannot be null!");
            }
            else {
                return;
            }
        }).collect(Collectors.toSet());
        final int integer2 = this.maxSupportedTextureSize;
        final Stitcher eag8 = new Stitcher(integer2, integer2, integer);
        int integer3 = Integer.MAX_VALUE;
        int integer4 = 1 << integer;
        aio.popPush("extracting_frames");
        for (final TextureAtlasSprite.Info a2 : this.getBasicSpriteInfos(zb, set6)) {
            integer3 = Math.min(integer3, Math.min(a2.width(), a2.height()));
            final int integer5 = Math.min(Integer.lowestOneBit(a2.width()), Integer.lowestOneBit(a2.height()));
            if (integer5 < integer4) {
                TextureAtlas.LOGGER.warn("Texture {} with size {}x{} limits mip level from {} to {}", a2.name(), a2.width(), a2.height(), Mth.log2(integer4), Mth.log2(integer5));
                integer4 = integer5;
            }
            eag8.registerSprite(a2);
        }
        final int integer6 = Math.min(integer3, integer4);
        final int integer7 = Mth.log2(integer6);
        int integer5;
        if (integer7 < integer) {
            TextureAtlas.LOGGER.warn("{}: dropping miplevel from {} to {}, because of minimum power of two: {}", this.location, integer, integer7, integer6);
            integer5 = integer7;
        }
        else {
            integer5 = integer;
        }
        aio.popPush("register");
        eag8.registerSprite(MissingTextureAtlasSprite.info());
        aio.popPush("stitching");
        try {
            eag8.stitch();
        }
        catch (StitcherException eah14) {
            final CrashReport h15 = CrashReport.forThrowable(eah14, "Stitching");
            final CrashReportCategory i16 = h15.addCategory("Stitcher");
            i16.setDetail("Sprites", eah14.getAllSprites().stream().map(a -> String.format("%s[%dx%d]", a.name(), a.width(), a.height())).collect(Collectors.joining(",")));
            i16.setDetail("Max Texture Size", integer2);
            throw new ReportedException(h15);
        }
        aio.popPush("loading");
        final List<TextureAtlasSprite> list14 = this.getLoadedSprites(zb, eag8, integer5);
        aio.pop();
        return new Preparations(set6, eag8.getWidth(), eag8.getHeight(), integer5, list14);
    }
    
    private Collection<TextureAtlasSprite.Info> getBasicSpriteInfos(final ResourceManager zb, final Set<ResourceLocation> set) {
        final List<CompletableFuture<?>> list4 = Lists.newArrayList();
        final ConcurrentLinkedQueue<TextureAtlasSprite.Info> concurrentLinkedQueue5 = new ConcurrentLinkedQueue<TextureAtlasSprite.Info>();
        for (final ResourceLocation sm7 : set) {
            if (MissingTextureAtlasSprite.getLocation().equals(sm7)) {
                continue;
            }
            final ResourceLocation resourceLocation;
            final ResourceLocation sm8;
            Resource za7;
            PngInfo cxs9;
            AnimationMetadataSection ebj10;
            Pair<Integer, Integer> pair11;
            TextureAtlasSprite.Info a6;
            final Throwable t2;
            final ConcurrentLinkedQueue<TextureAtlasSprite.Info> concurrentLinkedQueue6;
            list4.add(CompletableFuture.runAsync(() -> {
                sm8 = this.getResourceLocation(resourceLocation);
                try {
                    za7 = zb.getResource(sm8);
                    try {
                        cxs9 = new PngInfo(za7.toString(), za7.getInputStream());
                        ebj10 = za7.<AnimationMetadataSection>getMetadata((MetadataSectionSerializer<AnimationMetadataSection>)AnimationMetadataSection.SERIALIZER);
                        if (ebj10 == null) {
                            ebj10 = AnimationMetadataSection.EMPTY;
                        }
                        pair11 = ebj10.getFrameSize(cxs9.width, cxs9.height);
                        a6 = new TextureAtlasSprite.Info(resourceLocation, (int)pair11.getFirst(), (int)pair11.getSecond(), ebj10);
                    }
                    catch (Throwable t) {
                        throw t;
                    }
                    finally {
                        if (za7 != null) {
                            if (t2 != null) {
                                try {
                                    za7.close();
                                }
                                catch (Throwable exception) {
                                    t2.addSuppressed(exception);
                                }
                            }
                            else {
                                za7.close();
                            }
                        }
                    }
                }
                catch (RuntimeException runtimeException7) {
                    TextureAtlas.LOGGER.error("Unable to parse metadata from {} : {}", sm8, runtimeException7);
                    return;
                }
                catch (IOException iOException7) {
                    TextureAtlas.LOGGER.error("Using missing texture, unable to load {} : {}", sm8, iOException7);
                    return;
                }
                concurrentLinkedQueue6.add(a6);
                return;
            }, Util.backgroundExecutor()));
        }
        CompletableFuture.allOf(list4.<CompletableFuture<?>>toArray(new CompletableFuture[0])).join();
        return concurrentLinkedQueue5;
    }
    
    private List<TextureAtlasSprite> getLoadedSprites(final ResourceManager zb, final Stitcher eag, final int integer) {
        final ConcurrentLinkedQueue<TextureAtlasSprite> concurrentLinkedQueue5 = new ConcurrentLinkedQueue<TextureAtlasSprite>();
        final List<CompletableFuture<?>> list6 = Lists.newArrayList();
        MissingTextureAtlasSprite eac11;
        final ConcurrentLinkedQueue<MissingTextureAtlasSprite> concurrentLinkedQueue6;
        final List<CompletableFuture<Void>> list7;
        TextureAtlasSprite eaj10;
        final ConcurrentLinkedQueue<TextureAtlasSprite> concurrentLinkedQueue7;
        eag.gatherSprites((a, integer6, integer7, integer8, integer9) -> {
            if (a == MissingTextureAtlasSprite.info()) {
                eac11 = MissingTextureAtlasSprite.newInstance(this, integer, integer6, integer7, integer8, integer9);
                concurrentLinkedQueue6.add(eac11);
            }
            else {
                list7.add(CompletableFuture.runAsync(() -> {
                    eaj10 = this.load(zb, a, integer6, integer7, integer, integer8, integer9);
                    if (eaj10 != null) {
                        concurrentLinkedQueue7.add(eaj10);
                    }
                }, Util.backgroundExecutor()));
            }
            return;
        });
        CompletableFuture.allOf(list6.<CompletableFuture<?>>toArray(new CompletableFuture[0])).join();
        return Lists.newArrayList(concurrentLinkedQueue5);
    }
    
    @Nullable
    private TextureAtlasSprite load(final ResourceManager zb, final TextureAtlasSprite.Info a, final int integer3, final int integer4, final int integer5, final int integer6, final int integer7) {
        final ResourceLocation sm9 = this.getResourceLocation(a.name());
        try (final Resource za10 = zb.getResource(sm9)) {
            final NativeImage cxr12 = NativeImage.read(za10.getInputStream());
            return new TextureAtlasSprite(this, a, integer5, integer3, integer4, integer6, integer7, cxr12);
        }
        catch (RuntimeException runtimeException10) {
            TextureAtlas.LOGGER.error("Unable to parse metadata from {}", sm9, runtimeException10);
            return null;
        }
        catch (IOException iOException10) {
            TextureAtlas.LOGGER.error("Using missing texture, unable to load {}", sm9, iOException10);
            return null;
        }
    }
    
    private ResourceLocation getResourceLocation(final ResourceLocation sm) {
        return new ResourceLocation(sm.getNamespace(), String.format("textures/%s%s", sm.getPath(), ".png"));
    }
    
    public void cycleAnimationFrames() {
        this.bind();
        for (final TextureAtlasSprite eaj3 : this.animatedTextures) {
            eaj3.cycleFrames();
        }
    }
    
    @Override
    public void tick() {
        if (!RenderSystem.isOnRenderThread()) {
            RenderSystem.recordRenderCall(this::cycleAnimationFrames);
        }
        else {
            this.cycleAnimationFrames();
        }
    }
    
    public TextureAtlasSprite getSprite(final ResourceLocation sm) {
        final TextureAtlasSprite eaj3 = this.texturesByName.get(sm);
        if (eaj3 == null) {
            return this.texturesByName.get(MissingTextureAtlasSprite.getLocation());
        }
        return eaj3;
    }
    
    public void clearTextureData() {
        for (final TextureAtlasSprite eaj3 : this.texturesByName.values()) {
            eaj3.close();
        }
        this.texturesByName.clear();
        this.animatedTextures.clear();
    }
    
    public ResourceLocation location() {
        return this.location;
    }
    
    public void updateFilter(final Preparations a) {
        this.setFilter(false, a.mipLevel > 0);
    }
    
    static {
        LOGGER = LogManager.getLogger();
        LOCATION_BLOCKS = InventoryMenu.BLOCK_ATLAS;
        LOCATION_PARTICLES = new ResourceLocation("textures/atlas/particles.png");
    }
    
    public static class Preparations {
        final Set<ResourceLocation> sprites;
        final int width;
        final int height;
        final int mipLevel;
        final List<TextureAtlasSprite> regions;
        
        public Preparations(final Set<ResourceLocation> set, final int integer2, final int integer3, final int integer4, final List<TextureAtlasSprite> list) {
            this.sprites = set;
            this.width = integer2;
            this.height = integer3;
            this.mipLevel = integer4;
            this.regions = list;
        }
    }
}
