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.Collections;
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.Pack;
import java.util.List;
import org.apache.logging.log4j.Logger;

public class FallbackResourceManager implements ResourceManager {
    private static final Logger LOGGER;
    protected final List<Pack> fallbacks;
    private final PackType type;
    private final String namespace;
    
    public FallbackResourceManager(final PackType yf, final String string) {
        this.fallbacks = Lists.newArrayList();
        this.type = yf;
        this.namespace = string;
    }
    
    public void add(final Pack ye) {
        this.fallbacks.add(ye);
    }
    
    @Override
    public Set<String> getNamespaces() {
        return ImmutableSet.<String>of(this.namespace);
    }
    
    @Override
    public Resource getResource(final ResourceLocation sm) throws IOException {
        this.validateLocation(sm);
        Pack ye3 = null;
        final ResourceLocation sm2 = getMetadataLocation(sm);
        for (int integer5 = this.fallbacks.size() - 1; integer5 >= 0; --integer5) {
            final Pack ye4 = this.fallbacks.get(integer5);
            if (ye3 == null && ye4.hasResource(this.type, sm2)) {
                ye3 = ye4;
            }
            if (ye4.hasResource(this.type, sm)) {
                InputStream inputStream7 = null;
                if (ye3 != null) {
                    inputStream7 = this.getWrappedResource(sm2, ye3);
                }
                return new SimpleResource(ye4.getName(), sm, this.getWrappedResource(sm, ye4), inputStream7);
            }
        }
        throw new FileNotFoundException(sm.toString());
    }
    
    @Override
    public boolean hasResource(final ResourceLocation sm) {
        if (!this.isValidLocation(sm)) {
            return false;
        }
        for (int integer3 = this.fallbacks.size() - 1; integer3 >= 0; --integer3) {
            final Pack ye4 = this.fallbacks.get(integer3);
            if (ye4.hasResource(this.type, sm)) {
                return true;
            }
        }
        return false;
    }
    
    protected InputStream getWrappedResource(final ResourceLocation sm, final Pack ye) throws IOException {
        final InputStream inputStream4 = ye.getResource(this.type, sm);
        return FallbackResourceManager.LOGGER.isDebugEnabled() ? new LeakedResourceWarningInputStream(inputStream4, sm, ye.getName()) : inputStream4;
    }
    
    private void validateLocation(final ResourceLocation sm) throws IOException {
        if (!this.isValidLocation(sm)) {
            throw new IOException("Invalid relative path to resource: " + sm);
        }
    }
    
    private boolean isValidLocation(final ResourceLocation sm) {
        return !sm.getPath().contains("..");
    }
    
    @Override
    public List<Resource> getResources(final ResourceLocation sm) throws IOException {
        this.validateLocation(sm);
        final List<Resource> list3 = Lists.newArrayList();
        final ResourceLocation sm2 = getMetadataLocation(sm);
        for (final Pack ye6 : this.fallbacks) {
            if (ye6.hasResource(this.type, sm)) {
                final InputStream inputStream7 = ye6.hasResource(this.type, sm2) ? this.getWrappedResource(sm2, ye6) : null;
                list3.add(new SimpleResource(ye6.getName(), sm, this.getWrappedResource(sm, ye6), inputStream7));
            }
        }
        if (list3.isEmpty()) {
            throw new FileNotFoundException(sm.toString());
        }
        return list3;
    }
    
    @Override
    public Collection<ResourceLocation> listResources(final String string, final Predicate<String> predicate) {
        final List<ResourceLocation> list4 = Lists.newArrayList();
        for (final Pack ye6 : this.fallbacks) {
            list4.addAll(ye6.getResources(this.type, this.namespace, string, Integer.MAX_VALUE, predicate));
        }
        Collections.<ResourceLocation>sort(list4);
        return list4;
    }
    
    static ResourceLocation getMetadataLocation(final ResourceLocation sm) {
        return new ResourceLocation(sm.getNamespace(), sm.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 sm, final String string) {
            super(inputStream);
            final ByteArrayOutputStream byteArrayOutputStream5 = new ByteArrayOutputStream();
            new Exception().printStackTrace(new PrintStream(byteArrayOutputStream5));
            this.message = "Leaked resource: '" + sm + "' 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();
        }
    }
}
