// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.plugin.early;

import java.util.Iterator;
import java.security.Principal;
import java.security.PermissionCollection;
import java.security.ProtectionDomain;
import java.security.CodeSource;
import java.security.cert.Certificate;
import java.io.InputStream;
import java.io.IOException;
import javax.annotation.Nonnull;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.net.URLClassLoader;

public final class TransformingClassLoader extends URLClassLoader
{
    private static final Set<String> SECURE_PACKAGE_PREFIXES;
    private final List<ClassTransformer> transformers;
    private final ClassLoader appClassLoader;
    
    public TransformingClassLoader(@Nonnull final URL[] urls, @Nonnull final List<ClassTransformer> transformers, final ClassLoader parent, final ClassLoader appClassLoader) {
        super(urls, parent);
        this.transformers = transformers;
        this.appClassLoader = appClassLoader;
    }
    
    @Override
    protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
        synchronized (this.getClassLoadingLock(name)) {
            final Class<?> loaded = this.findLoadedClass(name);
            if (loaded != null) {
                if (resolve) {
                    this.resolveClass(loaded);
                }
                return loaded;
            }
            if (isPreloadedClass(name)) {
                final Class<?> clazz = this.appClassLoader.loadClass(name);
                if (resolve) {
                    this.resolveClass(clazz);
                }
                return clazz;
            }
            final String internalName = name.replace('.', '/');
            final URL resource = this.findResource(internalName + ".class");
            if (resource != null) {
                try (final InputStream is = resource.openStream()) {
                    final Class<?> clazz2 = this.transformAndDefine(name, internalName, is.readAllBytes(), resource);
                    if (resolve) {
                        this.resolveClass(clazz2);
                    }
                    return clazz2;
                }
                catch (final IOException ex) {}
            }
            return super.loadClass(name, resolve);
        }
    }
    
    private Class<?> transformAndDefine(final String name, final String internalName, byte[] classBytes, final URL resource) {
        if (!isSecureClass(name)) {
            for (ClassTransformer transformer : this.transformers) {
                try {
                    final byte[] transformed = transformer.transform(name, internalName, classBytes);
                    if (transformed == null) {
                        continue;
                    }
                    classBytes = transformed;
                }
                catch (final Exception e) {
                    System.err.println("[EarlyPlugin] Transformer " + transformer.getClass().getName() + " failed on " + name + ": " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
        final URL codeSourceUrl = getCodeSourceUrl(resource, internalName);
        final CodeSource codeSource = new CodeSource(codeSourceUrl, (Certificate[])null);
        final ProtectionDomain protectionDomain = new ProtectionDomain(codeSource, null, this, null);
        return this.defineClass(name, classBytes, 0, classBytes.length, protectionDomain);
    }
    
    private static URL getCodeSourceUrl(final URL resource, final String internalName) {
        final String urlStr = resource.toString();
        final String classPath = internalName + ".class";
        if (urlStr.startsWith("jar:")) {
            final int bangIndex = urlStr.indexOf("!/");
            if (bangIndex > 0) {
                try {
                    return new URL(urlStr.substring(4, bangIndex));
                }
                catch (final Exception e) {
                    return resource;
                }
            }
        }
        else if (urlStr.endsWith(classPath)) {
            try {
                return new URL(urlStr.substring(0, urlStr.length() - classPath.length()));
            }
            catch (final Exception e2) {
                return resource;
            }
        }
        return resource;
    }
    
    private static boolean isPreloadedClass(@Nonnull final String name) {
        return name.equals("com.hypixel.hytale.Main") || name.startsWith("com.hypixel.hytale.plugin.early.");
    }
    
    private static boolean isSecureClass(@Nonnull final String name) {
        for (final String prefix : TransformingClassLoader.SECURE_PACKAGE_PREFIXES) {
            if (name.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
    
    static {
        SECURE_PACKAGE_PREFIXES = Set.of(new String[] { "java.", "javax.", "jdk.", "sun.", "com.sun.", "org.bouncycastle.", "server.io.netty.", "org.objectweb.asm.", "com.google.gson.", "org.slf4j.", "org.apache.logging.", "ch.qos.logback.", "com.google.flogger.", "server.io.sentry.", "com.hypixel.protoplus.", "com.hypixel.fastutil.", "com.hypixel.hytale.plugin.early." });
    }
}
