package net.minecraft.server.packs.resources;

import java.io.OutputStream;
import java.io.PrintStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import org.apache.logging.log4j.LogManager;
import java.util.stream.Stream;
import java.util.Collections;
import java.util.Objects;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.Iterator;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileNotFoundException;
import net.minecraft.resources.ResourceLocation;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import com.google.common.collect.Lists;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.PackResources;
import java.util.List;
import org.apache.logging.log4j.Logger;

public class FallbackResourceManager implements ResourceManager {
    private static final Logger LOGGER;
    protected final List<PackResources> fallbacks;
    private final PackType type;
    private final String namespace;
    
    public FallbackResourceManager(final PackType aaf, final String string) {
        this.fallbacks = Lists.newArrayList();
        this.type = aaf;
        this.namespace = string;
    }
    
    public void add(final PackResources aae) {
        this.fallbacks.add(aae);
    }
    
    @Override
    public Set<String> getNamespaces() {
        return ImmutableSet.<String>of(this.namespace);
    }
    
    @Override
    public Resource getResource(final ResourceLocation uh) throws IOException {
        this.validateLocation(uh);
        PackResources aae3 = null;
        final ResourceLocation uh2 = getMetadataLocation(uh);
        for (int integer5 = this.fallbacks.size() - 1; integer5 >= 0; --integer5) {
            final PackResources aae4 = this.fallbacks.get(integer5);
            if (aae3 == null && aae4.hasResource(this.type, uh2)) {
                aae3 = aae4;
            }
            if (aae4.hasResource(this.type, uh)) {
                InputStream inputStream7 = null;
                if (aae3 != null) {
                    inputStream7 = this.getWrappedResource(uh2, aae3);
                }
                return new SimpleResource(aae4.getName(), uh, this.getWrappedResource(uh, aae4), inputStream7);
            }
        }
        throw new FileNotFoundException(uh.toString());
    }
    
    @Override
    public boolean hasResource(final ResourceLocation uh) {
        if (!this.isValidLocation(uh)) {
            return false;
        }
        for (int integer3 = this.fallbacks.size() - 1; integer3 >= 0; --integer3) {
            final PackResources aae4 = this.fallbacks.get(integer3);
            if (aae4.hasResource(this.type, uh)) {
                return true;
            }
        }
        return false;
    }
    
    protected InputStream getWrappedResource(final ResourceLocation uh, final PackResources aae) throws IOException {
        final InputStream inputStream4 = aae.getResource(this.type, uh);
        return FallbackResourceManager.LOGGER.isDebugEnabled() ? new LeakedResourceWarningInputStream(inputStream4, uh, aae.getName()) : inputStream4;
    }
    
    private void validateLocation(final ResourceLocation uh) throws IOException {
        if (!this.isValidLocation(uh)) {
            throw new IOException("Invalid relative path to resource: " + uh);
        }
    }
    
    private boolean isValidLocation(final ResourceLocation uh) {
        return !uh.getPath().contains("..");
    }
    
    @Override
    public List<Resource> getResources(final ResourceLocation uh) throws IOException {
        this.validateLocation(uh);
        final List<Resource> list3 = Lists.newArrayList();
        final ResourceLocation uh2 = getMetadataLocation(uh);
        for (final PackResources aae6 : this.fallbacks) {
            if (aae6.hasResource(this.type, uh)) {
                final InputStream inputStream7 = aae6.hasResource(this.type, uh2) ? this.getWrappedResource(uh2, aae6) : null;
                list3.add(new SimpleResource(aae6.getName(), uh, this.getWrappedResource(uh, aae6), inputStream7));
            }
        }
        if (list3.isEmpty()) {
            throw new FileNotFoundException(uh.toString());
        }
        return list3;
    }
    
    @Override
    public Collection<ResourceLocation> listResources(final ResourceLocation uh, final Predicate<String> predicate) {
        if (Objects.equals(uh.getNamespace(), this.namespace)) {
            return this.listResources(uh.getPath(), predicate);
        }
        return ImmutableSet.of();
    }
    
    @Override
    public Collection<ResourceLocation> listResources(final String string, final Predicate<String> predicate) {
        final List<ResourceLocation> list4 = Lists.newArrayList();
        for (final PackResources aae6 : this.fallbacks) {
            list4.addAll(aae6.getResources(this.type, this.namespace, string, Integer.MAX_VALUE, predicate));
        }
        Collections.<ResourceLocation>sort(list4);
        return list4;
    }
    
    @Override
    public Stream<PackResources> listPacks() {
        return this.fallbacks.stream();
    }
    
    static ResourceLocation getMetadataLocation(final ResourceLocation uh) {
        return new ResourceLocation(uh.getNamespace(), uh.getPath() + ".mcmeta");
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
    
    static class LeakedResourceWarningInputStream extends FilterInputStream {
        private final String message;
        private boolean closed;
        
        public LeakedResourceWarningInputStream(final InputStream inputStream, final ResourceLocation uh, final String string) {
            super(inputStream);
            final ByteArrayOutputStream byteArrayOutputStream5 = new ByteArrayOutputStream();
            new Exception().printStackTrace(new PrintStream(byteArrayOutputStream5));
            this.message = "Leaked resource: '" + uh + "' loaded from pack: '" + string + "'\n" + byteArrayOutputStream5;
        }
        
        @Override
        public void close() throws IOException {
            super.close();
            this.closed = true;
        }
        
        @Override
        protected void finalize() throws Throwable {
            if (!this.closed) {
                FallbackResourceManager.LOGGER.warn(this.message);
            }
            super.finalize();
        }
    }
}
