/*
 * Copyright 2016 FabricMC
 *
 * 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 net.fabricmc.loader.language;

import net.fabricmc.api.EnvType;
import net.fabricmc.loader.FabricLoader;
import net.fabricmc.loader.launch.common.FabricLauncherBase;
import org.objectweb.asm.ClassReader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class JavaLanguageAdapter implements LanguageAdapter {
	private static boolean canApplyInterface(String itfString) throws IOException {
		String className = itfString + ".class";

		// TODO: Be a bit more involved
		switch (itfString) {
			case "net/fabricmc/api/ClientModInitializer":
				if (FabricLoader.INSTANCE.getEnvironmentHandler().getEnvironmentType() == EnvType.SERVER) {
					return false;
				}
				break;
			case "net/fabricmc/api/DedicatedServerModInitializer":
				if (FabricLoader.INSTANCE.getEnvironmentHandler().getEnvironmentType() == EnvType.CLIENT) {
					return false;
				}
		}

		ClassReader reader = new ClassReader(FabricLauncherBase.getLauncher().getResourceAsStream(className));

		for (String s : reader.getInterfaces()) {
			if (!canApplyInterface(s)) {
				return false;
			}
		}

		return true;
	}

	public static Class<?> getClass(String classString, Options options) throws ClassNotFoundException, IOException {
		String className = classString.replace('.', '/') + ".class";
		ClassReader reader = new ClassReader(FabricLauncherBase.getLauncher().getResourceAsStream(className));
		for (String s : reader.getInterfaces()) {
			if (!canApplyInterface(s)) {
				switch (options.getMissingSuperclassBehavior()) {
					case RETURN_NULL:
						return null;
					case CRASH:
					default:
						throw new ClassNotFoundException("Could not find or load class " + s);

				}
			}
		}

		return Class.forName(classString);
	}

	@Override
	public Object createInstance(Class<?> modClass, Options options) throws LanguageAdapterException {
		try {
			Constructor<?> constructor = modClass.getDeclaredConstructor();
			return constructor.newInstance();
		} catch (NoSuchMethodException e) {
			throw new LanguageAdapterException("Could not find constructor for class " + modClass.getName() + "!", e);
		} catch (IllegalAccessException e) {
			throw new LanguageAdapterException("Could not access constructor of class " + modClass.getName() + "!", e);
		} catch (InvocationTargetException | IllegalArgumentException | InstantiationException e) {
			throw new LanguageAdapterException("Could not instantiate class " + modClass.getName() + "!", e);
		}
	}
}

@@ -1561,45 +1561,29 @@
 %0A%0A%09%09
-ClassReader reader = new ClassReader(
+InputStream stream = 
 Fabr
@@ -1641,16 +1641,108 @@
 assName)
+;%0A%09%09if (stream == null) %7B%0A%09%09%09return false;%0A%09%09%7D%0A%09%09ClassReader reader = new ClassReader(stream
 );%0A%0A%09%09fo
@@ -1809,24 +1809,44 @@
 rface(s)) %7B%0A
+%09%09%09%09stream.close();%0A
 %09%09%09%09return f
@@ -1853,32 +1853,50 @@
 alse;%0A%09%09%09%7D%0A%09%09%7D%0A%0A
+%09%09stream.close();%0A
 %09%09return true;%0A%09
@@ -2082,45 +2082,29 @@
 ;%0A%09%09
-ClassReader reader = new ClassReader(
+InputStream stream = 
 Fabr
@@ -2162,16 +2162,173 @@
 assName)
+;%0A%09%09if (stream == null) %7B%0A%09%09%09throw new ClassNotFoundException(%22Could not find or load class %22 + className);%0A%09%09%7D%0A%09%09ClassReader reader = new ClassReader(stream
 );%0A%09%09for
@@ -2475,16 +2475,38 @@
 N_NULL:%0A
+%09%09%09%09%09%09stream.close();%0A
 %09%09%09%09%09%09re
@@ -2547,16 +2547,38 @@
 efault:%0A
+%09%09%09%09%09%09stream.close();%0A
 %09%09%09%09%09%09th
@@ -2663,16 +2663,34 @@
 %09%7D%0A%09%09%7D%0A%0A
+%09%09stream.close();%0A
 %09%09return

fix #52