/*
 * Copyright 2016 Johannes Donath <johannesd@torchmind.com>
 * and other copyright owners as documented in the project's IP log.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.basinmc.sink.plugin.java;

import org.basinmc.faucet.plugin.PluginMetadata;
import org.basinmc.faucet.plugin.error.PluginException;
import org.objectweb.asm.ClassReader;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.annotation.Nonnull;

import jdk.internal.org.objectweb.asm.Type;

/**
 * Provides an archive based Java plugin loader capable of loading JAR or ZIP files which contain
 * a set of bytecode classes.
 *
 * @author <a href="mailto:johannesd@torchmind.com">Johannes Donath</a>
 */
public class ArchiveJavaPluginContext extends AbstractJavaPluginContext {
    private final String mainClass;
    private final Path storageDirectory;
    private final PluginMetadata metadata;
    private final PluginClassLoader classLoader;
    private final Set<String> adapters;

    public ArchiveJavaPluginContext(@Nonnull Path source, @Nonnull Path storageDirectory) throws IOException {
        super(source);

        LocatorClassVisitor locatorClassVisitor = new LocatorClassVisitor();
        String mainClass = null;
        PluginMetadata metadata = null;
        this.adapters = new HashSet<>();

        try (ZipFile file = new ZipFile(source.toFile())) {
            Enumeration<? extends ZipEntry> enumeration = file.entries();

            while (enumeration.hasMoreElements()) {
                ZipEntry entry = enumeration.nextElement();

                if (entry.isDirectory() || !entry.getName().endsWith(".class") || entry.getName().startsWith("META-INF")) {
                    continue;
                }

                try (InputStream inputStream = file.getInputStream(entry)) {
                    ClassReader reader = new ClassReader(inputStream);
                    reader.accept(locatorClassVisitor, ClassReader.EXPAND_FRAMES);

                    if (locatorClassVisitor.getMetadata().isPresent()) {
                        // noinspection OptionalGetWithoutIsPresent
                        mainClass = locatorClassVisitor.getClassName().get();
                        // noinspection OptionalGetWithoutIsPresent
                        metadata = locatorClassVisitor.getMetadata().get();

                        this.adapters.add(Type.getInternalName(VersionRestrictingClassAdapter.class));
                        this.adapters.addAll(locatorClassVisitor.getTransformers());
                    }
                }
            }
        }

        if (mainClass == null || metadata == null) {
            throw new IllegalArgumentException("No main class found in archive");
        }

        this.mainClass = mainClass;
        this.metadata = metadata;

        this.storageDirectory = storageDirectory.resolve(this.metadata.getId());
        this.classLoader = new PluginClassLoader(this, source.toFile().toURI().toURL());
    }

    /**
     * {@inheritDoc}
     */
    @Nonnull
    @Override
    protected Class<?> getMainClass() throws PluginException {
        try {
            return Class.forName(this.mainClass, true, this.classLoader);
        } catch (ClassNotFoundException ex) {
            throw new PluginException("Could not load main class: " + ex.getMessage(), ex);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Nonnull
    @Override
    public ClassLoader getClassLoader() {
        return this.classLoader;
    }

    /**
     * {@inheritDoc}
     */
    @Nonnull
    @Override
    public PluginMetadata getMetadata() {
        return this.metadata;
    }

    /**
     * {@inheritDoc}
     */
    @Nonnull
    @Override
    public Path getStorageDirectory() {
        return this.storageDirectory;
    }

    /**
     * {@inheritDoc}
     */
    @Nonnull
    @Override
    public Set<String> getAdapters() {
        return adapters;
    }
}

@@ -865,16 +865,47 @@
 sReader;
+%0Aimport org.objectweb.asm.Type;
 %0A%0Aimport
@@ -1159,53 +1159,8 @@
 l;%0A%0A
-import jdk.internal.org.objectweb.asm.Type;%0A%0A
 /**%0A

Fix broken jdk.internal import