package net.minecraft.client.renderer.texture;

import org.apache.logging.log4j.LogManager;
import com.mojang.realmsclient.RealmsMainScreen;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.util.profiling.ProfilerFiller;
import com.mojang.blaze3d.platform.TextureUtil;
import java.util.Iterator;
import net.minecraft.client.Minecraft;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.CrashReport;
import java.io.IOException;
import com.mojang.blaze3d.systems.RenderSystem;
import com.google.common.collect.Sets;
import com.google.common.collect.Maps;
import net.minecraft.server.packs.resources.ResourceManager;
import java.util.Set;
import java.util.Map;
import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.Logger;
import net.minecraft.server.packs.resources.PreparableReloadListener;

public class TextureManager implements Tickable, AutoCloseable, PreparableReloadListener {
    private static final Logger LOGGER;
    public static final ResourceLocation INTENTIONAL_MISSING_TEXTURE;
    private final Map<ResourceLocation, AbstractTexture> byPath;
    private final Set<Tickable> tickableTextures;
    private final Map<String, Integer> prefixRegister;
    private final ResourceManager resourceManager;
    
    public TextureManager(final ResourceManager zb) {
        this.byPath = Maps.newHashMap();
        this.tickableTextures = Sets.newHashSet();
        this.prefixRegister = Maps.newHashMap();
        this.resourceManager = zb;
    }
    
    public void bind(final ResourceLocation sm) {
        if (!RenderSystem.isOnRenderThread()) {
            RenderSystem.recordRenderCall(() -> this._bind(sm));
        }
        else {
            this._bind(sm);
        }
    }
    
    private void _bind(final ResourceLocation sm) {
        AbstractTexture dzw3 = this.byPath.get(sm);
        if (dzw3 == null) {
            dzw3 = new SimpleTexture(sm);
            this.register(sm, dzw3);
        }
        dzw3.bind();
    }
    
    public void register(final ResourceLocation sm, AbstractTexture dzw) {
        dzw = this.loadTexture(sm, dzw);
        final AbstractTexture dzw2 = this.byPath.put(sm, dzw);
        if (dzw2 != dzw) {
            if (dzw2 != null && dzw2 != MissingTextureAtlasSprite.getTexture()) {
                dzw2.releaseId();
                this.tickableTextures.remove(dzw2);
            }
            if (dzw instanceof Tickable) {
                this.tickableTextures.add((Tickable)dzw);
            }
        }
    }
    
    private AbstractTexture loadTexture(final ResourceLocation sm, final AbstractTexture dzw) {
        try {
            dzw.load(this.resourceManager);
            return dzw;
        }
        catch (IOException iOException4) {
            if (sm != TextureManager.INTENTIONAL_MISSING_TEXTURE) {
                TextureManager.LOGGER.warn("Failed to load texture: {}", sm, iOException4);
            }
            return MissingTextureAtlasSprite.getTexture();
        }
        catch (Throwable throwable4) {
            final CrashReport h5 = CrashReport.forThrowable(throwable4, "Registering texture");
            final CrashReportCategory i6 = h5.addCategory("Resource location being registered");
            final AbstractTexture dzw2 = dzw;
            i6.setDetail("Resource location", sm);
            i6.setDetail("Texture object class", () -> dzw2.getClass().getName());
            throw new ReportedException(h5);
        }
    }
    
    @Nullable
    public AbstractTexture getTexture(final ResourceLocation sm) {
        return this.byPath.get(sm);
    }
    
    public ResourceLocation register(final String string, final DynamicTexture dzy) {
        Integer integer4 = this.prefixRegister.get(string);
        if (integer4 == null) {
            integer4 = 1;
        }
        else {
            ++integer4;
        }
        this.prefixRegister.put(string, integer4);
        final ResourceLocation sm5 = new ResourceLocation(String.format("dynamic/%s_%d", string, integer4));
        this.register(sm5, dzy);
        return sm5;
    }
    
    public CompletableFuture<Void> preload(final ResourceLocation sm, final Executor executor) {
        if (!this.byPath.containsKey(sm)) {
            final PreloadedTexture eae4 = new PreloadedTexture(this.resourceManager, sm, executor);
            this.byPath.put(sm, eae4);
            return eae4.getFuture().thenRunAsync(() -> this.register(sm, eae4), TextureManager::execute);
        }
        return CompletableFuture.<Void>completedFuture((Void)null);
    }
    
    private static void execute(final Runnable runnable) {
        Minecraft.getInstance().execute(() -> RenderSystem.recordRenderCall(runnable::run));
    }
    
    @Override
    public void tick() {
        for (final Tickable eal3 : this.tickableTextures) {
            eal3.tick();
        }
    }
    
    public void release(final ResourceLocation sm) {
        final AbstractTexture dzw3 = this.getTexture(sm);
        if (dzw3 != null) {
            TextureUtil.releaseTextureId(dzw3.getId());
        }
    }
    
    @Override
    public void close() {
        this.byPath.values().forEach(AbstractTexture::releaseId);
        this.byPath.clear();
        this.tickableTextures.clear();
        this.prefixRegister.clear();
    }
    
    @Override
    public CompletableFuture<Void> reload(final PreparationBarrier a, final ResourceManager zb, final ProfilerFiller aio3, final ProfilerFiller aio4, final Executor executor5, final Executor executor6) {
        final Iterator<Map.Entry<ResourceLocation, AbstractTexture>> iterator5;
        Map.Entry<ResourceLocation, AbstractTexture> entry6;
        ResourceLocation sm7;
        AbstractTexture dzw8;
        return CompletableFuture.allOf(TitleScreen.preloadResources(this, executor5), this.preload(AbstractWidget.WIDGETS_LOCATION, executor5)).thenCompose(a::wait).thenAcceptAsync(void3 -> {
            MissingTextureAtlasSprite.getTexture();
            RealmsMainScreen.updateTeaserImages(this.resourceManager);
            iterator5 = this.byPath.entrySet().iterator();
            while (iterator5.hasNext()) {
                entry6 = iterator5.next();
                sm7 = entry6.getKey();
                dzw8 = entry6.getValue();
                if (dzw8 == MissingTextureAtlasSprite.getTexture() && !sm7.equals(MissingTextureAtlasSprite.getLocation())) {
                    iterator5.remove();
                }
                else {
                    dzw8.reset(this, zb, sm7, executor6);
                }
            }
        }, runnable -> RenderSystem.recordRenderCall(runnable::run));
    }
    
    static {
        LOGGER = LogManager.getLogger();
        INTENTIONAL_MISSING_TEXTURE = new ResourceLocation("");
    }
}
